Skip to content

Commit 4c7844b

Browse files
Implement export as namespace from (#34903)
* init export start as decl * fix some broken * fix more case * fix more and more case * make it work * make lint happy and accept baseline * add more tests * fix system module * add more case * delete useless assert * accept baseline * make lint happy * fix missing utils * update api * make lint happy * add missing semi * fix minor issue * fix locally bound * avoid useless check * update public api * add more case * fix some case * Use multi-module selection in test runner to cut down on duplication. * Accepted baselines. * remove superfluous tests. * Remove baseline. * Downlevel `export * as ns` in es2015. * Accepted baselines. * Update names of things. Co-authored-by: Daniel Rosenwasser <DanielRosenwasser@users.noreply.github.com>
1 parent 2f0d07c commit 4c7844b

File tree

129 files changed

+4734
-212
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

129 files changed

+4734
-212
lines changed

src/compiler/binder.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,10 @@ namespace ts {
5656
break;
5757
// 4. Export alias declarations pointing at only uninstantiated modules or things uninstantiated modules contain
5858
case SyntaxKind.ExportDeclaration:
59-
if (!(node as ExportDeclaration).moduleSpecifier && !!(node as ExportDeclaration).exportClause) {
59+
const exportDeclaration = node as ExportDeclaration;
60+
if (!exportDeclaration.moduleSpecifier && exportDeclaration.exportClause && exportDeclaration.exportClause.kind === SyntaxKind.NamedExports) {
6061
let state = ModuleInstanceState.NonInstantiated;
61-
for (const specifier of (node as ExportDeclaration).exportClause!.elements) {
62+
for (const specifier of exportDeclaration.exportClause.elements) {
6263
const specifierState = getModuleInstanceStateForAliasTarget(specifier, visited);
6364
if (specifierState > state) {
6465
state = specifierState;
@@ -2575,6 +2576,9 @@ namespace ts {
25752576
// All export * declarations are collected in an __export symbol
25762577
declareSymbol(container.symbol.exports, container.symbol, node, SymbolFlags.ExportStar, SymbolFlags.None);
25772578
}
2579+
else if (isNamespaceExport(node.exportClause)) {
2580+
declareSymbol(container.symbol.exports, container.symbol, node.exportClause, SymbolFlags.Alias, SymbolFlags.AliasExcludes);
2581+
}
25782582
}
25792583

25802584
function bindImportClause(node: ImportClause) {
@@ -4111,6 +4115,10 @@ namespace ts {
41114115
case SyntaxKind.SourceFile:
41124116
break;
41134117

4118+
case SyntaxKind.NamespaceExport:
4119+
transformFlags |= TransformFlags.AssertESNext;
4120+
break;
4121+
41144122
case SyntaxKind.ReturnStatement:
41154123
// Return statements may require an `await` in ES2018.
41164124
transformFlags |= TransformFlags.ContainsHoistedDeclarationOrCompletion | TransformFlags.AssertES2018;

src/compiler/checker.ts

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1551,7 +1551,7 @@ namespace ts {
15511551
const moduleExport = moduleExports.get(name);
15521552
if (moduleExport &&
15531553
moduleExport.flags === SymbolFlags.Alias &&
1554-
getDeclarationOfKind(moduleExport, SyntaxKind.ExportSpecifier)) {
1554+
(getDeclarationOfKind(moduleExport, SyntaxKind.ExportSpecifier) || getDeclarationOfKind(moduleExport, SyntaxKind.NamespaceExport))) {
15551555
break;
15561556
}
15571557
}
@@ -2221,6 +2221,11 @@ namespace ts {
22212221
return resolveESModuleSymbol(resolveExternalModuleName(node, moduleSpecifier), moduleSpecifier, dontResolveAlias, /*suppressUsageError*/ false);
22222222
}
22232223

2224+
function getTargetOfNamespaceExport(node: NamespaceExport, dontResolveAlias: boolean): Symbol | undefined {
2225+
const moduleSpecifier = node.parent.moduleSpecifier;
2226+
return moduleSpecifier && resolveESModuleSymbol(resolveExternalModuleName(node, moduleSpecifier), moduleSpecifier, dontResolveAlias, /*suppressUsageError*/ false);
2227+
}
2228+
22242229
// This function creates a synthetic symbol that combines the value side of one symbol with the
22252230
// type/namespace side of another symbol. Consider this example:
22262231
//
@@ -2386,6 +2391,8 @@ namespace ts {
23862391
return getTargetOfImportClause(<ImportClause>node, dontRecursivelyResolve);
23872392
case SyntaxKind.NamespaceImport:
23882393
return getTargetOfNamespaceImport(<NamespaceImport>node, dontRecursivelyResolve);
2394+
case SyntaxKind.NamespaceExport:
2395+
return getTargetOfNamespaceExport(<NamespaceExport>node, dontRecursivelyResolve);
23892396
case SyntaxKind.ImportSpecifier:
23902397
return getTargetOfImportSpecifier(<ImportSpecifier>node, dontRecursivelyResolve);
23912398
case SyntaxKind.ExportSpecifier:
@@ -5081,18 +5088,18 @@ namespace ts {
50815088

50825089
function mergeExportDeclarations(statements: Statement[]) {
50835090
// Pass 2: Combine all `export {}` declarations
5084-
const exports = filter(statements, d => isExportDeclaration(d) && !d.moduleSpecifier && !!d.exportClause) as ExportDeclaration[];
5091+
const exports = filter(statements, d => isExportDeclaration(d) && !d.moduleSpecifier && !!d.exportClause && isNamedExports(d.exportClause)) as ExportDeclaration[];
50855092
if (length(exports) > 1) {
50865093
const nonExports = filter(statements, d => !isExportDeclaration(d) || !!d.moduleSpecifier || !d.exportClause);
50875094
statements = [...nonExports, createExportDeclaration(
50885095
/*decorators*/ undefined,
50895096
/*modifiers*/ undefined,
5090-
createNamedExports(flatMap(exports, e => e.exportClause!.elements)),
5097+
createNamedExports(flatMap(exports, e => cast(e.exportClause, isNamedExports).elements)),
50915098
/*moduleSpecifier*/ undefined
50925099
)];
50935100
}
50945101
// Pass 2b: Also combine all `export {} from "..."` declarations as needed
5095-
const reexports = filter(statements, d => isExportDeclaration(d) && !!d.moduleSpecifier && !!d.exportClause) as ExportDeclaration[];
5102+
const reexports = filter(statements, d => isExportDeclaration(d) && !!d.moduleSpecifier && !!d.exportClause && isNamedExports(d.exportClause)) as ExportDeclaration[];
50965103
if (length(reexports) > 1) {
50975104
const groups = group(reexports, decl => isStringLiteral(decl.moduleSpecifier!) ? ">" + decl.moduleSpecifier.text : ">");
50985105
if (groups.length !== reexports.length) {
@@ -5104,7 +5111,7 @@ namespace ts {
51045111
createExportDeclaration(
51055112
/*decorators*/ undefined,
51065113
/*modifiers*/ undefined,
5107-
createNamedExports(flatMap(group, e => e.exportClause!.elements)),
5114+
createNamedExports(flatMap(group, e => cast(e.exportClause, isNamedExports).elements)),
51085115
group[0].moduleSpecifier
51095116
)
51105117
];
@@ -5118,8 +5125,8 @@ namespace ts {
51185125
function inlineExportModifiers(statements: Statement[]) {
51195126
// Pass 3: Move all `export {}`'s to `export` modifiers where possible
51205127
const exportDecl = find(statements, d => isExportDeclaration(d) && !d.moduleSpecifier && !!d.exportClause) as ExportDeclaration | undefined;
5121-
if (exportDecl) {
5122-
const replacements = mapDefined(exportDecl.exportClause!.elements, e => {
5128+
if (exportDecl && exportDecl.exportClause && isNamedExports(exportDecl.exportClause)) {
5129+
const replacements = mapDefined(exportDecl.exportClause.elements, e => {
51235130
if (!e.propertyName) {
51245131
// export {name} - look thru `statements` for `name`, and if all results can take an `export` modifier, do so and filter it
51255132
const associated = filter(statements, s => nodeHasName(s, e.name));
@@ -5137,7 +5144,7 @@ namespace ts {
51375144
else {
51385145
// some items filtered, others not - update the export declaration
51395146
// (mutating because why not, we're building a whole new tree here anyway)
5140-
exportDecl.exportClause!.elements = createNodeArray(replacements);
5147+
exportDecl.exportClause.elements = createNodeArray(replacements);
51415148
}
51425149
}
51435150
return statements;
@@ -5653,6 +5660,14 @@ namespace ts {
56535660
createLiteral(getSpecifierForModuleSymbol(target, context))
56545661
), ModifierFlags.None);
56555662
break;
5663+
case SyntaxKind.NamespaceExport:
5664+
addResult(createExportDeclaration(
5665+
/*decorators*/ undefined,
5666+
/*modifiers*/ undefined,
5667+
createNamespaceExport(createIdentifier(localName)),
5668+
createLiteral(getSpecifierForModuleSymbol(target, context))
5669+
), ModifierFlags.None);
5670+
break;
56565671
case SyntaxKind.ImportSpecifier:
56575672
addResult(createImportDeclaration(
56585673
/*decorators*/ undefined,
@@ -32608,7 +32623,7 @@ namespace ts {
3260832623
return true;
3260932624
}
3261032625

32611-
function checkAliasSymbol(node: ImportEqualsDeclaration | ImportClause | NamespaceImport | ImportSpecifier | ExportSpecifier) {
32626+
function checkAliasSymbol(node: ImportEqualsDeclaration | ImportClause | NamespaceImport | ImportSpecifier | ExportSpecifier | NamespaceExport) {
3261232627
let symbol = getSymbolOfNode(node);
3261332628
const target = resolveAlias(symbol);
3261432629

@@ -32727,7 +32742,12 @@ namespace ts {
3272732742
if (node.exportClause) {
3272832743
// export { x, y }
3272932744
// export { x, y } from "foo"
32730-
forEach(node.exportClause.elements, checkExportSpecifier);
32745+
if (isNamedExports(node.exportClause)) {
32746+
forEach(node.exportClause.elements, checkExportSpecifier);
32747+
}
32748+
else if(!isNamespaceExport(node.exportClause)) {
32749+
checkImportBinding(node.exportClause);
32750+
}
3273132751

3273232752
const inAmbientExternalModule = node.parent.kind === SyntaxKind.ModuleBlock && isAmbientModule(node.parent.parent);
3273332753
const inAmbientNamespaceDeclaration = !inAmbientExternalModule && node.parent.kind === SyntaxKind.ModuleBlock &&
@@ -34157,7 +34177,10 @@ namespace ts {
3415734177
return isAliasResolvedToValue(getSymbolOfNode(node) || unknownSymbol);
3415834178
case SyntaxKind.ExportDeclaration:
3415934179
const exportClause = (<ExportDeclaration>node).exportClause;
34160-
return !!exportClause && some(exportClause.elements, isValueAliasDeclaration);
34180+
return !!exportClause && (
34181+
isNamespaceExport(exportClause) ||
34182+
some(exportClause.elements, isValueAliasDeclaration)
34183+
);
3416134184
case SyntaxKind.ExportAssignment:
3416234185
return (<ExportAssignment>node).expression && (<ExportAssignment>node).expression.kind === SyntaxKind.Identifier ?
3416334186
isAliasResolvedToValue(getSymbolOfNode(node) || unknownSymbol) :

src/compiler/emitter.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1435,6 +1435,8 @@ namespace ts {
14351435
return emitImportClause(<ImportClause>node);
14361436
case SyntaxKind.NamespaceImport:
14371437
return emitNamespaceImport(<NamespaceImport>node);
1438+
case SyntaxKind.NamespaceExport:
1439+
return emitNamespaceExport(<NamespaceExport>node);
14381440
case SyntaxKind.NamedImports:
14391441
return emitNamedImports(<NamedImports>node);
14401442
case SyntaxKind.ImportSpecifier:
@@ -3104,6 +3106,14 @@ namespace ts {
31043106
writeTrailingSemicolon();
31053107
}
31063108

3109+
function emitNamespaceExport(node: NamespaceExport) {
3110+
const asPos = emitTokenWithComment(SyntaxKind.AsteriskToken, node.pos, writePunctuation, node);
3111+
writeSpace();
3112+
emitTokenWithComment(SyntaxKind.AsKeyword, asPos, writeKeyword, node);
3113+
writeSpace();
3114+
emit(node.name);
3115+
}
3116+
31073117
function emitNamedExports(node: NamedExports) {
31083118
emitNamedImportsOrExports(node);
31093119
}
@@ -4408,6 +4418,9 @@ namespace ts {
44084418
case SyntaxKind.NamespaceImport:
44094419
generateNameIfNeeded((<NamespaceImport>node).name);
44104420
break;
4421+
case SyntaxKind.NamespaceExport:
4422+
generateNameIfNeeded((<NamespaceExport>node).name);
4423+
break;
44114424
case SyntaxKind.NamedImports:
44124425
forEach((<NamedImports>node).elements, generateNames);
44134426
break;

src/compiler/factoryPublic.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2278,12 +2278,24 @@ namespace ts {
22782278
return node;
22792279
}
22802280

2281+
export function createNamespaceExport(name: Identifier): NamespaceExport {
2282+
const node = <NamespaceExport>createSynthesizedNode(SyntaxKind.NamespaceExport);
2283+
node.name = name;
2284+
return node;
2285+
}
2286+
22812287
export function updateNamespaceImport(node: NamespaceImport, name: Identifier) {
22822288
return node.name !== name
22832289
? updateNode(createNamespaceImport(name), node)
22842290
: node;
22852291
}
22862292

2293+
export function updateNamespaceExport(node: NamespaceExport, name: Identifier) {
2294+
return node.name !== name
2295+
? updateNode(createNamespaceExport(name), node)
2296+
: node;
2297+
}
2298+
22872299
export function createNamedImports(elements: readonly ImportSpecifier[]): NamedImports {
22882300
const node = <NamedImports>createSynthesizedNode(SyntaxKind.NamedImports);
22892301
node.elements = createNodeArray(elements);
@@ -2327,7 +2339,7 @@ namespace ts {
23272339
: node;
23282340
}
23292341

2330-
export function createExportDeclaration(decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, exportClause: NamedExports | undefined, moduleSpecifier?: Expression) {
2342+
export function createExportDeclaration(decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, exportClause: NamedExportBindings | undefined, moduleSpecifier?: Expression) {
23312343
const node = <ExportDeclaration>createSynthesizedNode(SyntaxKind.ExportDeclaration);
23322344
node.decorators = asNodeArray(decorators);
23332345
node.modifiers = asNodeArray(modifiers);
@@ -2340,7 +2352,7 @@ namespace ts {
23402352
node: ExportDeclaration,
23412353
decorators: readonly Decorator[] | undefined,
23422354
modifiers: readonly Modifier[] | undefined,
2343-
exportClause: NamedExports | undefined,
2355+
exportClause: NamedExportBindings | undefined,
23442356
moduleSpecifier: Expression | undefined) {
23452357
return node.decorators !== decorators
23462358
|| node.modifiers !== modifiers

src/compiler/parser.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,8 @@ namespace ts {
390390

391391
case SyntaxKind.NamespaceImport:
392392
return visitNode(cbNode, (<NamespaceImport>node).name);
393+
case SyntaxKind.NamespaceExport:
394+
return visitNode(cbNode, (<NamespaceExport>node).name);
393395
case SyntaxKind.NamedImports:
394396
case SyntaxKind.NamedExports:
395397
return visitNodes(cbNode, cbNodes, (<NamedImportsOrExports>node).elements);
@@ -6470,9 +6472,18 @@ namespace ts {
64706472
return finishNode(node);
64716473
}
64726474

6475+
function parseNamespaceExport(): NamespaceExport {
6476+
const node = <NamespaceExport>createNode(SyntaxKind.NamespaceExport);
6477+
node.name = parseIdentifier();
6478+
return finishNode(node);
6479+
}
6480+
64736481
function parseExportDeclaration(node: ExportDeclaration): ExportDeclaration {
64746482
node.kind = SyntaxKind.ExportDeclaration;
64756483
if (parseOptional(SyntaxKind.AsteriskToken)) {
6484+
if (parseOptional(SyntaxKind.AsKeyword)) {
6485+
node.exportClause = parseNamespaceExport();
6486+
}
64766487
parseExpected(SyntaxKind.FromKeyword);
64776488
node.moduleSpecifier = parseModuleSpecifier();
64786489
}

src/compiler/transformer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ namespace ts {
44
switch (moduleKind) {
55
case ModuleKind.ESNext:
66
case ModuleKind.ES2015:
7-
return transformES2015Module;
7+
return transformECMAScriptModule;
88
case ModuleKind.System:
99
return transformSystemModule;
1010
default:

src/compiler/transformers/module/es2015.ts renamed to src/compiler/transformers/module/esnextAnd2015.ts

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*@internal*/
22
namespace ts {
3-
export function transformES2015Module(context: TransformationContext) {
3+
export function transformECMAScriptModule(context: TransformationContext) {
44
const compilerOptions = context.getCompilerOptions();
55
const previousOnEmitNode = context.onEmitNode;
66
const previousOnSubstituteNode = context.onSubstituteNode;
@@ -44,6 +44,9 @@ namespace ts {
4444
return undefined;
4545
case SyntaxKind.ExportAssignment:
4646
return visitExportAssignment(<ExportAssignment>node);
47+
case SyntaxKind.ExportDeclaration:
48+
const exportDecl = (node as ExportDeclaration);
49+
return visitExportDeclaration(exportDecl);
4750
}
4851

4952
return node;
@@ -54,6 +57,41 @@ namespace ts {
5457
return node.isExportEquals ? undefined : node;
5558
}
5659

60+
function visitExportDeclaration(node: ExportDeclaration) {
61+
// `export * as ns` only needs to be transformed in ES2015
62+
if (compilerOptions.module !== undefined && compilerOptions.module > ModuleKind.ES2015) {
63+
return node;
64+
}
65+
66+
// Either ill-formed or don't need to be tranformed.
67+
if (!node.exportClause || !isNamespaceExport(node.exportClause) || !node.moduleSpecifier) {
68+
return node;
69+
}
70+
71+
const oldIdentifier = node.exportClause.name;
72+
const synthName = getGeneratedNameForNode(oldIdentifier);
73+
const importDecl = createImportDeclaration(
74+
/*decorators*/ undefined,
75+
/*modifiers*/ undefined,
76+
createImportClause(/*name*/ undefined,
77+
createNamespaceImport(
78+
synthName
79+
)
80+
),
81+
node.moduleSpecifier,
82+
);
83+
setOriginalNode(importDecl, node.exportClause);
84+
85+
const exportDecl = createExportDeclaration(
86+
/*decorators*/ undefined,
87+
/*modifiers*/ undefined,
88+
createNamedExports([createExportSpecifier(synthName, oldIdentifier)]),
89+
);
90+
setOriginalNode(exportDecl, node);
91+
92+
return [importDecl, exportDecl];
93+
}
94+
5795
//
5896
// Emit Notification
5997
//

0 commit comments

Comments
 (0)