Kas.st
Kas.stā€¢5d ago

Deno.readFile broken?

For some reason I can't read a file with Deno.readFile. I've tried relative and absolute paths. I can cat the file without issues. Here's the relevant snippet:
...
async function fixImport(
file: string,
line: number,
char: number,
broken: string
) {
const fixed = broken + ".ts";

// open the file and replace 'broken' with 'fixed'
const relative = file.replace(Deno.cwd(), ".");

const decoder = new TextDecoder();
const bytes = await Deno.readFile(relative);
const content = decoder.decode(bytes);

...
}
...
...
async function fixImport(
file: string,
line: number,
char: number,
broken: string
) {
const fixed = broken + ".ts";

// open the file and replace 'broken' with 'fixed'
const relative = file.replace(Deno.cwd(), ".");

const decoder = new TextDecoder();
const bytes = await Deno.readFile(relative);
const content = decoder.decode(bytes);

...
}
...
and the output including cat:
root@2da2d8303d16:/secretproject/frontend/datalayer# deno run fix:imports
Task fix:imports deno run --allow-read --allow-write --allow-env --allow-net --allow-run fix.imports.ts
šŸ” found broken import "/secretproject/frontend/datalayer/datalayer/core/rest/schemas.gen" in "/secretproject/frontend/datalayer/datalayer/core/rest/index.ts" (2:15)
error: Uncaught (in promise) NotFound: No such file or directory (os error 2): readfile './datalayer/core/rest/index.ts'
const bytes = await Deno.readFile(relative);
^
at Object.readFile (ext:deno_fs/30_fs.js:747:24)
at fixImport (file:///secretproject/frontend/datalayer/fix.imports.ts:13:28)
at file:///secretproject/frontend/datalayer/fix.imports.ts:55:13
at eventLoopTick (ext:core/01_core.js:175:7)
root@2da2d8303d16:/secretproject/frontend/datalayer# cat ./datalayer/core/rest/index.ts
// This file is auto-generated by @hey-api/openapi-ts
export * from "./schemas.gen";
export * from "./services.gen";
export * from "./types.gen";
root@2da2d8303d16:/secretproject/frontend/datalayer# deno run fix:imports
Task fix:imports deno run --allow-read --allow-write --allow-env --allow-net --allow-run fix.imports.ts
šŸ” found broken import "/secretproject/frontend/datalayer/datalayer/core/rest/schemas.gen" in "/secretproject/frontend/datalayer/datalayer/core/rest/index.ts" (2:15)
error: Uncaught (in promise) NotFound: No such file or directory (os error 2): readfile './datalayer/core/rest/index.ts'
const bytes = await Deno.readFile(relative);
^
at Object.readFile (ext:deno_fs/30_fs.js:747:24)
at fixImport (file:///secretproject/frontend/datalayer/fix.imports.ts:13:28)
at file:///secretproject/frontend/datalayer/fix.imports.ts:55:13
at eventLoopTick (ext:core/01_core.js:175:7)
root@2da2d8303d16:/secretproject/frontend/datalayer# cat ./datalayer/core/rest/index.ts
// This file is auto-generated by @hey-api/openapi-ts
export * from "./schemas.gen";
export * from "./services.gen";
export * from "./types.gen";
6 Replies
marvinh.
marvinh.ā€¢5d ago
Looks like the filepth is wrong. It's looking for a file at:
/secretproject/frontend/datalayer/datalayer/core/rest/index.ts
/secretproject/frontend/datalayer/datalayer/core/rest/index.ts
Is this the correct path? I find that for finding path issues it often helps to log out Deno.cwd() to see if my assumptions with relative paths is correct. You can also use import.meta.dirname to construct absolute paths from the current file
Kas.st
Kas.stā€¢5d ago
If I read the absolute path it fails aswell, I can cat it without issues
marvinh.
marvinh.ā€¢5d ago
Can't reproduce that on my end, regardless of passing relative or absolute paths. What I tried: 1. Create a new project with deno init 2. Replace contents of main.ts with
const content = await Deno.readFile("./deno.json");
console.log(content);
const content = await Deno.readFile("./deno.json");
console.log(content);
3. Run deno run -A main.ts
Kas.st
Kas.stā€¢5d ago
hmm I've checked that Deno.cwd === import.meta.dirname and it's the same
async function fixImport(
file: string,
line: number,
char: number,
broken: string
) {
const fixed = broken + ".ts";

// open the file and replace 'broken' with 'fixed'
console.log("cwd", Deno.cwd());
if (Deno.cwd() === import.meta.dirname) {
console.log("cwd is the same as the script directory");
}
const relative = file.replace(Deno.cwd(), ".");

const decoder = new TextDecoder();
const bytes = await Deno.readFile(file);
const content = decoder.decode(bytes);

const lines = content.split("\n");

// replace the content at the line and char position with the fixed import
const targetLine = lines[line - 1];
const fixedLine =
targetLine.slice(0, char) + fixed + targetLine.slice(char + broken.length);

lines[line - 1] = fixedLine;

const updated = lines.join("\n");
Deno.writeTextFileSync(file, updated);

console.log(`šŸ”§ fixed import for ${broken}`);
}

if (import.meta.main) {
// run deno check on and capture the output
const cmd = new Deno.Command(Deno.execPath(), {
args: ["check", "./datalayer/mod.ts"],
});

const decoder = new TextDecoder();
const fixes: string[] = [];

while (true) {
const { stderr } = await cmd.output();

const error = decoder.decode(stderr).replaceAll("\n", "");

const regex =
/(?:.*)Module not found "file:\/\/(?<broken>[^"]+)". Maybe add a '\.ts'.*file:\/\/(?<file>[^:]+):(?<line>[^:]+):(?<char>[^:]+)/;

const match = error.match(regex);

if (match && match.groups) {
const { file, broken, line, char } = match.groups;
console.log(
`šŸ” found broken import "${broken}" in "${file}" (${line}:${char})`
);
await fixImport(file, Number(line), Number(char), broken);
fixes.push(broken);
} else {
break;
}
}

console.log(`šŸš€ fixed ${fixes.length} imports`);
}
async function fixImport(
file: string,
line: number,
char: number,
broken: string
) {
const fixed = broken + ".ts";

// open the file and replace 'broken' with 'fixed'
console.log("cwd", Deno.cwd());
if (Deno.cwd() === import.meta.dirname) {
console.log("cwd is the same as the script directory");
}
const relative = file.replace(Deno.cwd(), ".");

const decoder = new TextDecoder();
const bytes = await Deno.readFile(file);
const content = decoder.decode(bytes);

const lines = content.split("\n");

// replace the content at the line and char position with the fixed import
const targetLine = lines[line - 1];
const fixedLine =
targetLine.slice(0, char) + fixed + targetLine.slice(char + broken.length);

lines[line - 1] = fixedLine;

const updated = lines.join("\n");
Deno.writeTextFileSync(file, updated);

console.log(`šŸ”§ fixed import for ${broken}`);
}

if (import.meta.main) {
// run deno check on and capture the output
const cmd = new Deno.Command(Deno.execPath(), {
args: ["check", "./datalayer/mod.ts"],
});

const decoder = new TextDecoder();
const fixes: string[] = [];

while (true) {
const { stderr } = await cmd.output();

const error = decoder.decode(stderr).replaceAll("\n", "");

const regex =
/(?:.*)Module not found "file:\/\/(?<broken>[^"]+)". Maybe add a '\.ts'.*file:\/\/(?<file>[^:]+):(?<line>[^:]+):(?<char>[^:]+)/;

const match = error.match(regex);

if (match && match.groups) {
const { file, broken, line, char } = match.groups;
console.log(
`šŸ” found broken import "${broken}" in "${file}" (${line}:${char})`
);
await fixImport(file, Number(line), Number(char), broken);
fixes.push(broken);
} else {
break;
}
}

console.log(`šŸš€ fixed ${fixes.length} imports`);
}
here's the full script
marvinh.
marvinh.ā€¢5d ago
Thanks for sharing. I assume the file you want to read is relative to that script at ./datalayer/mod.ts? Ah I see the issue. Your regex doesn't account for ansi escape codes. This is the file path I'm getting from the regex:
"/Users/marvinh/dev/test/deno-readfile/datalayer/mod.ts\x1b[0m"
"/Users/marvinh/dev/test/deno-readfile/datalayer/mod.ts\x1b[0m"
Unrelated: It looks like the goal of the script is to find and fix missing extensions in import paths. We have an unstable lint rule for that which might interest you:
deno lint --unstable-sloppy-imports path/to/files/or/folders
deno lint --unstable-sloppy-imports path/to/files/or/folders
It can also add the missing extensions by running:
deno lint --unstable-sloppy-imports path/to/files/or/folders --fix
deno lint --unstable-sloppy-imports path/to/files/or/folders --fix
Kas.st
Kas.stā€¢5d ago
oh, I saw the unstable flag but didn't know it's autofixable šŸ‘ thanks for helping!