Skip to content
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

initial revision of external module augmentations #6213

Merged
merged 10 commits into from
Jan 16, 2016
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
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
17 changes: 13 additions & 4 deletions src/compiler/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ namespace ts {
options = opts;
inStrictMode = !!file.externalModuleIndicator;
classifiableNames = {};

Symbol = objectAllocator.getSymbolConstructor();

if (!file.locals) {
Expand Down Expand Up @@ -191,8 +192,8 @@ namespace ts {
// unless it is a well known Symbol.
function getDeclarationName(node: Declaration): string {
if (node.name) {
if (node.kind === SyntaxKind.ModuleDeclaration && node.name.kind === SyntaxKind.StringLiteral) {
return `"${(<LiteralExpression>node.name).text}"`;
if (isAmbientModule(node)) {
return isGlobalScopeAugmentation(<ModuleDeclaration>node) ? "__global" : `"${(<LiteralExpression>node.name).text}"`;
}
if (node.name.kind === SyntaxKind.ComputedPropertyName) {
const nameExpression = (<ComputedPropertyName>node.name).expression;
Expand Down Expand Up @@ -348,7 +349,12 @@ namespace ts {
// 2. When we checkIdentifier in the checker, we set its resolved symbol to the local symbol,
// but return the export symbol (by calling getExportSymbolOfValueSymbolIfExported). That way
// when the emitter comes back to it, it knows not to qualify the name if it was found in a containing scope.
if (hasExportModifier || container.flags & NodeFlags.ExportContext) {

// NOTE: Nested ambient modules always should go to to 'locals' table to prevent their automatic merge
// during global merging in the checker. Why? The only case when ambient module is permitted inside another module is module augmentation
// and this case is specially handled. Module augmentations should only be merged with original module definition
// and should never be merged directly with other augmentation, and the latter case would be possible if automatic merge is allowed.
if (!isAmbientModule(node) && (hasExportModifier || container.flags & NodeFlags.ExportContext)) {
const exportKind =
(symbolFlags & SymbolFlags.Value ? SymbolFlags.ExportValue : 0) |
(symbolFlags & SymbolFlags.Type ? SymbolFlags.ExportType : 0) |
Expand Down Expand Up @@ -843,7 +849,10 @@ namespace ts {

function bindModuleDeclaration(node: ModuleDeclaration) {
setExportContextFlag(node);
if (node.name.kind === SyntaxKind.StringLiteral) {
if (isAmbientModule(node)) {
if (node.flags & NodeFlags.Export) {
errorOnFirstToken(node, Diagnostics.export_modifier_cannot_be_applied_to_ambient_modules_and_module_augmentations_since_they_are_always_visible);
}
declareSymbolAndAddToSymbolTable(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes);
}
else {
Expand Down
244 changes: 201 additions & 43 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

39 changes: 34 additions & 5 deletions src/compiler/declarationEmitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ namespace ts {
let writer = createAndSetNewTextWriterWithSymbolWriter();

let enclosingDeclaration: Node;
let resultHasExternalModuleIndicator: boolean;
let currentText: string;
let currentLineMap: number[];
let currentIdentifiers: Map<string>;
Expand Down Expand Up @@ -101,6 +102,7 @@ namespace ts {
});
}

resultHasExternalModuleIndicator = false;
if (!isBundledEmit || !isExternalModule(sourceFile)) {
noDeclare = false;
emitSourceFile(sourceFile);
Expand Down Expand Up @@ -139,6 +141,14 @@ namespace ts {
allSourcesModuleElementDeclarationEmitInfo = allSourcesModuleElementDeclarationEmitInfo.concat(moduleElementDeclarationEmitInfo);
moduleElementDeclarationEmitInfo = [];
}

if (!isBundledEmit && isExternalModule(sourceFile) && sourceFile.moduleAugmentations.length && !resultHasExternalModuleIndicator) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about external modules with no exports or no module augmentation? soemthing like:

import {a} from "./mod";
// do something with a

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i thought we will do this for any external module, that we never emitted any import, export, or module augmentation for.

// if file was external module with augmentations - this fact should be preserved in .d.ts as well.
// in case if we didn't write any external module specifiers in .d.ts we need to emit something
// that will force compiler to think that this file is an external module - 'export {}' is a reasonable choice here.
write("export {};");
writeLine();
}
});

return {
Expand Down Expand Up @@ -721,16 +731,25 @@ namespace ts {
writer.writeLine();
}

function emitExternalModuleSpecifier(parent: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration) {
function emitExternalModuleSpecifier(parent: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ModuleDeclaration) {
// emitExternalModuleSpecifier is usually called when we emit something in the.d.ts file that will make it an external module (i.e. import/export declarations).
// the only case when it is not true is when we call it to emit correct name for module augmentation - d.ts files with just module augmentations are not considered
// external modules since they are indistingushable from script files with ambient modules. To fix this in such d.ts files we'll emit top level 'export {}'
// so compiler will treat them as external modules.
resultHasExternalModuleIndicator = resultHasExternalModuleIndicator || parent.kind !== SyntaxKind.ModuleDeclaration;
let moduleSpecifier: Node;
if (parent.kind === SyntaxKind.ImportEqualsDeclaration) {
const node = parent as ImportEqualsDeclaration;
moduleSpecifier = getExternalModuleImportEqualsDeclarationExpression(node);
}
else if (parent.kind === SyntaxKind.ModuleDeclaration) {
moduleSpecifier = (<ModuleDeclaration>parent).name;
}
else {
const node = parent as (ImportDeclaration | ExportDeclaration);
moduleSpecifier = node.moduleSpecifier;
}

if (moduleSpecifier.kind === SyntaxKind.StringLiteral && isBundledEmit && (compilerOptions.out || compilerOptions.outFile)) {
const moduleName = getExternalModuleNameFromDeclaration(host, resolver, parent);
if (moduleName) {
Expand Down Expand Up @@ -784,13 +803,23 @@ namespace ts {
function writeModuleDeclaration(node: ModuleDeclaration) {
emitJsDocComments(node);
emitModuleElementDeclarationFlags(node);
if (node.flags & NodeFlags.Namespace) {
write("namespace ");
if (isGlobalScopeAugmentation(node)) {
write("global ");
}
else {
write("module ");
if (node.flags & NodeFlags.Namespace) {
write("namespace ");
}
else {
write("module ");
}
if (isExternalModuleAugmentation(node)) {
emitExternalModuleSpecifier(node);
}
else {
writeTextOfNode(currentText, node.name);
}
}
writeTextOfNode(currentText, node.name);
while (node.body.kind !== SyntaxKind.ModuleBlock) {
node = <ModuleDeclaration>node.body;
write(".");
Expand Down
28 changes: 28 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1767,6 +1767,34 @@
"category": "Error",
"code": 2661
},
"Invalid module name in augmentation, module '{0}' cannot be found.": {
"category": "Error",
"code": 2662
},
"Module augmentation cannot introduce new names in the top level scope.": {
"category": "Error",
"code": 2663
},
"Exports and export assignments are not permitted in module augmentations.": {
"category": "Error",
"code": 2664
},
"Imports are not permitted in module augmentations. Consider moving them to the enclosing external module.": {
"category": "Error",
"code": 2665
},
"'export' modifier cannot be applied to ambient modules and module augmentations since they are always visible.": {
"category": "Error",
"code": 2666
},
"Augmentations for the global scope can only be directly nested in external modules or ambient module declarations.": {
"category": "Error",
"code": 2667
},
"Augmentations for the global scope should have 'declare' modifier unless they appear in already ambient context.": {
"category": "Error",
"code": 2668
},
"Import declaration '{0}' is using private name '{1}'.": {
"category": "Error",
"code": 4000
Expand Down
21 changes: 19 additions & 2 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4430,6 +4430,9 @@ namespace ts {
}
continue;

case SyntaxKind.GlobalKeyword:
return nextToken() === SyntaxKind.OpenBraceToken;

case SyntaxKind.ImportKeyword:
nextToken();
return token === SyntaxKind.StringLiteral || token === SyntaxKind.AsteriskToken ||
Expand Down Expand Up @@ -4494,6 +4497,7 @@ namespace ts {
case SyntaxKind.ModuleKeyword:
case SyntaxKind.NamespaceKeyword:
case SyntaxKind.TypeKeyword:
case SyntaxKind.GlobalKeyword:
// When these don't start a declaration, they're an identifier in an expression statement
return true;

Expand Down Expand Up @@ -4582,6 +4586,7 @@ namespace ts {
case SyntaxKind.PublicKeyword:
case SyntaxKind.AbstractKeyword:
case SyntaxKind.StaticKeyword:
case SyntaxKind.GlobalKeyword:
if (isStartOfDeclaration()) {
return parseDeclaration();
}
Expand Down Expand Up @@ -4609,6 +4614,7 @@ namespace ts {
return parseTypeAliasDeclaration(fullStart, decorators, modifiers);
case SyntaxKind.EnumKeyword:
return parseEnumDeclaration(fullStart, decorators, modifiers);
case SyntaxKind.GlobalKeyword:
case SyntaxKind.ModuleKeyword:
case SyntaxKind.NamespaceKeyword:
return parseModuleDeclaration(fullStart, decorators, modifiers);
Expand Down Expand Up @@ -5243,14 +5249,25 @@ namespace ts {
const node = <ModuleDeclaration>createNode(SyntaxKind.ModuleDeclaration, fullStart);
node.decorators = decorators;
setModifiers(node, modifiers);
node.name = parseLiteralNode(/*internName*/ true);
if (token === SyntaxKind.GlobalKeyword) {
// parse 'global' as name of global scope augmentation
node.name = parseIdentifier();
node.flags |= NodeFlags.GlobalAugmentation;
}
else {
node.name = parseLiteralNode(/*internName*/ true);
}
node.body = parseModuleBlock();
return finishNode(node);
}

function parseModuleDeclaration(fullStart: number, decorators: NodeArray<Decorator>, modifiers: ModifiersArray): ModuleDeclaration {
let flags = modifiers ? modifiers.flags : 0;
if (parseOptional(SyntaxKind.NamespaceKeyword)) {
if (token === SyntaxKind.GlobalKeyword) {
// global augmentation
return parseAmbientExternalModuleDeclaration(fullStart, decorators, modifiers);
}
else if (parseOptional(SyntaxKind.NamespaceKeyword)) {
flags |= NodeFlags.Namespace;
}
else {
Expand Down
Loading