Skip to content

Commit e850525

Browse files
committed
UMD support
1 parent 0f6dbd0 commit e850525

32 files changed

+687
-16
lines changed

src/compiler/binder.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1349,6 +1349,8 @@ namespace ts {
13491349
case SyntaxKind.ImportSpecifier:
13501350
case SyntaxKind.ExportSpecifier:
13511351
return declareSymbolAndAddToSymbolTable(<Declaration>node, SymbolFlags.Alias, SymbolFlags.AliasExcludes);
1352+
case SyntaxKind.GlobalModuleExportDeclaration:
1353+
return bindGlobalModuleExportDeclaration(<GlobalModuleExportDeclaration>node);
13521354
case SyntaxKind.ImportClause:
13531355
return bindImportClause(<ImportClause>node);
13541356
case SyntaxKind.ExportDeclaration:
@@ -1398,6 +1400,15 @@ namespace ts {
13981400
}
13991401
}
14001402

1403+
function bindGlobalModuleExportDeclaration(node: GlobalModuleExportDeclaration) {
1404+
if (!file.externalModuleIndicator) {
1405+
file.bindDiagnostics.push(createDiagnosticForNode(node, Diagnostics.Global_module_exports_may_only_appear_in_module_files));
1406+
return;
1407+
}
1408+
file.symbol.globalExports = file.symbol.globalExports || {};
1409+
declareSymbol(file.symbol.globalExports, file.symbol, node, SymbolFlags.Alias, SymbolFlags.AliasExcludes);
1410+
}
1411+
14011412
function bindExportDeclaration(node: ExportDeclaration) {
14021413
if (!container.symbol || !container.symbol.exports) {
14031414
// 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

@@ -15200,6 +15206,23 @@ namespace ts {
1520015206
}
1520115207
}
1520215208

15209+
function checkGlobalModuleExportDeclaration(node: GlobalModuleExportDeclaration) {
15210+
if (node.modifiers && node.modifiers.length) {
15211+
error(node, Diagnostics.Modifiers_cannot_appear_here);
15212+
}
15213+
15214+
if (node.parent.kind !== SyntaxKind.SourceFile) {
15215+
error(node, Diagnostics.Global_module_exports_may_only_appear_at_top_level);
15216+
}
15217+
else {
15218+
const parent = node.parent as SourceFile;
15219+
// Note: the binder handles the case where the declaration isn't in an external module
15220+
if (parent.externalModuleIndicator && !parent.isDeclarationFile) {
15221+
error(node, Diagnostics.Global_module_exports_may_only_appear_in_declaration_files);
15222+
}
15223+
}
15224+
15225+
}
1520315226

1520415227
function checkSourceElement(node: Node): void {
1520515228
if (!node) {
@@ -15317,6 +15340,8 @@ namespace ts {
1531715340
return checkExportDeclaration(<ExportDeclaration>node);
1531815341
case SyntaxKind.ExportAssignment:
1531915342
return checkExportAssignment(<ExportAssignment>node);
15343+
case SyntaxKind.GlobalModuleExportDeclaration:
15344+
return checkGlobalModuleExportDeclaration(<GlobalModuleExportDeclaration>node);
1532015345
case SyntaxKind.EmptyStatement:
1532115346
checkGrammarStatementInAmbientContext(node);
1532215347
return;
@@ -16303,6 +16328,9 @@ namespace ts {
1630316328
if (file.moduleAugmentations.length) {
1630416329
(augmentations || (augmentations = [])).push(file.moduleAugmentations);
1630516330
}
16331+
if (file.wasReferenced && file.symbol && file.symbol.globalExports) {
16332+
mergeSymbolTable(globals, file.symbol.globalExports);
16333+
}
1630616334
});
1630716335

1630816336
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:
@@ -1121,7 +1124,7 @@ namespace ts {
11211124
if (token === SyntaxKind.DefaultKeyword) {
11221125
return lookAhead(nextTokenIsClassOrFunction);
11231126
}
1124-
return token !== SyntaxKind.AsteriskToken && token !== SyntaxKind.OpenBraceToken && canFollowModifier();
1127+
return token !== SyntaxKind.AsteriskToken && token !== SyntaxKind.AsKeyword && token !== SyntaxKind.OpenBraceToken && canFollowModifier();
11251128
}
11261129
if (token === SyntaxKind.DefaultKeyword) {
11271130
return nextTokenIsClassOrFunction();
@@ -4396,7 +4399,8 @@ namespace ts {
43964399
continue;
43974400

43984401
case SyntaxKind.GlobalKeyword:
4399-
return nextToken() === SyntaxKind.OpenBraceToken;
4402+
nextToken();
4403+
return token === SyntaxKind.OpenBraceToken || token === SyntaxKind.Identifier || token === SyntaxKind.ExportKeyword;
44004404

44014405
case SyntaxKind.ImportKeyword:
44024406
nextToken();
@@ -4405,7 +4409,8 @@ namespace ts {
44054409
case SyntaxKind.ExportKeyword:
44064410
nextToken();
44074411
if (token === SyntaxKind.EqualsToken || token === SyntaxKind.AsteriskToken ||
4408-
token === SyntaxKind.OpenBraceToken || token === SyntaxKind.DefaultKeyword) {
4412+
token === SyntaxKind.OpenBraceToken || token === SyntaxKind.DefaultKeyword ||
4413+
token === SyntaxKind.AsKeyword) {
44094414
return true;
44104415
}
44114416
continue;
@@ -4582,16 +4587,23 @@ namespace ts {
45824587
case SyntaxKind.EnumKeyword:
45834588
return parseEnumDeclaration(fullStart, decorators, modifiers);
45844589
case SyntaxKind.GlobalKeyword:
4590+
return parseModuleDeclaration(fullStart, decorators, modifiers);
45854591
case SyntaxKind.ModuleKeyword:
45864592
case SyntaxKind.NamespaceKeyword:
45874593
return parseModuleDeclaration(fullStart, decorators, modifiers);
45884594
case SyntaxKind.ImportKeyword:
45894595
return parseImportDeclarationOrImportEqualsDeclaration(fullStart, decorators, modifiers);
45904596
case SyntaxKind.ExportKeyword:
45914597
nextToken();
4592-
return token === SyntaxKind.DefaultKeyword || token === SyntaxKind.EqualsToken ?
4593-
parseExportAssignment(fullStart, decorators, modifiers) :
4594-
parseExportDeclaration(fullStart, decorators, modifiers);
4598+
switch (token) {
4599+
case SyntaxKind.DefaultKeyword:
4600+
case SyntaxKind.EqualsToken:
4601+
return parseExportAssignment(fullStart, decorators, modifiers);
4602+
case SyntaxKind.AsKeyword:
4603+
return parseGlobalModuleExportDeclaration(fullStart, decorators, modifiers);
4604+
default:
4605+
return parseExportDeclaration(fullStart, decorators, modifiers);
4606+
}
45954607
default:
45964608
if (decorators || modifiers) {
45974609
// We reached this point because we encountered decorators and/or modifiers and assumed a declaration
@@ -5260,6 +5272,20 @@ namespace ts {
52605272
return nextToken() === SyntaxKind.SlashToken;
52615273
}
52625274

5275+
function parseGlobalModuleExportDeclaration(fullStart: number, decorators: NodeArray<Decorator>, modifiers: ModifiersArray): GlobalModuleExportDeclaration {
5276+
const exportDeclaration = <GlobalModuleExportDeclaration>createNode(SyntaxKind.GlobalModuleExportDeclaration, fullStart);
5277+
exportDeclaration.decorators = decorators;
5278+
exportDeclaration.modifiers = modifiers;
5279+
parseExpected(SyntaxKind.AsKeyword);
5280+
parseExpected(SyntaxKind.NamespaceKeyword);
5281+
5282+
exportDeclaration.name = parseIdentifier();
5283+
5284+
parseExpected(SyntaxKind.SemicolonToken);
5285+
5286+
return finishNode(exportDeclaration);
5287+
}
5288+
52635289
function parseImportDeclarationOrImportEqualsDeclaration(fullStart: number, decorators: NodeArray<Decorator>, modifiers: ModifiersArray): ImportEqualsDeclaration | ImportDeclaration {
52645290
parseExpected(SyntaxKind.ImportKeyword);
52655291
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;
@@ -1994,6 +2003,7 @@ namespace ts {
19942003

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

src/compiler/utilities.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1475,6 +1475,7 @@ namespace ts {
14751475
// export default ...
14761476
export function isAliasSymbolDeclaration(node: Node): boolean {
14771477
return node.kind === SyntaxKind.ImportEqualsDeclaration ||
1478+
node.kind === SyntaxKind.GlobalModuleExportDeclaration ||
14781479
node.kind === SyntaxKind.ImportClause && !!(<ImportClause>node).name ||
14791480
node.kind === SyntaxKind.NamespaceImport ||
14801481
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)