Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix const violations in ESM imports when transformed to CJS #13258

Merged
merged 5 commits into from Jun 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -350,31 +350,33 @@ const rewriteReferencesVisitor: Visitor<RewriteReferencesVisitorState> = {
) {
const { scope, node } = path;
const { left } = node;
const { exported, scope: programScope } = this;
const { exported, imported, scope: programScope } = this;

if (!t.isVariableDeclaration(left)) {
let didTransform = false;
const bodyPath = path.get("body");
const loopBodyScope = bodyPath.scope;
let didTransformExport = false,
importConstViolationName;
const loopBodyScope = path.get("body").scope;
for (const name of Object.keys(t.getOuterBindingIdentifiers(left))) {
if (
exported.get(name) &&
programScope.getBinding(name) === scope.getBinding(name)
) {
didTransform = true;
if (loopBodyScope.hasOwnBinding(name)) {
loopBodyScope.rename(name);
if (programScope.getBinding(name) === scope.getBinding(name)) {
if (exported.has(name)) {
didTransformExport = true;
if (loopBodyScope.hasOwnBinding(name)) {
loopBodyScope.rename(name);
}
}
if (imported.has(name) && !importConstViolationName) {
importConstViolationName = name;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we record all const violation names? e.g.

import { x, y } from 'foo';
for ( [x, y] of [] );

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's enough to report the first one, since we only inject the error for the first one (the others are not observable anyway).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think @nicolo-ribaudo is correct. Only the first is necessary as that will throw an error before the 2nd is evaluated.

}
}
}
if (!didTransform) {
if (!didTransformExport && !importConstViolationName) {
return;
}

path.ensureBlock();
const bodyPath = path.get("body");

const newLoopId = scope.generateUidIdentifierBasedOnNode(left);
bodyPath.unshiftContainer(
"body",
t.expressionStatement(t.assignmentExpression("=", left, newLoopId)),
);
path
.get("left")
.replaceWith(
Expand All @@ -383,6 +385,19 @@ const rewriteReferencesVisitor: Visitor<RewriteReferencesVisitorState> = {
]),
);
scope.registerDeclaration(path.get("left"));

if (didTransformExport) {
bodyPath.unshiftContainer(
"body",
t.expressionStatement(t.assignmentExpression("=", left, newLoopId)),
);
}
if (importConstViolationName) {
bodyPath.unshiftContainer(
"body",
t.expressionStatement(buildImportThrow(importConstViolationName)),
);
}
}
},
};
30 changes: 24 additions & 6 deletions packages/babel-helper-simple-access/src/index.ts
Expand Up @@ -100,12 +100,30 @@ const simpleAssignmentVisitor = {
return;
}

path.node.right = t.binaryExpression(
path.node.operator.slice(0, -1),
t.cloneNode(path.node.left),
path.node.right,
);
path.node.operator = "=";
const operator = path.node.operator.slice(0, -1);
if (t.LOGICAL_OPERATORS.includes(operator)) {
// &&, ||, ??
// (foo &&= bar) => (foo && foo = bar)
path.replaceWith(
t.logicalExpression(
operator,
path.node.left,
t.assignmentExpression(
"=",
t.cloneNode(path.node.left),
path.node.right,
),
),
);
} else {
// (foo += bar) => (foo = foo + bar)
path.node.right = t.binaryExpression(
operator,
t.cloneNode(path.node.left),
path.node.right,
);
path.node.operator = "=";
}
},
},
};
Expand Up @@ -16,7 +16,9 @@ for ([foo, [...foo]] of []) {
let foo;
}

for (foo of []) ;

{
let foo;
for(foo of []) {}
}
}
Expand Up @@ -58,6 +58,11 @@ for (let _ref2 of []) {
let _foo8;
}

for (let _foo9 of []) {
exports.bar = exports.foo = foo = _foo9;
;
}

{
let foo;

Expand Down
Expand Up @@ -15,3 +15,33 @@ Baz = 44;
({prop: Foo} = {});
({prop: Bar} = {});
({prop: Baz} = {});

Foo += 2;
Bar += 2;
Baz += 2;

Foo >>>= 3;
Bar >>>= 3;
Baz >>>= 3;

Foo &&= 4;
Bar &&= 4;
Baz &&= 4;

--Foo;
--Bar;
--Baz;

Foo++;
Bar++;
Baz++;

for (Foo in {}) ;
for (Bar in {}) {}
for (Baz of []) {
let Baz;
}

for ({Foo} in {}) {}
for ([Bar] in {}) {}
for ([...Baz] in {}) {}
Expand Up @@ -47,3 +47,88 @@ _baz.Baz = (44, function () {
} = ({}, function () {
throw new Error('"' + "Baz" + '" is read-only.');
}()));
_foo.default = (_foo.default + 2, function () {
throw new Error('"' + "Foo" + '" is read-only.');
}());
Bar = (Bar + 2, function () {
throw new Error('"' + "Bar" + '" is read-only.');
}());
_baz.Baz = (_baz.Baz + 2, function () {
throw new Error('"' + "Baz" + '" is read-only.');
}());
_foo.default = (_foo.default >>> 3, function () {
throw new Error('"' + "Foo" + '" is read-only.');
}());
Bar = (Bar >>> 3, function () {
throw new Error('"' + "Bar" + '" is read-only.');
}());
_baz.Baz = (_baz.Baz >>> 3, function () {
throw new Error('"' + "Baz" + '" is read-only.');
}());
_foo.default && (_foo.default = (4, function () {
throw new Error('"' + "Foo" + '" is read-only.');
}()));
Bar && (Bar = (4, function () {
throw new Error('"' + "Bar" + '" is read-only.');
}()));
_baz.Baz && (_baz.Baz = (4, function () {
throw new Error('"' + "Baz" + '" is read-only.');
}()));
_foo.default = (_foo.default - 1, function () {
throw new Error('"' + "Foo" + '" is read-only.');
}());
Bar = (Bar - 1, function () {
throw new Error('"' + "Bar" + '" is read-only.');
}());
_baz.Baz = (_baz.Baz - 1, function () {
throw new Error('"' + "Baz" + '" is read-only.');
}());
_foo.default = (_foo.default + 1, function () {
throw new Error('"' + "Foo" + '" is read-only.');
}());
Bar = (Bar + 1, function () {
throw new Error('"' + "Bar" + '" is read-only.');
}());
_baz.Baz = (_baz.Baz + 1, function () {
throw new Error('"' + "Baz" + '" is read-only.');
}());

for (let _Foo in {}) {
(function () {
throw new Error('"' + "Foo" + '" is read-only.');
})();

;
}

for (let _Bar in {}) {
(function () {
throw new Error('"' + "Bar" + '" is read-only.');
})();
}

for (let _Baz of []) {
(function () {
throw new Error('"' + "Baz" + '" is read-only.');
})();

let Baz;
}

for (let _Foo2 in {}) {
(function () {
throw new Error('"' + "Foo" + '" is read-only.');
})();
}

for (let _ref in {}) {
(function () {
throw new Error('"' + "Bar" + '" is read-only.');
})();
}

for (let _ref2 in {}) {
(function () {
throw new Error('"' + "Baz" + '" is read-only.');
})();
}