Skip to content

Commit c8a0b13

Browse files
committed
UMD support
1 parent f34dcdd commit c8a0b13

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

@@ -15197,6 +15203,23 @@ namespace ts {
1519715203
}
1519815204
}
1519915205

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

1520115224
function checkSourceElement(node: Node): void {
1520215225
if (!node) {
@@ -15314,6 +15337,8 @@ namespace ts {
1531415337
return checkExportDeclaration(<ExportDeclaration>node);
1531515338
case SyntaxKind.ExportAssignment:
1531615339
return checkExportAssignment(<ExportAssignment>node);
15340+
case SyntaxKind.GlobalModuleExportDeclaration:
15341+
return checkGlobalModuleExportDeclaration(<GlobalModuleExportDeclaration>node);
1531715342
case SyntaxKind.EmptyStatement:
1531815343
checkGrammarStatementInAmbientContext(node);
1531915344
return;
@@ -16300,6 +16325,9 @@ namespace ts {
1630016325
if (file.moduleAugmentations.length) {
1630116326
(augmentations || (augmentations = [])).push(file.moduleAugmentations);
1630216327
}
16328+
if (file.wasReferenced && file.symbol && file.symbol.globalExports) {
16329+
mergeSymbolTable(globals, file.symbol.globalExports);
16330+
}
1630316331
});
1630416332

1630516333
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:
@@ -1143,7 +1146,7 @@ namespace ts {
11431146
if (token === SyntaxKind.DefaultKeyword) {
11441147
return lookAhead(nextTokenIsClassOrFunction);
11451148
}
1146-
return token !== SyntaxKind.AsteriskToken && token !== SyntaxKind.OpenBraceToken && canFollowModifier();
1149+
return token !== SyntaxKind.AsteriskToken && token !== SyntaxKind.AsKeyword && token !== SyntaxKind.OpenBraceToken && canFollowModifier();
11471150
}
11481151
if (token === SyntaxKind.DefaultKeyword) {
11491152
return nextTokenIsClassOrFunction();
@@ -4418,7 +4421,8 @@ namespace ts {
44184421
continue;
44194422

44204423
case SyntaxKind.GlobalKeyword:
4421-
return nextToken() === SyntaxKind.OpenBraceToken;
4424+
nextToken();
4425+
return token === SyntaxKind.OpenBraceToken || token === SyntaxKind.Identifier || token === SyntaxKind.ExportKeyword;
44224426

44234427
case SyntaxKind.ImportKeyword:
44244428
nextToken();
@@ -4427,7 +4431,8 @@ namespace ts {
44274431
case SyntaxKind.ExportKeyword:
44284432
nextToken();
44294433
if (token === SyntaxKind.EqualsToken || token === SyntaxKind.AsteriskToken ||
4430-
token === SyntaxKind.OpenBraceToken || token === SyntaxKind.DefaultKeyword) {
4434+
token === SyntaxKind.OpenBraceToken || token === SyntaxKind.DefaultKeyword ||
4435+
token === SyntaxKind.AsKeyword) {
44314436
return true;
44324437
}
44334438
continue;
@@ -4604,16 +4609,23 @@ namespace ts {
46044609
case SyntaxKind.EnumKeyword:
46054610
return parseEnumDeclaration(fullStart, decorators, modifiers);
46064611
case SyntaxKind.GlobalKeyword:
4612+
return parseModuleDeclaration(fullStart, decorators, modifiers);
46074613
case SyntaxKind.ModuleKeyword:
46084614
case SyntaxKind.NamespaceKeyword:
46094615
return parseModuleDeclaration(fullStart, decorators, modifiers);
46104616
case SyntaxKind.ImportKeyword:
46114617
return parseImportDeclarationOrImportEqualsDeclaration(fullStart, decorators, modifiers);
46124618
case SyntaxKind.ExportKeyword:
46134619
nextToken();
4614-
return token === SyntaxKind.DefaultKeyword || token === SyntaxKind.EqualsToken ?
4615-
parseExportAssignment(fullStart, decorators, modifiers) :
4616-
parseExportDeclaration(fullStart, decorators, modifiers);
4620+
switch (token) {
4621+
case SyntaxKind.DefaultKeyword:
4622+
case SyntaxKind.EqualsToken:
4623+
return parseExportAssignment(fullStart, decorators, modifiers);
4624+
case SyntaxKind.AsKeyword:
4625+
return parseGlobalModuleExportDeclaration(fullStart, decorators, modifiers);
4626+
default:
4627+
return parseExportDeclaration(fullStart, decorators, modifiers);
4628+
}
46174629
default:
46184630
if (decorators || modifiers) {
46194631
// We reached this point because we encountered decorators and/or modifiers and assumed a declaration
@@ -5282,6 +5294,20 @@ namespace ts {
52825294
return nextToken() === SyntaxKind.SlashToken;
52835295
}
52845296

5297+
function parseGlobalModuleExportDeclaration(fullStart: number, decorators: NodeArray<Decorator>, modifiers: ModifiersArray): GlobalModuleExportDeclaration {
5298+
const exportDeclaration = <GlobalModuleExportDeclaration>createNode(SyntaxKind.GlobalModuleExportDeclaration, fullStart);
5299+
exportDeclaration.decorators = decorators;
5300+
exportDeclaration.modifiers = modifiers;
5301+
parseExpected(SyntaxKind.AsKeyword);
5302+
parseExpected(SyntaxKind.NamespaceKeyword);
5303+
5304+
exportDeclaration.name = parseIdentifier();
5305+
5306+
parseExpected(SyntaxKind.SemicolonToken);
5307+
5308+
return finishNode(exportDeclaration);
5309+
}
5310+
52855311
function parseImportDeclarationOrImportEqualsDeclaration(fullStart: number, decorators: NodeArray<Decorator>, modifiers: ModifiersArray): ImportEqualsDeclaration | ImportDeclaration {
52865312
parseExpected(SyntaxKind.ImportKeyword);
52875313
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)