Files
rea/src/domain/javascript/electronNativeLoaderPaths.test.ts
T
BeqaKs 06739aeaf2 fix(javascript): ignore erased TypeScript names in native-addon requests (#2766)
* fix(javascript): ignore erased TypeScript names in native-addon requests

Skip import type / export type declarations and per-binding type
specifiers when collecting .node addon members so only runtime value
imports request native exports (fixes #2667).

Co-authored-by: Cursor <cursoragent@cursor.com>

* test(javascript): cover erased TypeScript re-export forms

* test(javascript): allow Inspector observation events to arrive

* test(inspector): emit events before enable responses

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-14 05:39:09 +08:00

113 lines
3.6 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { analyzeJavaScriptStaticSource } from "./javascriptStaticAnalysis.js";
describe("native addon type-only TypeScript imports", () => {
it("ignores declaration-wide import type and export type from .node modules", () => {
const analysis = analyzeJavaScriptStaticSource(`
import type { NativeThing } from "./addon.node";
export type { Extra } from "./addon.node";
`);
expect(analysis.parse_status).toBe("complete");
expect(analysis.electron.native_addon_bindings).toEqual([]);
});
it("keeps only value members from mixed type/value native imports", () => {
const analysis = analyzeJavaScriptStaticSource(`
import { type OtherThing, load } from "./addon.node";
load();
`);
expect(analysis.electron.native_addon_bindings).toEqual([
expect.objectContaining({
specifier: "./addon.node",
binding_kind: "import",
members: ["load"],
}),
]);
});
it("retains ordinary value imports of native addons", () => {
const analysis = analyzeJavaScriptStaticSource(`
import { load } from "./addon.node";
load();
`);
expect(analysis.electron.native_addon_bindings).toEqual([
expect.objectContaining({
specifier: "./addon.node",
binding_kind: "import",
members: ["load"],
}),
]);
});
});
describe("native addon type-only re-exports", () => {
it.each([
['export { type Extra, load } from "./addon.node";', ["load"]],
['export { type Extra } from "./addon.node";', []],
['export type * from "./addon.node";', []],
] as const)("retains only runtime members from %s", (source, members) => {
const analysis = analyzeJavaScriptStaticSource(source);
expect(analysis.parse_status).toBe("complete");
expect(analysis.electron.native_addon_bindings).toEqual(
members.length === 0
? []
: [
expect.objectContaining({
specifier: "./addon.node",
binding_kind: "re-export",
members: [...members],
}),
],
);
});
});
describe("native addon loader provenance", () => {
it("distinguishes CommonJS and ESM re-exports", () => {
const analysis = analyzeJavaScriptStaticSource(`
const required = require("./addon.node#literal.node");
module.exports = require("./addon.node#literal.node");
import imported from "./addon.node#literal.node";
export { default } from "./addon.node#literal.node";
`);
expect(analysis.electron.native_addon_bindings).toHaveLength(4);
expect(analysis.electron.native_addon_bindings).toEqual(
expect.arrayContaining([
expect.objectContaining({
binding_kind: "require",
module_kind: "require",
}),
expect.objectContaining({
binding_kind: "import",
module_kind: "import",
}),
expect.objectContaining({
binding_kind: "re-export",
module_kind: "require",
}),
expect.objectContaining({
binding_kind: "re-export",
module_kind: "import",
}),
]),
);
});
it.each(["#", "?"])(
"classifies require filenames literally for %s",
(punctuation) => {
const analysis = analyzeJavaScriptStaticSource(`
const script = require("./addon.node${punctuation}helper.js");
import native from "./addon.node${punctuation}helper.js";
`);
expect(analysis.electron.native_addon_bindings).toEqual([
expect.objectContaining({
binding_kind: "import",
module_kind: "import",
}),
]);
},
);
});