Skip to content

Commit

Permalink
Fix: local conflict in export declaration
Browse files Browse the repository at this point in the history
  • Loading branch information
eight04 committed Jun 27, 2024
1 parent 7367569 commit 7c31a89
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 1 deletion.
35 changes: 34 additions & 1 deletion lib/import-to-globals.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ function writeIdentifier(code, node, parent, name) {
code.appendLeft(node.end, `: ${name}`);
parent.key.isOverwritten = true;
parent.value.isOverwritten = true;
} else if (parent.type === "ExportSpecifier" && parent.local.start === parent.exported.start) {
code.appendLeft(node.start, `${name} as `);
parent.local.isOverwritten = true;
parent.exported.isOverwritten = true;
} else {
code.overwrite(node.start, node.end, name, {contentOnly: true});
// FIXME: do we need this?
Expand Down Expand Up @@ -101,13 +105,35 @@ function getDynamicImportSource(node) {
}
}

// export left hand analyzer
class ExportLeftHand {
constructor() {
this.inDeclaration = false;
this.inLeftHand = false;
}
enter(node, parent) {
if (parent && parent.type === "Program") {
this.inDeclaration = node.type === "ExportNamedDeclaration";
}
if (this.inDeclaration && parent.type === "VariableDeclarator" && parent.id === node) {
this.inLeftHand = true;
}
}
leave(node, parent) {
if (this.inLeftHand && parent.type === "VariableDeclarator") {
this.inLeftHand = false;
}
}
}

async function importToGlobals({ast, code, getName, getDynamicWrapper}) {
await prepare();
let scope = attachScopes(ast, "scope");
const bindings = new Map;
const globals = new Set;
let isTouched = false;
const tempNames = new Set;
const exportLeftHand = new ExportLeftHand;

for (const node of ast.body) {
if (node.type === "ImportDeclaration") {
Expand All @@ -120,6 +146,7 @@ async function importToGlobals({ast, code, getName, getDynamicWrapper}) {
let topStatement;
walk(ast, {
enter(node, parent) {
exportLeftHand.enter(node, parent);
if (parent && parent.type === "Program") {
topStatement = node;
}
Expand All @@ -138,7 +165,12 @@ async function importToGlobals({ast, code, getName, getDynamicWrapper}) {
writeIdentifier(code, node, parent, bindings.get(node.name));
}
} else if (globals.has(node.name) && scope.contains(node.name)) {
// conflict with local variable
writeIdentifier(code, node, parent, `_local_${node.name}`);
if (exportLeftHand.inLeftHand) {
code.appendLeft(topStatement.end, `export {_local_${node.name} as ${node.name}};\n`);
code.remove(topStatement.start, topStatement.declaration.start);
}
}
}
const source = getDynamicImportSource(node);
Expand All @@ -150,7 +182,8 @@ async function importToGlobals({ast, code, getName, getDynamicWrapper}) {
this.skip();
}
},
leave(node) {
leave(node, parent) {
exportLeftHand.leave(node, parent);
if (node.scope) {
scope = node.scope.parent;
}
Expand Down
35 changes: 35 additions & 0 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,41 @@ describe("main", () => {
})
);

it("conflict exported", () =>
withDir(`
- entry.js: |
import foo from "foo";
export const FOO = 123;
console.log(foo, FOO);
`, async resolve => {
const {output: {"entry.js": {code}}} = await bundle(resolve("entry.js"), {foo: "FOO"});
assert.equal(code.trim(), endent`
const _local_FOO = 123;
console.log(FOO, _local_FOO);
export { _local_FOO as FOO };
`);
})
);

it("conflict exported 2", () =>
withDir(`
- entry.js: |
import foo from "foo";
const FOO = 123;
export {FOO};
console.log(foo, FOO);
`, async resolve => {
const {output: {"entry.js": {code}}} = await bundle(resolve("entry.js"), {foo: "FOO"});
assert.equal(code.trim(), endent`
const _local_FOO = 123;
console.log(FOO, _local_FOO);
export { _local_FOO as FOO };
`);
})
);

it("don't touch unused", () =>
withDir(`
- entry.js: |
Expand Down

0 comments on commit 7c31a89

Please sign in to comment.