Skip to content

Commit

Permalink
Support destructuring variable declarator within TS namespace (#12760)
Browse files Browse the repository at this point in the history
* refactor: extract handleVariableDeclaration

* fix: support general variable declarator under namespace

* fix: support general declarator on export module id duplication check

* refactor: use for-in
  • Loading branch information
JLHwung committed Feb 5, 2021
1 parent 328ef42 commit 74ed698
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 21 deletions.
88 changes: 67 additions & 21 deletions packages/babel-plugin-transform-typescript/src/namespace.js
@@ -1,4 +1,4 @@
import { template } from "@babel/core";
import { template, types as t } from "@babel/core";

export default function transpileNamespace(path, t, allowNamespaces) {
if (path.node.declare || path.node.id.type === "StringLiteral") {
Expand Down Expand Up @@ -46,6 +46,57 @@ function getMemberExpression(t, name, itemName) {
return t.memberExpression(t.identifier(name), t.identifier(itemName));
}

/**
* Convert export const foo = 1 to Namepsace.foo = 1;
*
* @param {t.VariableDeclaration} node given variable declaration, e.g. `const foo = 1`
* @param {string} name the generated unique namespace member name
* @param {*} hub An instance implements HubInterface defined in `@babel/traverse` that can throw a code frame error
*/
function handleVariableDeclaration(
node: t.VariableDeclaration,
name: string,
hub: any,
): t.Statement[] {
if (node.kind !== "const") {
throw hub.file.buildCodeFrameError(
node,
"Namespaces exporting non-const are not supported by Babel." +
" Change to const or see:" +
" https://babeljs.io/docs/en/babel-plugin-transform-typescript",
);
}
const { declarations } = node;
if (declarations.every(declarator => t.isIdentifier(declarator.id))) {
// `export const a = 1` transforms to `const a = N.a = 1`, the output
// is smaller than `const a = 1; N.a = a`;
for (const declarator of node.declarations) {
declarator.init = t.assignmentExpression(
"=",
getMemberExpression(t, name, declarator.id.name),
declarator.init,
);
}
return [node];
}
// Now we have pattern in declarators
// `export const [a] = 1` transforms to `const [a] = 1; N.a = a`
const bindingIdentifiers = t.getBindingIdentifiers(node);
const assignments = [];
// getBindingIdentifiers returns an object without prototype.
// eslint-disable-next-line guard-for-in
for (const idName in bindingIdentifiers) {
assignments.push(
t.assignmentExpression(
"=",
getMemberExpression(t, name, idName),
t.cloneNode(bindingIdentifiers[idName]),
),
);
}
return [node, t.expressionStatement(t.sequenceExpression(assignments))];
}

function handleNested(path, t, node, parentExport) {
const names = new Set();
const realName = node.id;
Expand Down Expand Up @@ -78,11 +129,14 @@ function handleNested(path, t, node, parentExport) {
case "ClassDeclaration":
names.add(subNode.id.name);
continue;
case "VariableDeclaration":
for (const variable of subNode.declarations) {
names.add(variable.id.name);
case "VariableDeclaration": {
// getBindingIdentifiers returns an object without prototype.
// eslint-disable-next-line guard-for-in
for (const name in t.getBindingIdentifiers(subNode)) {
names.add(name);
}
continue;
}
default:
// Neither named declaration nor export, continue to next item.
continue;
Expand Down Expand Up @@ -111,24 +165,16 @@ function handleNested(path, t, node, parentExport) {
);
break;
}
case "VariableDeclaration":
if (subNode.declaration.kind !== "const") {
throw path.hub.file.buildCodeFrameError(
subNode.declaration,
"Namespaces exporting non-const are not supported by Babel." +
" Change to const or see:" +
" https://babeljs.io/docs/en/babel-plugin-transform-typescript",
);
}
for (const variable of subNode.declaration.declarations) {
variable.init = t.assignmentExpression(
"=",
getMemberExpression(t, name, variable.id.name),
variable.init,
);
}
namespaceTopLevel[i] = subNode.declaration;
case "VariableDeclaration": {
const nodes = handleVariableDeclaration(
subNode.declaration,
name,
path.hub,
);
namespaceTopLevel.splice(i, nodes.length, ...nodes);
i += nodes.length - 1;
break;
}
case "TSModuleDeclaration": {
const transformed = handleNested(
path,
Expand Down
@@ -0,0 +1,6 @@
namespace N {
const M1 = {}
export module M1 {}
const { M2 } = {}
export module M2 {}
}
@@ -0,0 +1,13 @@
let N;

(function (_N) {
const M1 = {};

(function (_M) {})(M1 || (M1 = _N.M1 || (_N.M1 = {})));

const {
M2
} = {};

(function (_M2) {})(M2 || (M2 = _N.M2 || (_N.M2 = {})));
})(N || (N = {}));
@@ -0,0 +1,6 @@
class C { }
namespace N {
export const { a } = C;
export const [ b ] = C;
export const [ { a: [{ b: c }] }] = A, { a: { b: { d = 1 } } = {}, ...e } = C;
}
@@ -0,0 +1,24 @@
class C {}

let N;

(function (_N) {
const {
a
} = C;
_N.a = a;
const [{
a: [{
b: c
}]
}] = A,
{
a: {
b: {
d = 1
}
} = {},
...e
} = C;
_N.e = e, _N.c = c, _N.d = d;
})(N || (N = {}));

0 comments on commit 74ed698

Please sign in to comment.