mirror of
https://github.com/morluto/rea.git
synced 2026-10-26 17:35:11 +00:00
fix(reference): report malformed UTF-8 before import parsing
Signed-off-by: Rudy Celekli <47457359+rudycelekli@users.noreply.github.com>
This commit is contained in:
1 parent
9055bd695b
commit
f166a2ae15
3 files changed
+100
-1
No files matched your search
@@ -147,6 +147,10 @@ from observations of the current app. File names do not automatically exclude
|
||||
files. Set `REA_REFERENCE_SECRET_PATTERNS_JSON` to a JSON array of ignore
|
||||
patterns when you want to exclude selected paths.
|
||||
|
||||
JavaScript and TypeScript import parsing requires valid UTF-8. Malformed source
|
||||
bytes retain their original hashes and sizes with a decoding diagnostic; REA
|
||||
does not infer module targets from replacement characters.
|
||||
|
||||
Historical-source import requires safe no-follow file opens on Linux or
|
||||
macOS. Native Windows returns `unsupported_host`; use Linux REA inside WSL
|
||||
or another supported host. See
|
||||
|
||||
@@ -331,7 +331,25 @@ export const parseReferenceSourceImports = (
|
||||
if (language !== null && !codeSourceLanguages.has(language))
|
||||
return { relationships: [], parse_failures: [] };
|
||||
|
||||
const source = new TextDecoder("utf-8", { fatal: false }).decode(bytes);
|
||||
let source: string;
|
||||
try {
|
||||
source = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
||||
} catch (cause: unknown) {
|
||||
// The import still records the original bytes; lossy text cannot supply
|
||||
// observed module names or relationships.
|
||||
void cause;
|
||||
return {
|
||||
relationships: [],
|
||||
parse_failures: [
|
||||
{
|
||||
path,
|
||||
parser: "utf-8",
|
||||
reason:
|
||||
"Source bytes are not valid UTF-8; import targets were not parsed.",
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
const { ast, reasons } = parseWithBabel(path, source, language);
|
||||
const parseFailures = reasons.map((reason) => ({
|
||||
path,
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
import { createHash } from "node:crypto";
|
||||
import { writeFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
|
||||
import { expect } from "vitest";
|
||||
|
||||
import { historicalSourceGraphSchema } from "../../../src/domain/referenceSourceGraph.js";
|
||||
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
||||
import { cliTest } from "../../support/cli/cliFixture.js";
|
||||
|
||||
cliTest(
|
||||
"retains malformed source bytes without inventing decoded import targets",
|
||||
async ({ cli }) => {
|
||||
const root = await createTestTempDirectory("rea-reference-utf8-");
|
||||
const sources = new Map([
|
||||
[
|
||||
"invalid.mjs",
|
||||
Buffer.concat([
|
||||
Buffer.from('import "./caf'),
|
||||
Buffer.from([0xff]),
|
||||
Buffer.from('.js";\n'),
|
||||
]),
|
||||
],
|
||||
[
|
||||
"comment.ts",
|
||||
Buffer.concat([
|
||||
Buffer.from('import "./dep.js"; // '),
|
||||
Buffer.from([0xc0, 0xaf]),
|
||||
]),
|
||||
],
|
||||
["valid.mjs", Buffer.from('import "./caf�.js";\n')],
|
||||
["caf�.js", Buffer.from("export const value = 1;\n")],
|
||||
["dep.js", Buffer.from("export const value = 2;\n")],
|
||||
]);
|
||||
await Promise.all(
|
||||
[...sources].map(([path, bytes]) => writeFile(join(root, path), bytes)),
|
||||
);
|
||||
const result = await cli.run({
|
||||
arguments: ["import-reference-source", root, "--json"],
|
||||
environment: { HOME: root, XDG_CONFIG_HOME: root, XDG_CACHE_HOME: root },
|
||||
});
|
||||
expect(result.exitCode).toBe(0);
|
||||
const graph = historicalSourceGraphSchema.parse(result.json);
|
||||
expect(graph.parse_failures).toEqual([
|
||||
{
|
||||
path: "comment.ts",
|
||||
parser: "utf-8",
|
||||
reason:
|
||||
"Source bytes are not valid UTF-8; import targets were not parsed.",
|
||||
},
|
||||
{
|
||||
path: "invalid.mjs",
|
||||
parser: "utf-8",
|
||||
reason:
|
||||
"Source bytes are not valid UTF-8; import targets were not parsed.",
|
||||
},
|
||||
]);
|
||||
expect(graph.relationships).toEqual([
|
||||
{
|
||||
from_path: "valid.mjs",
|
||||
to: "caf�.js",
|
||||
kind: "imports",
|
||||
resolution: "internal",
|
||||
parse_state: "parsed",
|
||||
},
|
||||
]);
|
||||
for (const [path, bytes] of sources)
|
||||
expect(graph.entries).toContainEqual(
|
||||
expect.objectContaining({
|
||||
path,
|
||||
sha256: createHash("sha256").update(bytes).digest("hex"),
|
||||
size: bytes.length,
|
||||
content_state: "hashed",
|
||||
}),
|
||||
);
|
||||
},
|
||||
);
|
||||
Reference in new issue
Block a user