Skip to content

Implement export as namespace from #34903

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

Merged
merged 33 commits into from
Dec 21, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
b3cb74f
init export start as decl
Kingwl Nov 4, 2019
a106216
fix some broken
Kingwl Nov 4, 2019
8f56361
fix more case
Kingwl Nov 5, 2019
1b7c88b
fix more and more case
Kingwl Nov 5, 2019
fba692c
make it work
Kingwl Nov 6, 2019
97d47f8
make lint happy and accept baseline
Kingwl Nov 6, 2019
20dc273
add more tests
Kingwl Nov 6, 2019
8b3a592
fix system module
Kingwl Nov 6, 2019
8a36290
add more case
Kingwl Nov 7, 2019
2895f79
delete useless assert
Kingwl Nov 7, 2019
2ad4d1b
accept baseline
Kingwl Nov 7, 2019
33ae592
make lint happy
Kingwl Nov 7, 2019
5d4b1a5
Merge branch 'master' into export_star_as
Kingwl Dec 11, 2019
3f62ec1
Merge branch 'master' into export_star_as
Kingwl Dec 11, 2019
fb4ae00
fix missing utils
Kingwl Dec 11, 2019
c0d8705
update api
Kingwl Dec 11, 2019
584cd5b
make lint happy
Kingwl Dec 12, 2019
2667cee
add missing semi
Kingwl Dec 12, 2019
afb33c7
Merge remote-tracking branch 'origin' into export_star_as
DanielRosenwasser Dec 19, 2019
7582e25
fix minor issue
Kingwl Dec 20, 2019
ee4b1f0
fix locally bound
Kingwl Dec 20, 2019
a66b75b
avoid useless check
Kingwl Dec 20, 2019
d7355b5
update public api
Kingwl Dec 20, 2019
dc20400
add more case
Kingwl Dec 20, 2019
a595ad1
fix some case
Kingwl Dec 20, 2019
1f7a06c
Merge remote-tracking branch 'origin' into export_star_as
DanielRosenwasser Dec 20, 2019
d5e927a
Use multi-module selection in test runner to cut down on duplication.
DanielRosenwasser Dec 20, 2019
aa969ac
Accepted baselines.
DanielRosenwasser Dec 20, 2019
ef3be4a
remove superfluous tests.
DanielRosenwasser Dec 20, 2019
a5e1d49
Remove baseline.
DanielRosenwasser Dec 20, 2019
bbe5b33
Downlevel `export * as ns` in es2015.
DanielRosenwasser Dec 20, 2019
fcc6f92
Accepted baselines.
DanielRosenwasser Dec 20, 2019
ed4f9b5
Update names of things.
DanielRosenwasser Dec 20, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions src/compiler/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,10 @@ namespace ts {
break;
// 4. Export alias declarations pointing at only uninstantiated modules or things uninstantiated modules contain
case SyntaxKind.ExportDeclaration:
if (!(node as ExportDeclaration).moduleSpecifier && !!(node as ExportDeclaration).exportClause) {
const exportDeclaration = node as ExportDeclaration;
if (!exportDeclaration.moduleSpecifier && exportDeclaration.exportClause && exportDeclaration.exportClause.kind === SyntaxKind.NamedExports) {
let state = ModuleInstanceState.NonInstantiated;
for (const specifier of (node as ExportDeclaration).exportClause!.elements) {
for (const specifier of exportDeclaration.exportClause.elements) {
const specifierState = getModuleInstanceStateForAliasTarget(specifier, visited);
if (specifierState > state) {
state = specifierState;
Expand Down Expand Up @@ -2575,6 +2576,9 @@ namespace ts {
// All export * declarations are collected in an __export symbol
declareSymbol(container.symbol.exports, container.symbol, node, SymbolFlags.ExportStar, SymbolFlags.None);
}
else if (isNamespaceExport(node.exportClause)) {
declareSymbol(container.symbol.exports, container.symbol, node.exportClause, SymbolFlags.Alias, SymbolFlags.AliasExcludes);
}
}

function bindImportClause(node: ImportClause) {
Expand Down Expand Up @@ -4111,6 +4115,10 @@ namespace ts {
case SyntaxKind.SourceFile:
break;

case SyntaxKind.NamespaceExport:
transformFlags |= TransformFlags.AssertESNext;
break;

case SyntaxKind.ReturnStatement:
// Return statements may require an `await` in ES2018.
transformFlags |= TransformFlags.ContainsHoistedDeclarationOrCompletion | TransformFlags.AssertES2018;
Expand Down
45 changes: 34 additions & 11 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1547,7 +1547,7 @@ namespace ts {
const moduleExport = moduleExports.get(name);
if (moduleExport &&
moduleExport.flags === SymbolFlags.Alias &&
getDeclarationOfKind(moduleExport, SyntaxKind.ExportSpecifier)) {
(getDeclarationOfKind(moduleExport, SyntaxKind.ExportSpecifier) || getDeclarationOfKind(moduleExport, SyntaxKind.NamespaceExport))) {
break;
}
}
Expand Down Expand Up @@ -2217,6 +2217,11 @@ namespace ts {
return resolveESModuleSymbol(resolveExternalModuleName(node, moduleSpecifier), moduleSpecifier, dontResolveAlias, /*suppressUsageError*/ false);
}

function getTargetOfNamespaceExport(node: NamespaceExport, dontResolveAlias: boolean): Symbol | undefined {
const moduleSpecifier = node.parent.moduleSpecifier;
return moduleSpecifier && resolveESModuleSymbol(resolveExternalModuleName(node, moduleSpecifier), moduleSpecifier, dontResolveAlias, /*suppressUsageError*/ false);
}

// This function creates a synthetic symbol that combines the value side of one symbol with the
// type/namespace side of another symbol. Consider this example:
//
Expand Down Expand Up @@ -2382,6 +2387,8 @@ namespace ts {
return getTargetOfImportClause(<ImportClause>node, dontRecursivelyResolve);
case SyntaxKind.NamespaceImport:
return getTargetOfNamespaceImport(<NamespaceImport>node, dontRecursivelyResolve);
case SyntaxKind.NamespaceExport:
return getTargetOfNamespaceExport(<NamespaceExport>node, dontRecursivelyResolve);
case SyntaxKind.ImportSpecifier:
return getTargetOfImportSpecifier(<ImportSpecifier>node, dontRecursivelyResolve);
case SyntaxKind.ExportSpecifier:
Expand Down Expand Up @@ -5077,18 +5084,18 @@ namespace ts {

function mergeExportDeclarations(statements: Statement[]) {
// Pass 2: Combine all `export {}` declarations
const exports = filter(statements, d => isExportDeclaration(d) && !d.moduleSpecifier && !!d.exportClause) as ExportDeclaration[];
const exports = filter(statements, d => isExportDeclaration(d) && !d.moduleSpecifier && !!d.exportClause && isNamedExports(d.exportClause)) as ExportDeclaration[];
if (length(exports) > 1) {
const nonExports = filter(statements, d => !isExportDeclaration(d) || !!d.moduleSpecifier || !d.exportClause);
statements = [...nonExports, createExportDeclaration(
/*decorators*/ undefined,
/*modifiers*/ undefined,
createNamedExports(flatMap(exports, e => e.exportClause!.elements)),
createNamedExports(flatMap(exports, e => cast(e.exportClause, isNamedExports).elements)),
/*moduleSpecifier*/ undefined
)];
}
// Pass 2b: Also combine all `export {} from "..."` declarations as needed
const reexports = filter(statements, d => isExportDeclaration(d) && !!d.moduleSpecifier && !!d.exportClause) as ExportDeclaration[];
const reexports = filter(statements, d => isExportDeclaration(d) && !!d.moduleSpecifier && !!d.exportClause && isNamedExports(d.exportClause)) as ExportDeclaration[];
if (length(reexports) > 1) {
const groups = group(reexports, decl => isStringLiteral(decl.moduleSpecifier!) ? ">" + decl.moduleSpecifier.text : ">");
if (groups.length !== reexports.length) {
Expand All @@ -5100,7 +5107,7 @@ namespace ts {
createExportDeclaration(
/*decorators*/ undefined,
/*modifiers*/ undefined,
createNamedExports(flatMap(group, e => e.exportClause!.elements)),
createNamedExports(flatMap(group, e => cast(e.exportClause, isNamedExports).elements)),
group[0].moduleSpecifier
)
];
Expand All @@ -5114,8 +5121,8 @@ namespace ts {
function inlineExportModifiers(statements: Statement[]) {
// Pass 3: Move all `export {}`'s to `export` modifiers where possible
const exportDecl = find(statements, d => isExportDeclaration(d) && !d.moduleSpecifier && !!d.exportClause) as ExportDeclaration | undefined;
if (exportDecl) {
const replacements = mapDefined(exportDecl.exportClause!.elements, e => {
if (exportDecl && exportDecl.exportClause && isNamedExports(exportDecl.exportClause)) {
const replacements = mapDefined(exportDecl.exportClause.elements, e => {
if (!e.propertyName) {
// export {name} - look thru `statements` for `name`, and if all results can take an `export` modifier, do so and filter it
const associated = filter(statements, s => nodeHasName(s, e.name));
Expand All @@ -5133,7 +5140,7 @@ namespace ts {
else {
// some items filtered, others not - update the export declaration
// (mutating because why not, we're building a whole new tree here anyway)
exportDecl.exportClause!.elements = createNodeArray(replacements);
exportDecl.exportClause.elements = createNodeArray(replacements);
}
}
return statements;
Expand Down Expand Up @@ -5649,6 +5656,14 @@ namespace ts {
createLiteral(getSpecifierForModuleSymbol(target, context))
), ModifierFlags.None);
break;
case SyntaxKind.NamespaceExport:
addResult(createExportDeclaration(
/*decorators*/ undefined,
/*modifiers*/ undefined,
createNamespaceExport(createIdentifier(localName)),
createLiteral(getSpecifierForModuleSymbol(target, context))
), ModifierFlags.None);
break;
case SyntaxKind.ImportSpecifier:
addResult(createImportDeclaration(
/*decorators*/ undefined,
Expand Down Expand Up @@ -32600,7 +32615,7 @@ namespace ts {
return true;
}

function checkAliasSymbol(node: ImportEqualsDeclaration | ImportClause | NamespaceImport | ImportSpecifier | ExportSpecifier) {
function checkAliasSymbol(node: ImportEqualsDeclaration | ImportClause | NamespaceImport | ImportSpecifier | ExportSpecifier | NamespaceExport) {
let symbol = getSymbolOfNode(node);
const target = resolveAlias(symbol);

Expand Down Expand Up @@ -32719,7 +32734,12 @@ namespace ts {
if (node.exportClause) {
// export { x, y }
// export { x, y } from "foo"
forEach(node.exportClause.elements, checkExportSpecifier);
if (isNamedExports(node.exportClause)) {
forEach(node.exportClause.elements, checkExportSpecifier);
}
else if(!isNamespaceExport(node.exportClause)) {
checkImportBinding(node.exportClause);
}

const inAmbientExternalModule = node.parent.kind === SyntaxKind.ModuleBlock && isAmbientModule(node.parent.parent);
const inAmbientNamespaceDeclaration = !inAmbientExternalModule && node.parent.kind === SyntaxKind.ModuleBlock &&
Expand Down Expand Up @@ -34149,7 +34169,10 @@ namespace ts {
return isAliasResolvedToValue(getSymbolOfNode(node) || unknownSymbol);
case SyntaxKind.ExportDeclaration:
const exportClause = (<ExportDeclaration>node).exportClause;
return !!exportClause && some(exportClause.elements, isValueAliasDeclaration);
return !!exportClause && (
isNamespaceExport(exportClause) ||
some(exportClause.elements, isValueAliasDeclaration)
);
case SyntaxKind.ExportAssignment:
return (<ExportAssignment>node).expression && (<ExportAssignment>node).expression.kind === SyntaxKind.Identifier ?
isAliasResolvedToValue(getSymbolOfNode(node) || unknownSymbol) :
Expand Down
13 changes: 13 additions & 0 deletions src/compiler/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1435,6 +1435,8 @@ namespace ts {
return emitImportClause(<ImportClause>node);
case SyntaxKind.NamespaceImport:
return emitNamespaceImport(<NamespaceImport>node);
case SyntaxKind.NamespaceExport:
return emitNamespaceExport(<NamespaceExport>node);
case SyntaxKind.NamedImports:
return emitNamedImports(<NamedImports>node);
case SyntaxKind.ImportSpecifier:
Expand Down Expand Up @@ -3104,6 +3106,14 @@ namespace ts {
writeTrailingSemicolon();
}

function emitNamespaceExport(node: NamespaceExport) {
const asPos = emitTokenWithComment(SyntaxKind.AsteriskToken, node.pos, writePunctuation, node);
writeSpace();
emitTokenWithComment(SyntaxKind.AsKeyword, asPos, writeKeyword, node);
writeSpace();
emit(node.name);
}

function emitNamedExports(node: NamedExports) {
emitNamedImportsOrExports(node);
}
Expand Down Expand Up @@ -4408,6 +4418,9 @@ namespace ts {
case SyntaxKind.NamespaceImport:
generateNameIfNeeded((<NamespaceImport>node).name);
break;
case SyntaxKind.NamespaceExport:
generateNameIfNeeded((<NamespaceExport>node).name);
break;
case SyntaxKind.NamedImports:
forEach((<NamedImports>node).elements, generateNames);
break;
Expand Down
16 changes: 14 additions & 2 deletions src/compiler/factoryPublic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2278,12 +2278,24 @@ namespace ts {
return node;
}

export function createNamespaceExport(name: Identifier): NamespaceExport {
const node = <NamespaceExport>createSynthesizedNode(SyntaxKind.NamespaceExport);
node.name = name;
return node;
}

export function updateNamespaceImport(node: NamespaceImport, name: Identifier) {
return node.name !== name
? updateNode(createNamespaceImport(name), node)
: node;
}

export function updateNamespaceExport(node: NamespaceExport, name: Identifier) {
return node.name !== name
? updateNode(createNamespaceExport(name), node)
: node;
}

export function createNamedImports(elements: readonly ImportSpecifier[]): NamedImports {
const node = <NamedImports>createSynthesizedNode(SyntaxKind.NamedImports);
node.elements = createNodeArray(elements);
Expand Down Expand Up @@ -2327,7 +2339,7 @@ namespace ts {
: node;
}

export function createExportDeclaration(decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, exportClause: NamedExports | undefined, moduleSpecifier?: Expression) {
export function createExportDeclaration(decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, exportClause: NamedExportBindings | undefined, moduleSpecifier?: Expression) {
const node = <ExportDeclaration>createSynthesizedNode(SyntaxKind.ExportDeclaration);
node.decorators = asNodeArray(decorators);
node.modifiers = asNodeArray(modifiers);
Expand All @@ -2340,7 +2352,7 @@ namespace ts {
node: ExportDeclaration,
decorators: readonly Decorator[] | undefined,
modifiers: readonly Modifier[] | undefined,
exportClause: NamedExports | undefined,
exportClause: NamedExportBindings | undefined,
moduleSpecifier: Expression | undefined) {
return node.decorators !== decorators
|| node.modifiers !== modifiers
Expand Down
11 changes: 11 additions & 0 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,8 @@ namespace ts {

case SyntaxKind.NamespaceImport:
return visitNode(cbNode, (<NamespaceImport>node).name);
case SyntaxKind.NamespaceExport:
return visitNode(cbNode, (<NamespaceExport>node).name);
case SyntaxKind.NamedImports:
case SyntaxKind.NamedExports:
return visitNodes(cbNode, cbNodes, (<NamedImportsOrExports>node).elements);
Expand Down Expand Up @@ -6470,9 +6472,18 @@ namespace ts {
return finishNode(node);
}

function parseNamespaceExport(): NamespaceExport {
const node = <NamespaceExport>createNode(SyntaxKind.NamespaceExport);
node.name = parseIdentifier();
return finishNode(node);
}

function parseExportDeclaration(node: ExportDeclaration): ExportDeclaration {
node.kind = SyntaxKind.ExportDeclaration;
if (parseOptional(SyntaxKind.AsteriskToken)) {
if (parseOptional(SyntaxKind.AsKeyword)) {
node.exportClause = parseNamespaceExport();
}
parseExpected(SyntaxKind.FromKeyword);
node.moduleSpecifier = parseModuleSpecifier();
}
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/transformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ namespace ts {
switch (moduleKind) {
case ModuleKind.ESNext:
case ModuleKind.ES2015:
return transformES2015Module;
return transformECMAScriptModule;
case ModuleKind.System:
return transformSystemModule;
default:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*@internal*/
namespace ts {
export function transformES2015Module(context: TransformationContext) {
export function transformECMAScriptModule(context: TransformationContext) {
const compilerOptions = context.getCompilerOptions();
const previousOnEmitNode = context.onEmitNode;
const previousOnSubstituteNode = context.onSubstituteNode;
Expand Down Expand Up @@ -44,6 +44,9 @@ namespace ts {
return undefined;
case SyntaxKind.ExportAssignment:
return visitExportAssignment(<ExportAssignment>node);
case SyntaxKind.ExportDeclaration:
const exportDecl = (node as ExportDeclaration);
return visitExportDeclaration(exportDecl);
}

return node;
Expand All @@ -54,6 +57,41 @@ namespace ts {
return node.isExportEquals ? undefined : node;
}

function visitExportDeclaration(node: ExportDeclaration) {
// `export * as ns` only needs to be transformed in ES2015
if (compilerOptions.module !== undefined && compilerOptions.module > ModuleKind.ES2015) {
return node;
}

// Either ill-formed or don't need to be tranformed.
if (!node.exportClause || !isNamespaceExport(node.exportClause) || !node.moduleSpecifier) {
return node;
}

const oldIdentifier = node.exportClause.name;
const synthName = getGeneratedNameForNode(oldIdentifier);
const importDecl = createImportDeclaration(
/*decorators*/ undefined,
/*modifiers*/ undefined,
createImportClause(/*name*/ undefined,
createNamespaceImport(
synthName
)
),
node.moduleSpecifier,
);
setOriginalNode(importDecl, node.exportClause);

const exportDecl = createExportDeclaration(
/*decorators*/ undefined,
/*modifiers*/ undefined,
createNamedExports([createExportSpecifier(synthName, oldIdentifier)]),
);
setOriginalNode(exportDecl, node);

return [importDecl, exportDecl];
}

//
// Emit Notification
//
Expand Down
Loading