Skip to content

Commit

Permalink
Merge pull request #6213 from Microsoft/moduleAugmentations
Browse files Browse the repository at this point in the history
initial revision of external module augmentations
  • Loading branch information
vladima committed Jan 16, 2016
2 parents 4673b57 + 36c489c commit c6b0cf1
Show file tree
Hide file tree
Showing 158 changed files with 7,664 additions and 130 deletions.
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
245 changes: 201 additions & 44 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) {
// 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 @@ -1775,6 +1775,34 @@
"category": "Error",
"code": 2663
},
"Invalid module name in augmentation, module '{0}' cannot be found.": {
"category": "Error",
"code": 2664
},
"Module augmentation cannot introduce new names in the top level scope.": {
"category": "Error",
"code": 2665
},
"Exports and export assignments are not permitted in module augmentations.": {
"category": "Error",
"code": 2666
},
"Imports are not permitted in module augmentations. Consider moving them to the enclosing external module.": {
"category": "Error",
"code": 2667
},
"'export' modifier cannot be applied to ambient modules and module augmentations since they are always visible.": {
"category": "Error",
"code": 2668
},
"Augmentations for the global scope can only be directly nested in external modules or ambient module declarations.": {
"category": "Error",
"code": 2669
},
"Augmentations for the global scope should have 'declare' modifier unless they appear in already ambient context.": {
"category": "Error",
"code": 2670
},
"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

0 comments on commit c6b0cf1

Please sign in to comment.