Skip to content

Commit

Permalink
fix: implement isESMExport probe to consider export stmts as dependen…
Browse files Browse the repository at this point in the history
…cies
  • Loading branch information
fraxken committed Jan 4, 2025
1 parent d3052b4 commit 110101f
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/ProbeRunner.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import isImportDeclaration from "./probes/isImportDeclaration.js";
import isWeakCrypto from "./probes/isWeakCrypto.js";
import isBinaryExpression from "./probes/isBinaryExpression.js";
import isArrayExpression from "./probes/isArrayExpression.js";
import isESMExport from "./probes/isESMExport.js";

// Import Internal Dependencies
import { SourceFile } from "./SourceFile.js";
Expand Down Expand Up @@ -39,6 +40,7 @@ export class ProbeRunner {
*/
static Defaults = [
isRequire,
isESMExport,
isUnsafeCallee,
isLiteral,
isLiteralRegex,
Expand Down
31 changes: 31 additions & 0 deletions src/probes/isESMExport.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* @description Search for ESM Export
*
* @example
* export { bar } from "./foo.js";
* export * from "./bar.js";
*/
function validateNode(node) {
return [
/**
* We must be sure that the source property is a Literal to not fall in a trap
* export const foo = "bar";
*/
(node.type === "ExportNamedDeclaration" && node.source?.type === "Literal") ||
node.type === "ExportAllDeclaration"
];
}

function main(node, { sourceFile }) {
sourceFile.addDependency(
node.source.value,
node.loc
);
}

export default {
name: "isESMExport",
validateNode,
main,
breakOnMatch: true
};
24 changes: 24 additions & 0 deletions test/EntryFilesAnalyser.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,30 @@ describe("EntryFilesAnalyser", () => {
assert.strictEqual(calls.length, 6);
});

it("should analyze ESM export statements recursively", async(t) => {
const entryFilesAnalyser = new EntryFilesAnalyser();
const entryUrl = new URL("export.js", FIXTURE_URL);

t.mock.method(AstAnalyser.prototype, "analyseFile");

const generator = entryFilesAnalyser.analyse([
entryUrl
]);
const reports = await fromAsync(generator);

assert.deepEqual(
reports.map((report) => report.file),
[
entryUrl,
new URL("shared.js", FIXTURE_URL)
].map((url) => fileURLToPath(url))
);

// Check that shared dependencies are not analyzed several times
const calls = AstAnalyser.prototype.analyseFile.mock.calls;
assert.strictEqual(calls.length, 2);
});

it("should detect internal deps that failed to be analyzed", async() => {
const entryFilesAnalyser = new EntryFilesAnalyser();
const entryUrl = new URL("entryWithInvalidDep.js", FIXTURE_URL);
Expand Down
1 change: 1 addition & 0 deletions test/fixtures/entryFiles/export.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./shared.js";
34 changes: 34 additions & 0 deletions test/probes/isESMExport.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Import Node.js Dependencies
import { describe, it } from "node:test";
import assert from "node:assert";

// Import Internal Dependencies
import { AstAnalyser } from "../../index.js";

describe("probe: isESMExport", () => {
it("should detect ExportNamedDeclaration statement with a Literal source as dependency", () => {
const code = `
export { foo } from "./bar.js";
export const bar = "foo";
`;
const { dependencies } = new AstAnalyser().analyse(code);

assert.deepEqual(
[...dependencies.keys()],
["./bar.js"]
);
});

it("should detect ExportAllDeclaration statement as dependency", () => {
const code = `
export * from "./bar.js";
`;
const { dependencies } = new AstAnalyser().analyse(code);

assert.deepEqual(
[...dependencies.keys()],
["./bar.js"]
);
});
});

0 comments on commit 110101f

Please sign in to comment.