Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement TypeScript namespace support (#9785)
- Loading branch information
1 parent
8bf9714
commit 0a98814
Showing
39 changed files
with
959 additions
and
289 deletions.
There are no files selected for viewing
571 changes: 287 additions & 284 deletions
571
packages/babel-plugin-transform-typescript/src/index.js
Large diffs are not rendered by default.
Oops, something went wrong.
171 changes: 171 additions & 0 deletions
171
packages/babel-plugin-transform-typescript/src/namespace.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
import { template } from "@babel/core"; | ||
|
||
export default function transpileNamespace(path, t, allowNamespaces) { | ||
if (path.node.declare || path.node.id.type === "StringLiteral") { | ||
path.remove(); | ||
return; | ||
} | ||
|
||
if (!allowNamespaces) { | ||
throw path.hub.file.buildCodeFrameError( | ||
path.node.id, | ||
"Namespace not marked type-only declare." + | ||
" Non-declarative namespaces are only supported experimentally in Babel." + | ||
" To enable and review caveats see:" + | ||
" https://babeljs.io/docs/en/babel-plugin-transform-typescript", | ||
); | ||
} | ||
|
||
const name = path.node.id.name; | ||
const value = handleNested(path, t, t.cloneDeep(path.node)); | ||
const bound = path.scope.hasOwnBinding(name); | ||
if (path.parent.type === "ExportNamedDeclaration") { | ||
if (!bound) { | ||
path.parentPath.insertAfter(value); | ||
path.replaceWith(getDeclaration(t, name)); | ||
path.scope.registerDeclaration(path.parentPath); | ||
} else { | ||
path.parentPath.replaceWith(value); | ||
} | ||
} else if (bound) { | ||
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 getMemberExpression(t, name, itemName) { | ||
return t.memberExpression(t.identifier(name), t.identifier(itemName)); | ||
} | ||
|
||
function handleNested(path, t, node, parentExport) { | ||
const names = new Set(); | ||
const realName = node.id; | ||
const name = path.scope.generateUid(realName.name); | ||
const namespaceTopLevel = node.body.body; | ||
for (let i = 0; i < namespaceTopLevel.length; i++) { | ||
const subNode = namespaceTopLevel[i]; | ||
|
||
// The first switch is mainly to detect name usage. Only export | ||
// declarations require further transformation. | ||
switch (subNode.type) { | ||
case "TSModuleDeclaration": { | ||
const transformed = handleNested(path, t, subNode); | ||
const moduleName = subNode.id.name; | ||
if (names.has(moduleName)) { | ||
namespaceTopLevel[i] = transformed; | ||
} else { | ||
names.add(moduleName); | ||
namespaceTopLevel.splice( | ||
i++, | ||
1, | ||
getDeclaration(t, moduleName), | ||
transformed, | ||
); | ||
} | ||
continue; | ||
} | ||
case "TSEnumDeclaration": | ||
case "FunctionDeclaration": | ||
case "ClassDeclaration": | ||
names.add(subNode.id.name); | ||
continue; | ||
case "VariableDeclaration": | ||
for (const variable of subNode.declarations) { | ||
names.add(variable.id.name); | ||
} | ||
continue; | ||
default: | ||
// Neither named declaration nor export, continue to next item. | ||
continue; | ||
case "ExportNamedDeclaration": | ||
// Export declarations get parsed using the next switch. | ||
} | ||
|
||
// Transform the export declarations that occur inside of a namespace. | ||
switch (subNode.declaration.type) { | ||
case "TSEnumDeclaration": | ||
case "FunctionDeclaration": | ||
case "ClassDeclaration": { | ||
const itemName = subNode.declaration.id.name; | ||
names.add(itemName); | ||
namespaceTopLevel.splice( | ||
i++, | ||
1, | ||
subNode.declaration, | ||
t.expressionStatement( | ||
t.assignmentExpression( | ||
"=", | ||
getMemberExpression(t, name, itemName), | ||
t.identifier(itemName), | ||
), | ||
), | ||
); | ||
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; | ||
break; | ||
case "TSModuleDeclaration": { | ||
const transformed = handleNested( | ||
path, | ||
t, | ||
subNode.declaration, | ||
t.identifier(name), | ||
); | ||
const moduleName = subNode.declaration.id.name; | ||
if (names.has(moduleName)) { | ||
namespaceTopLevel[i] = transformed; | ||
} else { | ||
names.add(moduleName); | ||
namespaceTopLevel.splice( | ||
i++, | ||
1, | ||
getDeclaration(t, moduleName), | ||
transformed, | ||
); | ||
} | ||
} | ||
} | ||
} | ||
|
||
// {} | ||
let fallthroughValue = t.objectExpression([]); | ||
|
||
if (parentExport) { | ||
fallthroughValue = template.expression.ast` | ||
${parentExport}.${realName} || ( | ||
${parentExport}.${realName} = ${fallthroughValue} | ||
) | ||
`; | ||
} | ||
|
||
return template.statement.ast` | ||
(function (${t.identifier(name)}) { | ||
${namespaceTopLevel} | ||
})(${realName} || (${realName} = ${fallthroughValue})); | ||
`; | ||
} |
4 changes: 4 additions & 0 deletions
4
...s/babel-plugin-transform-typescript/test/fixtures/declarations/nested-namespace/input.mjs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
; // Otherwise-empty file | ||
export declare namespace P { | ||
export namespace C {} | ||
} |
1 change: 1 addition & 0 deletions
1
.../babel-plugin-transform-typescript/test/fixtures/declarations/nested-namespace/output.mjs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
; // Otherwise-empty file |
35 changes: 35 additions & 0 deletions
35
packages/babel-plugin-transform-typescript/test/fixtures/namespace/canonical/input.mjs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 }`); | ||
} | ||
} |
39 changes: 39 additions & 0 deletions
39
packages/babel-plugin-transform-typescript/test/fixtures/namespace/canonical/output.mjs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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}`); | ||
} | ||
} |
4 changes: 4 additions & 0 deletions
4
packages/babel-plugin-transform-typescript/test/fixtures/namespace/clobber-class/input.mjs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
class A { } | ||
namespace A { | ||
export const B = 1; | ||
} |
5 changes: 5 additions & 0 deletions
5
packages/babel-plugin-transform-typescript/test/fixtures/namespace/clobber-class/output.mjs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
class A {} | ||
|
||
(function (_A) { | ||
const B = _A.B = 1; | ||
})(A || (A = {})); |
6 changes: 6 additions & 0 deletions
6
packages/babel-plugin-transform-typescript/test/fixtures/namespace/clobber-enum/input.mjs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
enum A { | ||
C = 2, | ||
} | ||
namespace A { | ||
export const B = 1; | ||
} |
9 changes: 9 additions & 0 deletions
9
packages/babel-plugin-transform-typescript/test/fixtures/namespace/clobber-enum/output.mjs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
var A; | ||
|
||
(function (A) { | ||
A[A["C"] = 2] = "C"; | ||
})(A || (A = {})); | ||
|
||
(function (_A) { | ||
const B = _A.B = 1; | ||
})(A || (A = {})); |
3 changes: 3 additions & 0 deletions
3
packages/babel-plugin-transform-typescript/test/fixtures/namespace/clobber-export/input.mjs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export class N {} | ||
export namespace N {} | ||
export default N; |
5 changes: 5 additions & 0 deletions
5
packages/babel-plugin-transform-typescript/test/fixtures/namespace/clobber-export/output.mjs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export class N {} | ||
|
||
(function (_N) {})(N || (N = {})); | ||
|
||
export default N; |
3 changes: 3 additions & 0 deletions
3
packages/babel-plugin-transform-typescript/test/fixtures/namespace/clobber-import/input.mjs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import N from 'n'; | ||
|
||
namespace N {} |
3 changes: 3 additions & 0 deletions
3
packages/babel-plugin-transform-typescript/test/fixtures/namespace/clobber-import/output.mjs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import N from 'n'; | ||
|
||
(function (_N) {})(N || (N = {})); |
36 changes: 36 additions & 0 deletions
36
...ges/babel-plugin-transform-typescript/test/fixtures/namespace/contentious-names/input.mjs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
namespace N { | ||
namespace N {} | ||
namespace constructor {} | ||
namespace length {} | ||
namespace concat {} | ||
namespace copyWithin {} | ||
namespace fill {} | ||
namespace find {} | ||
namespace findIndex {} | ||
namespace lastIndexOf {} | ||
namespace pop {} | ||
namespace push {} | ||
namespace reverse {} | ||
namespace shift {} | ||
namespace unshift {} | ||
namespace slice {} | ||
namespace sort {} | ||
namespace splice {} | ||
namespace includes {} | ||
namespace indexOf {} | ||
namespace join {} | ||
namespace keys {} | ||
namespace entries {} | ||
namespace values {} | ||
namespace forEach {} | ||
namespace filter {} | ||
namespace map {} | ||
namespace every {} | ||
namespace some {} | ||
namespace reduce {} | ||
namespace reduceRight {} | ||
namespace toLocaleString {} | ||
namespace toString {} | ||
namespace flat {} | ||
namespace flatMap {} | ||
} |
Oops, something went wrong.