Skip to content

Commit e6a306c

Browse files
author
Andy Hanson
committed
Support shorthand ambient module declarations
1 parent 166f399 commit e6a306c

13 files changed

+128
-19
lines changed

src/compiler/binder.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ namespace ts {
5353
return state;
5454
}
5555
else if (node.kind === SyntaxKind.ModuleDeclaration) {
56-
return getModuleInstanceState((<ModuleDeclaration>node).body);
56+
const body = (<ModuleDeclaration>node).body;
57+
return body ? getModuleInstanceState(body) : ModuleInstanceState.NonInstantiated;
5758
}
5859
else {
5960
return ModuleInstanceState.Instantiated;
@@ -1256,7 +1257,7 @@ namespace ts {
12561257

12571258
function hasExportDeclarations(node: ModuleDeclaration | SourceFile): boolean {
12581259
const body = node.kind === SyntaxKind.SourceFile ? node : (<ModuleDeclaration>node).body;
1259-
if (body.kind === SyntaxKind.SourceFile || body.kind === SyntaxKind.ModuleBlock) {
1260+
if (body && (body.kind === SyntaxKind.SourceFile || body.kind === SyntaxKind.ModuleBlock)) {
12601261
for (const stat of (<Block>body).statements) {
12611262
if (stat.kind === SyntaxKind.ExportDeclaration || stat.kind === SyntaxKind.ExportAssignment) {
12621263
return true;

src/compiler/checker.ts

+29-6
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,7 @@ namespace ts {
380380
}
381381

382382
function mergeSymbol(target: Symbol, source: Symbol) {
383+
//TODO: how to merge w/ shorthand ambient module?
383384
if (!(target.flags & getExcludedSymbolFlags(source.flags))) {
384385
if (source.flags & SymbolFlags.ValueModule && target.flags & SymbolFlags.ValueModule && target.constEnumOnlyModule && !source.constEnumOnlyModule) {
385386
// reset flag when merging instantiated module into value module that has only const enums
@@ -986,7 +987,9 @@ namespace ts {
986987
const moduleSymbol = resolveExternalModuleName(node, (<ImportDeclaration>node.parent).moduleSpecifier);
987988

988989
if (moduleSymbol) {
989-
const exportDefaultSymbol = moduleSymbol.exports["export="] ?
990+
const exportDefaultSymbol = isShorthandAmbientModuleSymbol(moduleSymbol) ?
991+
moduleSymbol :
992+
moduleSymbol.exports["export="] ?
990993
getPropertyOfType(getTypeOfSymbol(moduleSymbol.exports["export="]), "default") :
991994
resolveSymbol(moduleSymbol.exports["default"]);
992995

@@ -1044,6 +1047,15 @@ namespace ts {
10441047
}
10451048
}
10461049
}
1050+
1051+
//move
1052+
function isShorthandAmbientModuleSymbol(symbol: Symbol): boolean {
1053+
const module = symbol.valueDeclaration;
1054+
return module.kind === SyntaxKind.ModuleDeclaration && isShorthandAmbientModule(<ModuleDeclaration>module);
1055+
}
1056+
function isShorthandAmbientModule(module: ModuleDeclaration): boolean {
1057+
return !module.body;
1058+
}
10471059

10481060
function getPropertyOfVariable(symbol: Symbol, name: string): Symbol {
10491061
if (symbol.flags & SymbolFlags.Variable) {
@@ -1060,6 +1072,10 @@ namespace ts {
10601072
if (targetSymbol) {
10611073
const name = specifier.propertyName || specifier.name;
10621074
if (name.text) {
1075+
if (isShorthandAmbientModuleSymbol(moduleSymbol)) {
1076+
return moduleSymbol;
1077+
}
1078+
10631079
let symbolFromVariable: Symbol;
10641080
// First check if module was specified with "export=". If so, get the member from the resolved type
10651081
if (moduleSymbol && moduleSymbol.exports && moduleSymbol.exports["export="]) {
@@ -3220,9 +3236,14 @@ namespace ts {
32203236
function getTypeOfFuncClassEnumModule(symbol: Symbol): Type {
32213237
const links = getSymbolLinks(symbol);
32223238
if (!links.type) {
3223-
const type = createObjectType(TypeFlags.Anonymous, symbol);
3224-
links.type = strictNullChecks && symbol.flags & SymbolFlags.Optional ?
3225-
addNullableKind(type, TypeFlags.Undefined) : type;
3239+
if (symbol.valueDeclaration.kind === SyntaxKind.ModuleDeclaration && isShorthandAmbientModule(<ModuleDeclaration>symbol.valueDeclaration)) {
3240+
links.type = anyType;
3241+
}
3242+
else {
3243+
const type = createObjectType(TypeFlags.Anonymous, symbol);
3244+
links.type = strictNullChecks && symbol.flags & SymbolFlags.Optional ?
3245+
addNullableKind(type, TypeFlags.Undefined) : type;
3246+
}
32263247
}
32273248
return links.type;
32283249
}
@@ -16010,7 +16031,7 @@ namespace ts {
1601016031
// - augmentation for a global scope is always applied
1601116032
// - augmentation for some external module is applied if symbol for augmentation is merged (it was combined with target module).
1601216033
const checkBody = isGlobalAugmentation || (getSymbolOfNode(node).flags & SymbolFlags.Merged);
16013-
if (checkBody) {
16034+
if (checkBody && node.body) {
1601416035
// body of ambient external module is always a module block
1601516036
for (const statement of (<ModuleBlock>node.body).statements) {
1601616037
checkModuleAugmentationElement(statement, isGlobalAugmentation);
@@ -16037,7 +16058,9 @@ namespace ts {
1603716058
}
1603816059
}
1603916060
}
16040-
checkSourceElement(node.body);
16061+
if (node.body) {
16062+
checkSourceElement(node.body);
16063+
}
1604116064
}
1604216065

1604316066
function checkModuleAugmentationElement(node: Node, isGlobalAugmentation: boolean): void {

src/compiler/declarationEmitter.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -834,6 +834,7 @@ namespace ts {
834834
}
835835

836836
function writeModuleDeclaration(node: ModuleDeclaration) {
837+
if (!node.body) { throw new Error("TODO"); } //TODO
837838
emitJsDocComments(node);
838839
emitModuleElementDeclarationFlags(node);
839840
if (isGlobalScopeAugmentation(node)) {
@@ -853,7 +854,7 @@ namespace ts {
853854
writeTextOfNode(currentText, node.name);
854855
}
855856
}
856-
while (node.body.kind !== SyntaxKind.ModuleBlock) {
857+
while (node.body && node.body.kind !== SyntaxKind.ModuleBlock) {
857858
node = <ModuleDeclaration>node.body;
858859
write(".");
859860
writeTextOfNode(currentText, node.name);

src/compiler/emitter.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -6246,7 +6246,7 @@ const _super = (function (geti, seti) {
62466246
}
62476247

62486248
function getInnerMostModuleDeclarationFromDottedModule(moduleDeclaration: ModuleDeclaration): ModuleDeclaration {
6249-
if (moduleDeclaration.body.kind === SyntaxKind.ModuleDeclaration) {
6249+
if (moduleDeclaration.body && moduleDeclaration.body.kind === SyntaxKind.ModuleDeclaration) {
62506250
const recursiveInnerModule = getInnerMostModuleDeclarationFromDottedModule(<ModuleDeclaration>moduleDeclaration.body);
62516251
return recursiveInnerModule || <ModuleDeclaration>moduleDeclaration.body;
62526252
}
@@ -6295,6 +6295,7 @@ const _super = (function (geti, seti) {
62956295
write(getGeneratedNameForNode(node));
62966296
emitEnd(node.name);
62976297
write(") ");
6298+
if (!node.body) { throw new Error("TODO"); } //TODO
62986299
if (node.body.kind === SyntaxKind.ModuleBlock) {
62996300
const saveConvertedLoopState = convertedLoopState;
63006301
const saveTempFlags = tempFlags;

src/compiler/parser.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -5330,7 +5330,12 @@ namespace ts {
53305330
else {
53315331
node.name = parseLiteralNode(/*internName*/ true);
53325332
}
5333-
node.body = parseModuleBlock();
5333+
5334+
if (token === SyntaxKind.OpenBraceToken) {
5335+
node.body = parseModuleBlock();
5336+
} else {
5337+
parseSemicolon();
5338+
}
53345339
return finishNode(node);
53355340
}
53365341

src/compiler/program.ts

+6-3
Original file line numberDiff line numberDiff line change
@@ -1712,9 +1712,12 @@ namespace ts {
17121712
// The StringLiteral must specify a top - level external module name.
17131713
// Relative external module names are not permitted
17141714

1715-
// NOTE: body of ambient module is always a module block
1716-
for (const statement of (<ModuleBlock>(<ModuleDeclaration>node).body).statements) {
1717-
collectModuleReferences(statement, /*inAmbientModule*/ true);
1715+
// NOTE: body of ambient module is always a module block, if it exists
1716+
const body = <ModuleBlock>(<ModuleDeclaration>node).body;
1717+
if (body) {
1718+
for (const statement of body.statements) {
1719+
collectModuleReferences(statement, /*inAmbientModule*/ true);
1720+
}
17181721
}
17191722
}
17201723
}

src/compiler/types.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1301,7 +1301,7 @@ namespace ts {
13011301
// @kind(SyntaxKind.ModuleDeclaration)
13021302
export interface ModuleDeclaration extends DeclarationStatement {
13031303
name: Identifier | LiteralExpression;
1304-
body: ModuleBlock | ModuleDeclaration;
1304+
body?: ModuleBlock | ModuleDeclaration;
13051305
}
13061306

13071307
// @kind(SyntaxKind.ModuleBlock)

src/services/navigationBar.ts

+7-3
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,10 @@ namespace ts.NavigationBar {
188188
case SyntaxKind.ModuleDeclaration:
189189
let moduleDeclaration = <ModuleDeclaration>node;
190190
topLevelNodes.push(node);
191-
addTopLevelNodes((<Block>getInnermostModule(moduleDeclaration).body).statements, topLevelNodes);
191+
const inner = getInnermostModule(moduleDeclaration);
192+
if (inner.body) {
193+
addTopLevelNodes((<Block>inner.body).statements, topLevelNodes);
194+
}
192195
break;
193196

194197
case SyntaxKind.FunctionDeclaration:
@@ -453,7 +456,8 @@ namespace ts.NavigationBar {
453456
function createModuleItem(node: ModuleDeclaration): NavigationBarItem {
454457
const moduleName = getModuleName(node);
455458

456-
const childItems = getItemsWorker(getChildNodes((<Block>getInnermostModule(node).body).statements), createChildItem);
459+
const body = <Block>getInnermostModule(node).body;
460+
const childItems = body ? getItemsWorker(getChildNodes(body.statements), createChildItem) : [];
457461

458462
return getNavigationBarItem(moduleName,
459463
ts.ScriptElementKind.moduleElement,
@@ -611,7 +615,7 @@ namespace ts.NavigationBar {
611615
}
612616

613617
function getInnermostModule(node: ModuleDeclaration): ModuleDeclaration {
614-
while (node.body.kind === SyntaxKind.ModuleDeclaration) {
618+
while (node.body && node.body.kind === SyntaxKind.ModuleDeclaration) {
615619
node = <ModuleDeclaration>node.body;
616620
}
617621

src/services/services.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,7 @@ namespace ts {
415415
}
416416

417417
// If this is left side of dotted module declaration, there is no doc comments associated with this node
418-
if (declaration.kind === SyntaxKind.ModuleDeclaration && (<ModuleDeclaration>declaration).body.kind === SyntaxKind.ModuleDeclaration) {
418+
if (declaration.kind === SyntaxKind.ModuleDeclaration && (<ModuleDeclaration>declaration).body && (<ModuleDeclaration>declaration).body.kind === SyntaxKind.ModuleDeclaration) {
419419
return;
420420
}
421421

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//// [tests/cases/conformance/ambient/ambientShorthand.ts] ////
2+
3+
//// [declarations.d.ts]
4+
declare module "jquery"
5+
// Semicolon is optional
6+
declare module "fs";
7+
8+
//// [user.ts]
9+
///<reference path="declarations.d.ts"/>
10+
import foo, {bar} from "jquery";
11+
import * as baz from "fs";
12+
foo(bar, baz);
13+
14+
15+
//// [user.js]
16+
"use strict";
17+
///<reference path="declarations.d.ts"/>
18+
var jquery_1 = require("jquery");
19+
var baz = require("fs");
20+
jquery_1["default"](jquery_1.bar, baz);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
=== tests/cases/conformance/ambient/user.ts ===
2+
///<reference path="declarations.d.ts"/>
3+
import foo, {bar} from "jquery";
4+
>foo : Symbol(foo, Decl(user.ts, 1, 6))
5+
>bar : Symbol(bar, Decl(user.ts, 1, 13))
6+
7+
import * as baz from "fs";
8+
>baz : Symbol(baz, Decl(user.ts, 2, 6))
9+
10+
foo(bar, baz);
11+
>foo : Symbol(foo, Decl(user.ts, 1, 6))
12+
>bar : Symbol(bar, Decl(user.ts, 1, 13))
13+
>baz : Symbol(baz, Decl(user.ts, 2, 6))
14+
15+
=== tests/cases/conformance/ambient/declarations.d.ts ===
16+
declare module "jquery"
17+
No type information for this code.// Semicolon is optional
18+
No type information for this code.declare module "fs";
19+
No type information for this code.
20+
No type information for this code.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
=== tests/cases/conformance/ambient/user.ts ===
2+
///<reference path="declarations.d.ts"/>
3+
import foo, {bar} from "jquery";
4+
>foo : any
5+
>bar : any
6+
7+
import * as baz from "fs";
8+
>baz : any
9+
10+
foo(bar, baz);
11+
>foo(bar, baz) : any
12+
>foo : any
13+
>bar : any
14+
>baz : any
15+
16+
=== tests/cases/conformance/ambient/declarations.d.ts ===
17+
declare module "jquery"
18+
No type information for this code.// Semicolon is optional
19+
No type information for this code.declare module "fs";
20+
No type information for this code.
21+
No type information for this code.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// @Filename: declarations.d.ts
2+
declare module "jquery"
3+
// Semicolon is optional
4+
declare module "fs";
5+
6+
// @Filename: user.ts
7+
///<reference path="declarations.d.ts"/>
8+
import foo, {bar} from "jquery";
9+
import * as baz from "fs";
10+
foo(bar, baz);

0 commit comments

Comments
 (0)