Skip to content

Commit 44aa738

Browse files
committed
UMD support
1 parent b29f460 commit 44aa738

32 files changed

Lines changed: 687 additions & 16 deletions

src/compiler/binder.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1355,6 +1355,8 @@ namespace ts {
13551355
case SyntaxKind.ImportSpecifier:
13561356
case SyntaxKind.ExportSpecifier:
13571357
return declareSymbolAndAddToSymbolTable(<Declaration>node, SymbolFlags.Alias, SymbolFlags.AliasExcludes);
1358+
case SyntaxKind.GlobalModuleExportDeclaration:
1359+
return bindGlobalModuleExportDeclaration(<GlobalModuleExportDeclaration>node);
13581360
case SyntaxKind.ImportClause:
13591361
return bindImportClause(<ImportClause>node);
13601362
case SyntaxKind.ExportDeclaration:
@@ -1404,6 +1406,15 @@ namespace ts {
14041406
}
14051407
}
14061408

1409+
function bindGlobalModuleExportDeclaration(node: GlobalModuleExportDeclaration) {
1410+
if (!file.externalModuleIndicator) {
1411+
file.bindDiagnostics.push(createDiagnosticForNode(node, Diagnostics.Global_module_exports_may_only_appear_in_module_files));
1412+
return;
1413+
}
1414+
file.symbol.globalExports = file.symbol.globalExports || {};
1415+
declareSymbol(file.symbol.globalExports, file.symbol, node, SymbolFlags.Alias, SymbolFlags.AliasExcludes);
1416+
}
1417+
14071418
function bindExportDeclaration(node: ExportDeclaration) {
14081419
if (!container.symbol || !container.symbol.exports) {
14091420
// Export * in some sort of block construct

src/compiler/checker.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -981,6 +981,10 @@ namespace ts {
981981
return getExternalModuleMember(<ImportDeclaration>node.parent.parent.parent, node);
982982
}
983983

984+
function getTargetOfGlobalModuleExportDeclaration(node: GlobalModuleExportDeclaration): Symbol {
985+
return node.parent.symbol;
986+
}
987+
984988
function getTargetOfExportSpecifier(node: ExportSpecifier): Symbol {
985989
return (<ExportDeclaration>node.parent.parent).moduleSpecifier ?
986990
getExternalModuleMember(<ExportDeclaration>node.parent.parent, node) :
@@ -1005,6 +1009,8 @@ namespace ts {
10051009
return getTargetOfExportSpecifier(<ExportSpecifier>node);
10061010
case SyntaxKind.ExportAssignment:
10071011
return getTargetOfExportAssignment(<ExportAssignment>node);
1012+
case SyntaxKind.GlobalModuleExportDeclaration:
1013+
return getTargetOfGlobalModuleExportDeclaration(<GlobalModuleExportDeclaration>node);
10081014
}
10091015
}
10101016

@@ -15220,6 +15226,23 @@ namespace ts {
1522015226
}
1522115227
}
1522215228

15229+
function checkGlobalModuleExportDeclaration(node: GlobalModuleExportDeclaration) {
15230+
if (node.modifiers && node.modifiers.length) {
15231+
error(node, Diagnostics.Modifiers_cannot_appear_here);
15232+
}
15233+
15234+
if (node.parent.kind !== SyntaxKind.SourceFile) {
15235+
error(node, Diagnostics.Global_module_exports_may_only_appear_at_top_level);
15236+
}
15237+
else {
15238+
const parent = node.parent as SourceFile;
15239+
// Note: the binder handles the case where the declaration isn't in an external module
15240+
if (parent.externalModuleIndicator && !parent.isDeclarationFile) {
15241+
error(node, Diagnostics.Global_module_exports_may_only_appear_in_declaration_files);
15242+
}
15243+
}
15244+
15245+
}
1522315246

1522415247
function checkSourceElement(node: Node): void {
1522515248
if (!node) {
@@ -15337,6 +15360,8 @@ namespace ts {
1533715360
return checkExportDeclaration(<ExportDeclaration>node);
1533815361
case SyntaxKind.ExportAssignment:
1533915362
return checkExportAssignment(<ExportAssignment>node);
15363+
case SyntaxKind.GlobalModuleExportDeclaration:
15364+
return checkGlobalModuleExportDeclaration(<GlobalModuleExportDeclaration>node);
1534015365
case SyntaxKind.EmptyStatement:
1534115366
checkGrammarStatementInAmbientContext(node);
1534215367
return;
@@ -16331,6 +16356,9 @@ namespace ts {
1633116356
if (file.moduleAugmentations.length) {
1633216357
(augmentations || (augmentations = [])).push(file.moduleAugmentations);
1633316358
}
16359+
if (file.wasReferenced && file.symbol && file.symbol.globalExports) {
16360+
mergeSymbolTable(globals, file.symbol.globalExports);
16361+
}
1633416362
});
1633516363

1633616364
if (augmentations) {

src/compiler/diagnosticMessages.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -831,6 +831,18 @@
831831
"category": "Error",
832832
"code": 1313
833833
},
834+
"Global module exports may only appear in module files.": {
835+
"category": "Error",
836+
"code": 1314
837+
},
838+
"Global module exports may only appear in declaration files.": {
839+
"category": "Error",
840+
"code": 1315
841+
},
842+
"Global module exports may only appear at top level.": {
843+
"category": "Error",
844+
"code": 1316
845+
},
834846
"Duplicate identifier '{0}'.": {
835847
"category": "Error",
836848
"code": 2300

src/compiler/parser.ts

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,9 @@ namespace ts {
301301
case SyntaxKind.ImportClause:
302302
return visitNode(cbNode, (<ImportClause>node).name) ||
303303
visitNode(cbNode, (<ImportClause>node).namedBindings);
304+
case SyntaxKind.GlobalModuleExportDeclaration:
305+
return visitNode(cbNode, (<GlobalModuleExportDeclaration>node).name);
306+
304307
case SyntaxKind.NamespaceImport:
305308
return visitNode(cbNode, (<NamespaceImport>node).name);
306309
case SyntaxKind.NamedImports:
@@ -1125,7 +1128,7 @@ namespace ts {
11251128
if (token === SyntaxKind.DefaultKeyword) {
11261129
return lookAhead(nextTokenIsClassOrFunction);
11271130
}
1128-
return token !== SyntaxKind.AsteriskToken && token !== SyntaxKind.OpenBraceToken && canFollowModifier();
1131+
return token !== SyntaxKind.AsteriskToken && token !== SyntaxKind.AsKeyword && token !== SyntaxKind.OpenBraceToken && canFollowModifier();
11291132
}
11301133
if (token === SyntaxKind.DefaultKeyword) {
11311134
return nextTokenIsClassOrFunction();
@@ -4400,7 +4403,8 @@ namespace ts {
44004403
continue;
44014404

44024405
case SyntaxKind.GlobalKeyword:
4403-
return nextToken() === SyntaxKind.OpenBraceToken;
4406+
nextToken();
4407+
return token === SyntaxKind.OpenBraceToken || token === SyntaxKind.Identifier || token === SyntaxKind.ExportKeyword;
44044408

44054409
case SyntaxKind.ImportKeyword:
44064410
nextToken();
@@ -4409,7 +4413,8 @@ namespace ts {
44094413
case SyntaxKind.ExportKeyword:
44104414
nextToken();
44114415
if (token === SyntaxKind.EqualsToken || token === SyntaxKind.AsteriskToken ||
4412-
token === SyntaxKind.OpenBraceToken || token === SyntaxKind.DefaultKeyword) {
4416+
token === SyntaxKind.OpenBraceToken || token === SyntaxKind.DefaultKeyword ||
4417+
token === SyntaxKind.AsKeyword) {
44134418
return true;
44144419
}
44154420
continue;
@@ -4586,16 +4591,23 @@ namespace ts {
45864591
case SyntaxKind.EnumKeyword:
45874592
return parseEnumDeclaration(fullStart, decorators, modifiers);
45884593
case SyntaxKind.GlobalKeyword:
4594+
return parseModuleDeclaration(fullStart, decorators, modifiers);
45894595
case SyntaxKind.ModuleKeyword:
45904596
case SyntaxKind.NamespaceKeyword:
45914597
return parseModuleDeclaration(fullStart, decorators, modifiers);
45924598
case SyntaxKind.ImportKeyword:
45934599
return parseImportDeclarationOrImportEqualsDeclaration(fullStart, decorators, modifiers);
45944600
case SyntaxKind.ExportKeyword:
45954601
nextToken();
4596-
return token === SyntaxKind.DefaultKeyword || token === SyntaxKind.EqualsToken ?
4597-
parseExportAssignment(fullStart, decorators, modifiers) :
4598-
parseExportDeclaration(fullStart, decorators, modifiers);
4602+
switch (token) {
4603+
case SyntaxKind.DefaultKeyword:
4604+
case SyntaxKind.EqualsToken:
4605+
return parseExportAssignment(fullStart, decorators, modifiers);
4606+
case SyntaxKind.AsKeyword:
4607+
return parseGlobalModuleExportDeclaration(fullStart, decorators, modifiers);
4608+
default:
4609+
return parseExportDeclaration(fullStart, decorators, modifiers);
4610+
}
45994611
default:
46004612
if (decorators || modifiers) {
46014613
// We reached this point because we encountered decorators and/or modifiers and assumed a declaration
@@ -5264,6 +5276,20 @@ namespace ts {
52645276
return nextToken() === SyntaxKind.SlashToken;
52655277
}
52665278

5279+
function parseGlobalModuleExportDeclaration(fullStart: number, decorators: NodeArray<Decorator>, modifiers: ModifiersArray): GlobalModuleExportDeclaration {
5280+
const exportDeclaration = <GlobalModuleExportDeclaration>createNode(SyntaxKind.GlobalModuleExportDeclaration, fullStart);
5281+
exportDeclaration.decorators = decorators;
5282+
exportDeclaration.modifiers = modifiers;
5283+
parseExpected(SyntaxKind.AsKeyword);
5284+
parseExpected(SyntaxKind.NamespaceKeyword);
5285+
5286+
exportDeclaration.name = parseIdentifier();
5287+
5288+
parseExpected(SyntaxKind.SemicolonToken);
5289+
5290+
return finishNode(exportDeclaration);
5291+
}
5292+
52675293
function parseImportDeclarationOrImportEqualsDeclaration(fullStart: number, decorators: NodeArray<Decorator>, modifiers: ModifiersArray): ImportEqualsDeclaration | ImportDeclaration {
52685294
parseExpected(SyntaxKind.ImportKeyword);
52695295
const afterImportPos = scanner.getStartPos();

src/compiler/program.ts

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1282,7 +1282,7 @@ namespace ts {
12821282
}
12831283

12841284
function processRootFile(fileName: string, isDefaultLib: boolean) {
1285-
processSourceFile(normalizePath(fileName), isDefaultLib);
1285+
processSourceFile(normalizePath(fileName), isDefaultLib, /*isReference*/ true);
12861286
}
12871287

12881288
function fileReferenceIsEqualTo(a: FileReference, b: FileReference): boolean {
@@ -1376,15 +1376,18 @@ namespace ts {
13761376
}
13771377
}
13781378

1379-
function processSourceFile(fileName: string, isDefaultLib: boolean, refFile?: SourceFile, refPos?: number, refEnd?: number) {
1379+
/**
1380+
* 'isReference' indicates whether the file was brought in via a reference directive (rather than an import declaration)
1381+
*/
1382+
function processSourceFile(fileName: string, isDefaultLib: boolean, isReference: boolean, refFile?: SourceFile, refPos?: number, refEnd?: number) {
13801383
let diagnosticArgument: string[];
13811384
let diagnostic: DiagnosticMessage;
13821385
if (hasExtension(fileName)) {
13831386
if (!options.allowNonTsExtensions && !forEach(supportedExtensions, extension => fileExtensionIs(host.getCanonicalFileName(fileName), extension))) {
13841387
diagnostic = Diagnostics.File_0_has_unsupported_extension_The_only_supported_extensions_are_1;
13851388
diagnosticArgument = [fileName, "'" + supportedExtensions.join("', '") + "'"];
13861389
}
1387-
else if (!findSourceFile(fileName, toPath(fileName, currentDirectory, getCanonicalFileName), isDefaultLib, refFile, refPos, refEnd)) {
1390+
else if (!findSourceFile(fileName, toPath(fileName, currentDirectory, getCanonicalFileName), isDefaultLib, isReference, refFile, refPos, refEnd)) {
13881391
diagnostic = Diagnostics.File_0_not_found;
13891392
diagnosticArgument = [fileName];
13901393
}
@@ -1394,13 +1397,13 @@ namespace ts {
13941397
}
13951398
}
13961399
else {
1397-
const nonTsFile: SourceFile = options.allowNonTsExtensions && findSourceFile(fileName, toPath(fileName, currentDirectory, getCanonicalFileName), isDefaultLib, refFile, refPos, refEnd);
1400+
const nonTsFile: SourceFile = options.allowNonTsExtensions && findSourceFile(fileName, toPath(fileName, currentDirectory, getCanonicalFileName), isDefaultLib, isReference, refFile, refPos, refEnd);
13981401
if (!nonTsFile) {
13991402
if (options.allowNonTsExtensions) {
14001403
diagnostic = Diagnostics.File_0_not_found;
14011404
diagnosticArgument = [fileName];
14021405
}
1403-
else if (!forEach(supportedExtensions, extension => findSourceFile(fileName + extension, toPath(fileName + extension, currentDirectory, getCanonicalFileName), isDefaultLib, refFile, refPos, refEnd))) {
1406+
else if (!forEach(supportedExtensions, extension => findSourceFile(fileName + extension, toPath(fileName + extension, currentDirectory, getCanonicalFileName), isDefaultLib, isReference, refFile, refPos, refEnd))) {
14041407
diagnostic = Diagnostics.File_0_not_found;
14051408
fileName += ".ts";
14061409
diagnosticArgument = [fileName];
@@ -1429,7 +1432,7 @@ namespace ts {
14291432
}
14301433

14311434
// Get source file from normalized fileName
1432-
function findSourceFile(fileName: string, path: Path, isDefaultLib: boolean, refFile?: SourceFile, refPos?: number, refEnd?: number): SourceFile {
1435+
function findSourceFile(fileName: string, path: Path, isDefaultLib: boolean, isReference: boolean, refFile?: SourceFile, refPos?: number, refEnd?: number): SourceFile {
14331436
if (filesByName.contains(path)) {
14341437
const file = filesByName.get(path);
14351438
// try to check if we've already seen this file but with a different casing in path
@@ -1438,6 +1441,10 @@ namespace ts {
14381441
reportFileNamesDifferOnlyInCasingError(fileName, file.fileName, refFile, refPos, refEnd);
14391442
}
14401443

1444+
if (file) {
1445+
file.wasReferenced = file.wasReferenced || isReference;
1446+
}
1447+
14411448
return file;
14421449
}
14431450

@@ -1454,6 +1461,7 @@ namespace ts {
14541461

14551462
filesByName.set(path, file);
14561463
if (file) {
1464+
file.wasReferenced = file.wasReferenced || isReference;
14571465
file.path = path;
14581466

14591467
if (host.useCaseSensitiveFileNames()) {
@@ -1491,7 +1499,7 @@ namespace ts {
14911499
function processReferencedFiles(file: SourceFile, basePath: string) {
14921500
forEach(file.referencedFiles, ref => {
14931501
const referencedFileName = resolveTripleslashReference(ref.fileName, file.fileName);
1494-
processSourceFile(referencedFileName, /*isDefaultLib*/ false, file, ref.pos, ref.end);
1502+
processSourceFile(referencedFileName, /*isDefaultLib*/ false, /*isReference*/ true, file, ref.pos, ref.end);
14951503
});
14961504
}
14971505

@@ -1517,7 +1525,7 @@ namespace ts {
15171525
i < file.imports.length;
15181526

15191527
if (shouldAddFile) {
1520-
const importedFile = findSourceFile(resolution.resolvedFileName, toPath(resolution.resolvedFileName, currentDirectory, getCanonicalFileName), /*isDefaultLib*/ false, file, skipTrivia(file.text, file.imports[i].pos), file.imports[i].end);
1528+
const importedFile = findSourceFile(resolution.resolvedFileName, toPath(resolution.resolvedFileName, currentDirectory, getCanonicalFileName), /*isDefaultLib*/ false, /*isReference*/ false, file, skipTrivia(file.text, file.imports[i].pos), file.imports[i].end);
15211529

15221530
if (importedFile && resolution.isExternalLibraryImport) {
15231531
// Since currently irrespective of allowJs, we only look for supportedTypeScript extension external module files,

src/compiler/types.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@ namespace ts {
274274
ModuleDeclaration,
275275
ModuleBlock,
276276
CaseBlock,
277+
GlobalModuleExportDeclaration,
277278
ImportEqualsDeclaration,
278279
ImportDeclaration,
279280
ImportClause,
@@ -1324,6 +1325,12 @@ namespace ts {
13241325
name: Identifier;
13251326
}
13261327

1328+
// @kind(SyntaxKind.GlobalModuleImport)
1329+
export interface GlobalModuleExportDeclaration extends DeclarationStatement {
1330+
name: Identifier;
1331+
moduleReference: LiteralLikeNode;
1332+
}
1333+
13271334
// @kind(SyntaxKind.ExportDeclaration)
13281335
export interface ExportDeclaration extends DeclarationStatement {
13291336
exportClause?: NamedExports;
@@ -1537,6 +1544,8 @@ namespace ts {
15371544
/* @internal */ externalModuleIndicator: Node;
15381545
// The first node that causes this file to be a CommonJS module
15391546
/* @internal */ commonJsModuleIndicator: Node;
1547+
// True if the file was a root file in a compilation or a /// reference targets
1548+
/* @internal */ wasReferenced?: boolean;
15401549

15411550
/* @internal */ identifiers: Map<string>;
15421551
/* @internal */ nodeCount: number;
@@ -1995,6 +2004,7 @@ namespace ts {
19952004

19962005
members?: SymbolTable; // Class, interface or literal instance members
19972006
exports?: SymbolTable; // Module exports
2007+
globalExports?: SymbolTable; // Conditional global UMD exports
19982008
/* @internal */ id?: number; // Unique id (used to look up SymbolLinks)
19992009
/* @internal */ mergeId?: number; // Merge id (used to look up merged symbol)
20002010
/* @internal */ parent?: Symbol; // Parent symbol

src/compiler/utilities.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1471,6 +1471,7 @@ namespace ts {
14711471
// export default ...
14721472
export function isAliasSymbolDeclaration(node: Node): boolean {
14731473
return node.kind === SyntaxKind.ImportEqualsDeclaration ||
1474+
node.kind === SyntaxKind.GlobalModuleExportDeclaration ||
14741475
node.kind === SyntaxKind.ImportClause && !!(<ImportClause>node).name ||
14751476
node.kind === SyntaxKind.NamespaceImport ||
14761477
node.kind === SyntaxKind.ImportSpecifier ||

src/harness/compilerRunner.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ class CompilerBaselineRunner extends RunnerBase {
8888
toBeCompiled = [];
8989
otherFiles = [];
9090

91-
if (/require\(/.test(lastUnit.content) || /reference\spath/.test(lastUnit.content)) {
91+
if (testCaseContent.settings["noImplicitReferences"] || /require\(/.test(lastUnit.content) || /reference\spath/.test(lastUnit.content)) {
9292
toBeCompiled.push({ unitName: this.makeUnitName(lastUnit.name, rootDir), content: lastUnit.content });
9393
units.forEach(unit => {
9494
if (unit.name !== lastUnit.name) {

src/harness/harness.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -896,7 +896,8 @@ namespace Harness {
896896
{ name: "fileName", type: "string" },
897897
{ name: "libFiles", type: "string" },
898898
{ name: "noErrorTruncation", type: "boolean" },
899-
{ name: "suppressOutputPathCheck", type: "boolean" }
899+
{ name: "suppressOutputPathCheck", type: "boolean" },
900+
{ name: "noImplicitReferences", type: "boolean" }
900901
];
901902

902903
let optionsIndex: ts.Map<ts.CommandLineOption>;

0 commit comments

Comments
 (0)