From 62f21958bf54de10bfa27601b5af21e6b9114db1 Mon Sep 17 00:00:00 2001 From: Wesley Wolfe Date: Thu, 28 Mar 2019 12:15:40 -0500 Subject: [PATCH] Implement TypeScript namespace support This also fixes enum not adding the respective declaration to the scope during the typescript visitation. --- .../src/enum.js | 4 +- .../src/index.js | 6 +- .../src/namespace.js | 199 ++++++++++++++++++ .../declarations/nested-namespace/input.mjs | 4 + .../declarations/nested-namespace/output.mjs | 1 + .../namespace-fails/class-export/input.mjs | 3 + .../namespace-fails/class-export/options.json | 3 + .../fixtures/namespace-fails/class/input.mjs | 3 + .../namespace-fails/class/options.json | 3 + .../namespace-fails/enum-export/input.mjs | 3 + .../namespace-fails/enum-export/options.json | 3 + .../fixtures/namespace-fails/enum/input.mjs | 3 + .../namespace-fails/enum/options.json | 3 + .../namespace-fails/function-export/input.mjs | 3 + .../function-export/options.json | 3 + .../namespace-fails/function/input.mjs | 3 + .../namespace-fails/function/options.json | 3 + .../namespace-fails/mutable/input.mjs | 3 + .../namespace-fails/mutable/options.json | 3 + .../namespace-export/input.mjs | 3 + .../namespace-export/options.json | 3 + .../namespace-fails/namespace/input.mjs | 3 + .../namespace-fails/namespace/options.json | 3 + .../namespace-fails/variable-export/input.mjs | 3 + .../variable-export/options.json | 3 + .../namespace-fails/variable/input.mjs | 3 + .../namespace-fails/variable/options.json | 3 + .../fixtures/namespace/canonical/input.mjs | 35 +++ .../fixtures/namespace/canonical/output.mjs | 39 ++++ .../namespace/clobber-class/input.mjs | 4 + .../namespace/clobber-class/output.mjs | 5 + .../fixtures/namespace/clobber-enum/input.mjs | 6 + .../namespace/clobber-enum/output.mjs | 9 + .../test/fixtures/namespace/export/input.mjs | 1 + .../test/fixtures/namespace/export/output.mjs | 3 + .../test/fixtures/namespace/fails/input.js | 1 - .../fixtures/namespace/fails/options.json | 3 - .../fixtures/namespace/multiple/input.mjs | 2 + .../fixtures/namespace/multiple/output.mjs | 5 + .../test/fixtures/namespace/nested/input.mjs | 22 ++ .../test/fixtures/namespace/nested/output.mjs | 41 ++++ .../fixtures/namespace/undeclared/input.mjs | 1 + .../fixtures/namespace/undeclared/output.mjs | 3 + .../babel-types/src/definitions/typescript.js | 1 + .../src/validators/generated/index.js | 3 + 45 files changed, 455 insertions(+), 9 deletions(-) create mode 100644 packages/babel-plugin-transform-typescript/src/namespace.js create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/declarations/nested-namespace/input.mjs create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/declarations/nested-namespace/output.mjs create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/class-export/input.mjs create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/class-export/options.json create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/class/input.mjs create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/class/options.json create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/enum-export/input.mjs create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/enum-export/options.json create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/enum/input.mjs create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/enum/options.json create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/function-export/input.mjs create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/function-export/options.json create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/function/input.mjs create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/function/options.json create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/mutable/input.mjs create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/mutable/options.json create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/namespace-export/input.mjs create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/namespace-export/options.json create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/namespace/input.mjs create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/namespace/options.json create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/variable-export/input.mjs create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/variable-export/options.json create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/variable/input.mjs create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/variable/options.json create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/namespace/canonical/input.mjs create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/namespace/canonical/output.mjs create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/namespace/clobber-class/input.mjs create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/namespace/clobber-class/output.mjs create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/namespace/clobber-enum/input.mjs create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/namespace/clobber-enum/output.mjs create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/namespace/export/input.mjs create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/namespace/export/output.mjs delete mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/namespace/fails/input.js delete mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/namespace/fails/options.json create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/namespace/multiple/input.mjs create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/namespace/multiple/output.mjs create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/namespace/nested/input.mjs create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/namespace/nested/output.mjs create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/namespace/undeclared/input.mjs create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/namespace/undeclared/output.mjs diff --git a/packages/babel-plugin-transform-typescript/src/enum.js b/packages/babel-plugin-transform-typescript/src/enum.js index fbede7f9f547..11c7b5376d83 100644 --- a/packages/babel-plugin-transform-typescript/src/enum.js +++ b/packages/babel-plugin-transform-typescript/src/enum.js @@ -24,7 +24,9 @@ export default function transpileEnum(path, t) { path.remove(); } else { const isGlobal = t.isProgram(path.parent); // && !path.parent.body.some(t.isModuleDeclaration); - path.replaceWith(makeVar(node.id, t, isGlobal ? "var" : "let")); + path.scope.registerDeclaration( + path.replaceWith(makeVar(node.id, t, isGlobal ? "var" : "let"))[0], + ); } break; } diff --git a/packages/babel-plugin-transform-typescript/src/index.js b/packages/babel-plugin-transform-typescript/src/index.js index b55844773361..ad108e90ca8c 100644 --- a/packages/babel-plugin-transform-typescript/src/index.js +++ b/packages/babel-plugin-transform-typescript/src/index.js @@ -3,6 +3,7 @@ import syntaxTypeScript from "@babel/plugin-syntax-typescript"; import { types as t } from "@babel/core"; import transpileEnum from "./enum"; +import transpileNamespace from "./namespace"; function isInType(path) { switch (path.parent.type) { @@ -239,10 +240,7 @@ export default declare((api, { jsxPragma = "React" }) => { }, TSModuleDeclaration(path) { - if (!path.node.declare && path.node.id.type !== "StringLiteral") { - throw path.buildCodeFrameError("Namespaces are not supported."); - } - path.remove(); + transpileNamespace(path, t); }, TSInterfaceDeclaration(path) { diff --git a/packages/babel-plugin-transform-typescript/src/namespace.js b/packages/babel-plugin-transform-typescript/src/namespace.js new file mode 100644 index 000000000000..27a0b17df3ed --- /dev/null +++ b/packages/babel-plugin-transform-typescript/src/namespace.js @@ -0,0 +1,199 @@ +const SHARED_NAMESPACE_ERRORS = { + TSEnumDeclaration: "An enum may not share the name of its parent namespace.", + FunctionDeclaration: + "A function may not share the name of its parent namespace.", + ClassDeclaration: "A class may not share the name of its parent namespace.", + VariableDeclaration: + "A variable may not share the name of its parent namespace.", + TSModuleDeclaration: + "A namespace may not share the name of its parent namespace.", +}; + +export default function transpileNamespace(path, t) { + if (path.node.declare || path.node.id.type === "StringLiteral") { + path.remove(); + return; + } + + const name = path.node.id.name; + const value = handleNested(path, t, JSON.parse(JSON.stringify(path.node))); + if (path.parent.type === "ExportNamedDeclaration") { + path.parentPath.insertAfter(value); + path.replaceWith(getDeclaration(t, name)); + path.scope.registerDeclaration(path.parentPath); + } else if (path.scope.hasOwnBinding(name)) { + path.replaceWith(value); + } else { + path.scope.registerDeclaration( + path.replaceWithMultiple([getDeclaration(t, name), value])[0], + ); + } +} + +function getDeclaration(t, name) { + return t.variableDeclaration("let", [ + t.variableDeclarator(t.identifier(name)), + ]); +} + +function handleNested(path, t, node, parentExportName) { + const names = []; + const name = node.id.name; + const namespaceTopLevel = node.body.body; + for (let i = 0; i < namespaceTopLevel.length; i++) { + const subNode = namespaceTopLevel[i]; + switch (subNode.type) { + case "TSModuleDeclaration": { + const moduleName = subNode.id.name; + if (moduleName === name) { + throw path.hub.file.buildCodeFrameError( + subNode, + SHARED_NAMESPACE_ERRORS.TSModuleDeclaration, + ); + } + if (names[moduleName]) { + namespaceTopLevel[i] = handleNested(path, t, subNode); + } else { + names[moduleName] = true; + namespaceTopLevel.splice( + i++, + 1, + getDeclaration(t, moduleName), + handleNested(path, t, subNode), + ); + } + continue; + } + case "TSEnumDeclaration": + case "FunctionDeclaration": + case "ClassDeclaration": { + const itemName = subNode.id.name; + if (itemName === name) { + throw path.hub.file.buildCodeFrameError( + subNode, + SHARED_NAMESPACE_ERRORS[subNode.type], + ); + } + names[itemName] = true; + continue; + } + case "VariableDeclaration": + for (const variable of subNode.declarations) { + const variableName = variable.id.name; + if (variableName === name) { + throw path.hub.file.buildCodeFrameError( + variable, + SHARED_NAMESPACE_ERRORS.VariableDeclaration, + ); + } + names[variableName] = true; + } + continue; + default: + continue; + case "ExportNamedDeclaration": + } + switch (subNode.declaration.type) { + case "TSEnumDeclaration": + case "FunctionDeclaration": + case "ClassDeclaration": { + const itemName = subNode.declaration.id.name; + if (itemName === name) { + throw path.hub.file.buildCodeFrameError( + subNode.declaration, + SHARED_NAMESPACE_ERRORS[subNode.declaration.type], + ); + } + names[itemName] = true; + namespaceTopLevel.splice( + i++, + 1, + subNode.declaration, + t.expressionStatement( + t.assignmentExpression( + "=", + t.memberExpression(t.identifier(name), t.identifier(itemName)), + t.identifier(itemName), + ), + ), + ); + break; + } + case "VariableDeclaration": + if (subNode.declaration.kind !== "const") { + throw path.hub.file.buildCodeFrameError( + subNode.declaration, + "Namespaces exporting non-const are unsupported.", + ); + } + for (const variable of subNode.declaration.declarations) { + const variableName = variable.id.name; + if (variableName === name) { + throw path.hub.file.buildCodeFrameError( + variable, + SHARED_NAMESPACE_ERRORS.VariableDeclaration, + ); + } + variable.init = t.assignmentExpression( + "=", + t.memberExpression(t.identifier(name), t.identifier(variableName)), + variable.init, + ); + } + namespaceTopLevel[i] = subNode.declaration; + break; + case "TSModuleDeclaration": { + const moduleName = subNode.declaration.id.name; + if (moduleName === name) { + throw path.hub.file.buildCodeFrameError( + subNode.declaration, + SHARED_NAMESPACE_ERRORS.TSModuleDeclaration, + ); + } + if (names[moduleName]) { + namespaceTopLevel[i] = handleNested( + path, + t, + subNode.declaration, + name, + ); + } else { + names[moduleName] = true; + namespaceTopLevel.splice( + i++, + 1, + getDeclaration(t, moduleName), + handleNested(path, t, subNode.declaration, name), + ); + } + } + } + } + + const derivedParameter = t.logicalExpression( + "||", + t.identifier(name), + t.assignmentExpression("=", t.identifier(name), t.objectExpression([])), + ); + return t.expressionStatement( + t.callExpression( + t.functionExpression( + null, + [t.identifier(name)], + t.blockStatement(namespaceTopLevel), + ), + [ + parentExportName + ? t.assignmentExpression( + "=", + t.memberExpression( + t.identifier(parentExportName), + t.identifier(name), + ), + derivedParameter, + ) + : derivedParameter, + ], + ), + ); +} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/declarations/nested-namespace/input.mjs b/packages/babel-plugin-transform-typescript/test/fixtures/declarations/nested-namespace/input.mjs new file mode 100644 index 000000000000..8d01bb41f132 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/declarations/nested-namespace/input.mjs @@ -0,0 +1,4 @@ +; // Otherwise-empty file +export declare namespace P { + export namespace C {} +} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/declarations/nested-namespace/output.mjs b/packages/babel-plugin-transform-typescript/test/fixtures/declarations/nested-namespace/output.mjs new file mode 100644 index 000000000000..dab5d3d1c154 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/declarations/nested-namespace/output.mjs @@ -0,0 +1 @@ +; // Otherwise-empty file diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/class-export/input.mjs b/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/class-export/input.mjs new file mode 100644 index 000000000000..8389eeb8f2eb --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/class-export/input.mjs @@ -0,0 +1,3 @@ +namespace N { + export class N {} +} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/class-export/options.json b/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/class-export/options.json new file mode 100644 index 000000000000..4ea06ab1caf4 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/class-export/options.json @@ -0,0 +1,3 @@ +{ + "throws": "A class may not share the name of its parent namespace." +} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/class/input.mjs b/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/class/input.mjs new file mode 100644 index 000000000000..72850d9802d4 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/class/input.mjs @@ -0,0 +1,3 @@ +namespace N { + class N {} +} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/class/options.json b/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/class/options.json new file mode 100644 index 000000000000..4ea06ab1caf4 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/class/options.json @@ -0,0 +1,3 @@ +{ + "throws": "A class may not share the name of its parent namespace." +} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/enum-export/input.mjs b/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/enum-export/input.mjs new file mode 100644 index 000000000000..401665aaf3df --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/enum-export/input.mjs @@ -0,0 +1,3 @@ +namespace N { + export enum N {} +} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/enum-export/options.json b/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/enum-export/options.json new file mode 100644 index 000000000000..df5b56d8846c --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/enum-export/options.json @@ -0,0 +1,3 @@ +{ + "throws": "An enum may not share the name of its parent namespace." +} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/enum/input.mjs b/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/enum/input.mjs new file mode 100644 index 000000000000..f758f0d7f239 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/enum/input.mjs @@ -0,0 +1,3 @@ +namespace N { + enum N {} +} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/enum/options.json b/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/enum/options.json new file mode 100644 index 000000000000..df5b56d8846c --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/enum/options.json @@ -0,0 +1,3 @@ +{ + "throws": "An enum may not share the name of its parent namespace." +} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/function-export/input.mjs b/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/function-export/input.mjs new file mode 100644 index 000000000000..ccf5b4dfa5d5 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/function-export/input.mjs @@ -0,0 +1,3 @@ +namespace N { + export function N() {} +} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/function-export/options.json b/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/function-export/options.json new file mode 100644 index 000000000000..985cadcf34c9 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/function-export/options.json @@ -0,0 +1,3 @@ +{ + "throws": "A function may not share the name of its parent namespace." +} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/function/input.mjs b/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/function/input.mjs new file mode 100644 index 000000000000..9b8a7e87b835 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/function/input.mjs @@ -0,0 +1,3 @@ +namespace N { + function N() {} +} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/function/options.json b/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/function/options.json new file mode 100644 index 000000000000..985cadcf34c9 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/function/options.json @@ -0,0 +1,3 @@ +{ + "throws": "A function may not share the name of its parent namespace." +} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/mutable/input.mjs b/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/mutable/input.mjs new file mode 100644 index 000000000000..167274d7aee5 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/mutable/input.mjs @@ -0,0 +1,3 @@ +namespace N { + export let V; +} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/mutable/options.json b/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/mutable/options.json new file mode 100644 index 000000000000..da26bf820bdd --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/mutable/options.json @@ -0,0 +1,3 @@ +{ + "throws": "Namespaces exporting non-const are unsupported." +} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/namespace-export/input.mjs b/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/namespace-export/input.mjs new file mode 100644 index 000000000000..58f7f3b7ed8e --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/namespace-export/input.mjs @@ -0,0 +1,3 @@ +namespace N { + export namespace N {} +} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/namespace-export/options.json b/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/namespace-export/options.json new file mode 100644 index 000000000000..07ed530d0578 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/namespace-export/options.json @@ -0,0 +1,3 @@ +{ + "throws": "A namespace may not share the name of its parent namespace." +} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/namespace/input.mjs b/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/namespace/input.mjs new file mode 100644 index 000000000000..086b9de2e35a --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/namespace/input.mjs @@ -0,0 +1,3 @@ +namespace N { + namespace N {} +} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/namespace/options.json b/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/namespace/options.json new file mode 100644 index 000000000000..07ed530d0578 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/namespace/options.json @@ -0,0 +1,3 @@ +{ + "throws": "A namespace may not share the name of its parent namespace." +} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/variable-export/input.mjs b/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/variable-export/input.mjs new file mode 100644 index 000000000000..e41ac52e1da0 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/variable-export/input.mjs @@ -0,0 +1,3 @@ +namespace N { + export const N; +} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/variable-export/options.json b/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/variable-export/options.json new file mode 100644 index 000000000000..5b3ac524c9c2 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/variable-export/options.json @@ -0,0 +1,3 @@ +{ + "throws": "A variable may not share the name of its parent namespace." +} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/variable/input.mjs b/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/variable/input.mjs new file mode 100644 index 000000000000..0c42ee9ac96f --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/variable/input.mjs @@ -0,0 +1,3 @@ +namespace N { + let N; +} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/variable/options.json b/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/variable/options.json new file mode 100644 index 000000000000..5b3ac524c9c2 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/namespace-fails/variable/options.json @@ -0,0 +1,3 @@ +{ + "throws": "A variable may not share the name of its parent namespace." +} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/namespace/canonical/input.mjs b/packages/babel-plugin-transform-typescript/test/fixtures/namespace/canonical/input.mjs new file mode 100644 index 000000000000..58f6b5b21389 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/namespace/canonical/input.mjs @@ -0,0 +1,35 @@ +namespace Validation { + export interface StringValidator { + isAcceptable(s: string): boolean; + } + + const lettersRegexp = /^[A-Za-z]+$/; + const numberRegexp = /^[0-9]+$/; + + export class LettersOnlyValidator implements StringValidator { + constructor() { + console.log("1"); + } + isAcceptable(s: string) { + return lettersRegexp.test(s); + } + } + + export class ZipCodeValidator implements StringValidator { + isAcceptable(s: string) { + return s.length === 5 && numberRegexp.test(s); + } + } +} + +let strings = ["Hello", "98052", "101"]; + +let validators: { [s: string]: Validation.StringValidator; } = {}; +validators["ZIP code"] = new Validation.ZipCodeValidator(); +validators["Letters only"] = new Validation.LettersOnlyValidator(); + +for (let s of strings) { + for (let name in validators) { + console.log(`"${ s }" - ${ validators[name].isAcceptable(s) ? "matches" : "does not match" } ${ name }`); + } +} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/namespace/canonical/output.mjs b/packages/babel-plugin-transform-typescript/test/fixtures/namespace/canonical/output.mjs new file mode 100644 index 000000000000..93370969b43e --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/namespace/canonical/output.mjs @@ -0,0 +1,39 @@ +let Validation; + +(function (Validation) { + const lettersRegexp = /^[A-Za-z]+$/; + const numberRegexp = /^[0-9]+$/; + + class LettersOnlyValidator { + constructor() { + console.log("1"); + } + + isAcceptable(s) { + return lettersRegexp.test(s); + } + + } + + Validation.LettersOnlyValidator = LettersOnlyValidator; + + class ZipCodeValidator { + isAcceptable(s) { + return s.length === 5 && numberRegexp.test(s); + } + + } + + Validation.ZipCodeValidator = ZipCodeValidator; +})(Validation || (Validation = {})); + +let strings = ["Hello", "98052", "101"]; +let validators = {}; +validators["ZIP code"] = new Validation.ZipCodeValidator(); +validators["Letters only"] = new Validation.LettersOnlyValidator(); + +for (let s of strings) { + for (let name in validators) { + console.log(`"${s}" - ${validators[name].isAcceptable(s) ? "matches" : "does not match"} ${name}`); + } +} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/namespace/clobber-class/input.mjs b/packages/babel-plugin-transform-typescript/test/fixtures/namespace/clobber-class/input.mjs new file mode 100644 index 000000000000..f8debe984b83 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/namespace/clobber-class/input.mjs @@ -0,0 +1,4 @@ +class A { } +namespace A { + export const B = 1; +} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/namespace/clobber-class/output.mjs b/packages/babel-plugin-transform-typescript/test/fixtures/namespace/clobber-class/output.mjs new file mode 100644 index 000000000000..1fc854d3fcb4 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/namespace/clobber-class/output.mjs @@ -0,0 +1,5 @@ +class A {} + +(function (A) { + const B = A.B = 1; +})(A || (A = {})); diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/namespace/clobber-enum/input.mjs b/packages/babel-plugin-transform-typescript/test/fixtures/namespace/clobber-enum/input.mjs new file mode 100644 index 000000000000..7b569657b080 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/namespace/clobber-enum/input.mjs @@ -0,0 +1,6 @@ +enum A { + C = 2, +} +namespace A { + export const B = 1; +} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/namespace/clobber-enum/output.mjs b/packages/babel-plugin-transform-typescript/test/fixtures/namespace/clobber-enum/output.mjs new file mode 100644 index 000000000000..4cbb6cea231b --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/namespace/clobber-enum/output.mjs @@ -0,0 +1,9 @@ +var A; + +(function (A) { + A[A["C"] = 2] = "C"; +})(A || (A = {})); + +(function (A) { + const B = A.B = 1; +})(A || (A = {})); diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/namespace/export/input.mjs b/packages/babel-plugin-transform-typescript/test/fixtures/namespace/export/input.mjs new file mode 100644 index 000000000000..a27f292cbf1c --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/namespace/export/input.mjs @@ -0,0 +1 @@ +export namespace N {} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/namespace/export/output.mjs b/packages/babel-plugin-transform-typescript/test/fixtures/namespace/export/output.mjs new file mode 100644 index 000000000000..3f8860981205 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/namespace/export/output.mjs @@ -0,0 +1,3 @@ +export let N; + +(function (N) {})(N || (N = {})); diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/namespace/fails/input.js b/packages/babel-plugin-transform-typescript/test/fixtures/namespace/fails/input.js deleted file mode 100644 index 4db486e32f06..000000000000 --- a/packages/babel-plugin-transform-typescript/test/fixtures/namespace/fails/input.js +++ /dev/null @@ -1 +0,0 @@ -namespace N {} \ No newline at end of file diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/namespace/fails/options.json b/packages/babel-plugin-transform-typescript/test/fixtures/namespace/fails/options.json deleted file mode 100644 index 8449a74304e1..000000000000 --- a/packages/babel-plugin-transform-typescript/test/fixtures/namespace/fails/options.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "throws": "Namespaces are not supported." -} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/namespace/multiple/input.mjs b/packages/babel-plugin-transform-typescript/test/fixtures/namespace/multiple/input.mjs new file mode 100644 index 000000000000..35c080283606 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/namespace/multiple/input.mjs @@ -0,0 +1,2 @@ +namespace N {} +namespace N {} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/namespace/multiple/output.mjs b/packages/babel-plugin-transform-typescript/test/fixtures/namespace/multiple/output.mjs new file mode 100644 index 000000000000..a178777c8935 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/namespace/multiple/output.mjs @@ -0,0 +1,5 @@ +let N; + +(function (N) {})(N || (N = {})); + +(function (N) {})(N || (N = {})); diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/namespace/nested/input.mjs b/packages/babel-plugin-transform-typescript/test/fixtures/namespace/nested/input.mjs new file mode 100644 index 000000000000..510e2850bc80 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/namespace/nested/input.mjs @@ -0,0 +1,22 @@ +class A { } +namespace A { + export namespace C { + export class G {} + export const E = 7; + } + function D() {} + export namespace D { + const C = 5; + export enum H { + I = 11, + J = 13, + K = 17, + } + } + class F {} + namespace F {} + namespace G {} + enum L { + M = 19, + } +} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/namespace/nested/output.mjs b/packages/babel-plugin-transform-typescript/test/fixtures/namespace/nested/output.mjs new file mode 100644 index 000000000000..2798c0f6d1de --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/namespace/nested/output.mjs @@ -0,0 +1,41 @@ +class A {} + +(function (A) { + let C; + + (function (C) { + class G {} + + C.G = G; + const E = C.E = 7; + })(A.C = C || (C = {})); + + function D() {} + + (function (D) { + const C = 5; + let H; + + (function (H) { + H[H["I"] = 11] = "I"; + H[H["J"] = 13] = "J"; + H[H["K"] = 17] = "K"; + })(H || (H = {})); + + D.H = H; + })(A.D = D || (D = {})); + + class F {} + + (function (F) {})(F || (F = {})); + + let G; + + (function (G) {})(G || (G = {})); + + let L; + + (function (L) { + L[L["M"] = 19] = "M"; + })(L || (L = {})); +})(A || (A = {})); diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/namespace/undeclared/input.mjs b/packages/babel-plugin-transform-typescript/test/fixtures/namespace/undeclared/input.mjs new file mode 100644 index 000000000000..fe71f4004e4a --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/namespace/undeclared/input.mjs @@ -0,0 +1 @@ +namespace N {} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/namespace/undeclared/output.mjs b/packages/babel-plugin-transform-typescript/test/fixtures/namespace/undeclared/output.mjs new file mode 100644 index 000000000000..7134e600048a --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/namespace/undeclared/output.mjs @@ -0,0 +1,3 @@ +let N; + +(function (N) {})(N || (N = {})); diff --git a/packages/babel-types/src/definitions/typescript.js b/packages/babel-types/src/definitions/typescript.js index f6a89fa8dcd3..843f2408ecc9 100644 --- a/packages/babel-types/src/definitions/typescript.js +++ b/packages/babel-types/src/definitions/typescript.js @@ -399,6 +399,7 @@ defineType("TSModuleDeclaration", { }); defineType("TSModuleBlock", { + aliases: ["Scopable", "Block", "BlockParent"], visitor: ["body"], fields: { body: validateArrayOfType("Statement"), diff --git a/packages/babel-types/src/validators/generated/index.js b/packages/babel-types/src/validators/generated/index.js index 890999e5d0e1..076495d5cb28 100644 --- a/packages/babel-types/src/validators/generated/index.js +++ b/packages/babel-types/src/validators/generated/index.js @@ -3350,6 +3350,7 @@ export function isScopable(node: ?Object, opts?: Object): boolean { "ForOfStatement" === nodeType || "ClassMethod" === nodeType || "ClassPrivateMethod" === nodeType || + "TSModuleBlock" === nodeType || (nodeType === "Placeholder" && "BlockStatement" === node.expectedNode) ) { if (typeof opts === "undefined") { @@ -3382,6 +3383,7 @@ export function isBlockParent(node: ?Object, opts?: Object): boolean { "ForOfStatement" === nodeType || "ClassMethod" === nodeType || "ClassPrivateMethod" === nodeType || + "TSModuleBlock" === nodeType || (nodeType === "Placeholder" && "BlockStatement" === node.expectedNode) ) { if (typeof opts === "undefined") { @@ -3401,6 +3403,7 @@ export function isBlock(node: ?Object, opts?: Object): boolean { nodeType === "Block" || "BlockStatement" === nodeType || "Program" === nodeType || + "TSModuleBlock" === nodeType || (nodeType === "Placeholder" && "BlockStatement" === node.expectedNode) ) { if (typeof opts === "undefined") {