Skip to content

Commit 7d372bf

Browse files
committed
Merge pull request #7264 from RyanCavanaugh/umd
UMD support
2 parents 489bf92 + 043b338 commit 7d372bf

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+2095
-17
lines changed

src/compiler/binder.ts

+29
Original file line numberDiff line numberDiff line change
@@ -1356,6 +1356,8 @@ namespace ts {
13561356
case SyntaxKind.ImportSpecifier:
13571357
case SyntaxKind.ExportSpecifier:
13581358
return declareSymbolAndAddToSymbolTable(<Declaration>node, SymbolFlags.Alias, SymbolFlags.AliasExcludes);
1359+
case SyntaxKind.GlobalModuleExportDeclaration:
1360+
return bindGlobalModuleExportDeclaration(<GlobalModuleExportDeclaration>node);
13591361
case SyntaxKind.ImportClause:
13601362
return bindImportClause(<ImportClause>node);
13611363
case SyntaxKind.ExportDeclaration:
@@ -1405,6 +1407,33 @@ namespace ts {
14051407
}
14061408
}
14071409

1410+
function bindGlobalModuleExportDeclaration(node: GlobalModuleExportDeclaration) {
1411+
if (node.modifiers && node.modifiers.length) {
1412+
file.bindDiagnostics.push(createDiagnosticForNode(node, Diagnostics.Modifiers_cannot_appear_here));
1413+
}
1414+
1415+
if (node.parent.kind !== SyntaxKind.SourceFile) {
1416+
file.bindDiagnostics.push(createDiagnosticForNode(node, Diagnostics.Global_module_exports_may_only_appear_at_top_level));
1417+
return;
1418+
}
1419+
else {
1420+
const parent = node.parent as SourceFile;
1421+
1422+
if (!isExternalModule(parent)) {
1423+
file.bindDiagnostics.push(createDiagnosticForNode(node, Diagnostics.Global_module_exports_may_only_appear_in_module_files));
1424+
return;
1425+
}
1426+
1427+
if (!parent.isDeclarationFile) {
1428+
file.bindDiagnostics.push(createDiagnosticForNode(node, Diagnostics.Global_module_exports_may_only_appear_in_declaration_files));
1429+
return;
1430+
}
1431+
}
1432+
1433+
file.symbol.globalExports = file.symbol.globalExports || {};
1434+
declareSymbol(file.symbol.globalExports, file.symbol, node, SymbolFlags.Alias, SymbolFlags.AliasExcludes);
1435+
}
1436+
14081437
function bindExportDeclaration(node: ExportDeclaration) {
14091438
if (!container.symbol || !container.symbol.exports) {
14101439
// Export * in some sort of block construct

src/compiler/checker.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -992,6 +992,10 @@ namespace ts {
992992
return getExternalModuleMember(<ImportDeclaration>node.parent.parent.parent, node);
993993
}
994994

995+
function getTargetOfGlobalModuleExportDeclaration(node: GlobalModuleExportDeclaration): Symbol {
996+
return resolveExternalModuleSymbol(node.parent.symbol);
997+
}
998+
995999
function getTargetOfExportSpecifier(node: ExportSpecifier): Symbol {
9961000
return (<ExportDeclaration>node.parent.parent).moduleSpecifier ?
9971001
getExternalModuleMember(<ExportDeclaration>node.parent.parent, node) :
@@ -1016,6 +1020,8 @@ namespace ts {
10161020
return getTargetOfExportSpecifier(<ExportSpecifier>node);
10171021
case SyntaxKind.ExportAssignment:
10181022
return getTargetOfExportAssignment(<ExportAssignment>node);
1023+
case SyntaxKind.GlobalModuleExportDeclaration:
1024+
return getTargetOfGlobalModuleExportDeclaration(<GlobalModuleExportDeclaration>node);
10191025
}
10201026
}
10211027

@@ -15282,7 +15288,6 @@ namespace ts {
1528215288
}
1528315289
}
1528415290

15285-
1528615291
function checkSourceElement(node: Node): void {
1528715292
if (!node) {
1528815293
return;
@@ -16393,6 +16398,9 @@ namespace ts {
1639316398
if (file.moduleAugmentations.length) {
1639416399
(augmentations || (augmentations = [])).push(file.moduleAugmentations);
1639516400
}
16401+
if (file.wasReferenced && file.symbol && file.symbol.globalExports) {
16402+
mergeSymbolTable(globals, file.symbol.globalExports);
16403+
}
1639616404
});
1639716405

1639816406
if (augmentations) {

src/compiler/diagnosticMessages.json

+12
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

+31-6
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;
@@ -4593,9 +4598,15 @@ namespace ts {
45934598
return parseImportDeclarationOrImportEqualsDeclaration(fullStart, decorators, modifiers);
45944599
case SyntaxKind.ExportKeyword:
45954600
nextToken();
4596-
return token === SyntaxKind.DefaultKeyword || token === SyntaxKind.EqualsToken ?
4597-
parseExportAssignment(fullStart, decorators, modifiers) :
4598-
parseExportDeclaration(fullStart, decorators, modifiers);
4601+
switch (token) {
4602+
case SyntaxKind.DefaultKeyword:
4603+
case SyntaxKind.EqualsToken:
4604+
return parseExportAssignment(fullStart, decorators, modifiers);
4605+
case SyntaxKind.AsKeyword:
4606+
return parseGlobalModuleExportDeclaration(fullStart, decorators, modifiers);
4607+
default:
4608+
return parseExportDeclaration(fullStart, decorators, modifiers);
4609+
}
45994610
default:
46004611
if (decorators || modifiers) {
46014612
// We reached this point because we encountered decorators and/or modifiers and assumed a declaration
@@ -5264,6 +5275,20 @@ namespace ts {
52645275
return nextToken() === SyntaxKind.SlashToken;
52655276
}
52665277

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

src/compiler/program.ts

+16-8
Original file line numberDiff line numberDiff line change
@@ -1286,7 +1286,7 @@ namespace ts {
12861286
}
12871287

12881288
function processRootFile(fileName: string, isDefaultLib: boolean) {
1289-
processSourceFile(normalizePath(fileName), isDefaultLib);
1289+
processSourceFile(normalizePath(fileName), isDefaultLib, /*isReference*/ true);
12901290
}
12911291

12921292
function fileReferenceIsEqualTo(a: FileReference, b: FileReference): boolean {
@@ -1380,15 +1380,18 @@ namespace ts {
13801380
}
13811381
}
13821382

1383-
function processSourceFile(fileName: string, isDefaultLib: boolean, refFile?: SourceFile, refPos?: number, refEnd?: number) {
1383+
/**
1384+
* 'isReference' indicates whether the file was brought in via a reference directive (rather than an import declaration)
1385+
*/
1386+
function processSourceFile(fileName: string, isDefaultLib: boolean, isReference: boolean, refFile?: SourceFile, refPos?: number, refEnd?: number) {
13841387
let diagnosticArgument: string[];
13851388
let diagnostic: DiagnosticMessage;
13861389
if (hasExtension(fileName)) {
13871390
if (!options.allowNonTsExtensions && !forEach(supportedExtensions, extension => fileExtensionIs(host.getCanonicalFileName(fileName), extension))) {
13881391
diagnostic = Diagnostics.File_0_has_unsupported_extension_The_only_supported_extensions_are_1;
13891392
diagnosticArgument = [fileName, "'" + supportedExtensions.join("', '") + "'"];
13901393
}
1391-
else if (!findSourceFile(fileName, toPath(fileName, currentDirectory, getCanonicalFileName), isDefaultLib, refFile, refPos, refEnd)) {
1394+
else if (!findSourceFile(fileName, toPath(fileName, currentDirectory, getCanonicalFileName), isDefaultLib, isReference, refFile, refPos, refEnd)) {
13921395
diagnostic = Diagnostics.File_0_not_found;
13931396
diagnosticArgument = [fileName];
13941397
}
@@ -1398,13 +1401,13 @@ namespace ts {
13981401
}
13991402
}
14001403
else {
1401-
const nonTsFile: SourceFile = options.allowNonTsExtensions && findSourceFile(fileName, toPath(fileName, currentDirectory, getCanonicalFileName), isDefaultLib, refFile, refPos, refEnd);
1404+
const nonTsFile: SourceFile = options.allowNonTsExtensions && findSourceFile(fileName, toPath(fileName, currentDirectory, getCanonicalFileName), isDefaultLib, isReference, refFile, refPos, refEnd);
14021405
if (!nonTsFile) {
14031406
if (options.allowNonTsExtensions) {
14041407
diagnostic = Diagnostics.File_0_not_found;
14051408
diagnosticArgument = [fileName];
14061409
}
1407-
else if (!forEach(supportedExtensions, extension => findSourceFile(fileName + extension, toPath(fileName + extension, currentDirectory, getCanonicalFileName), isDefaultLib, refFile, refPos, refEnd))) {
1410+
else if (!forEach(supportedExtensions, extension => findSourceFile(fileName + extension, toPath(fileName + extension, currentDirectory, getCanonicalFileName), isDefaultLib, isReference, refFile, refPos, refEnd))) {
14081411
diagnostic = Diagnostics.File_0_not_found;
14091412
fileName += ".ts";
14101413
diagnosticArgument = [fileName];
@@ -1433,7 +1436,7 @@ namespace ts {
14331436
}
14341437

14351438
// Get source file from normalized fileName
1436-
function findSourceFile(fileName: string, path: Path, isDefaultLib: boolean, refFile?: SourceFile, refPos?: number, refEnd?: number): SourceFile {
1439+
function findSourceFile(fileName: string, path: Path, isDefaultLib: boolean, isReference: boolean, refFile?: SourceFile, refPos?: number, refEnd?: number): SourceFile {
14371440
if (filesByName.contains(path)) {
14381441
const file = filesByName.get(path);
14391442
// try to check if we've already seen this file but with a different casing in path
@@ -1442,6 +1445,10 @@ namespace ts {
14421445
reportFileNamesDifferOnlyInCasingError(fileName, file.fileName, refFile, refPos, refEnd);
14431446
}
14441447

1448+
if (file) {
1449+
file.wasReferenced = file.wasReferenced || isReference;
1450+
}
1451+
14451452
return file;
14461453
}
14471454

@@ -1458,6 +1465,7 @@ namespace ts {
14581465

14591466
filesByName.set(path, file);
14601467
if (file) {
1468+
file.wasReferenced = file.wasReferenced || isReference;
14611469
file.path = path;
14621470

14631471
if (host.useCaseSensitiveFileNames()) {
@@ -1495,7 +1503,7 @@ namespace ts {
14951503
function processReferencedFiles(file: SourceFile, basePath: string) {
14961504
forEach(file.referencedFiles, ref => {
14971505
const referencedFileName = resolveTripleslashReference(ref.fileName, file.fileName);
1498-
processSourceFile(referencedFileName, /*isDefaultLib*/ false, file, ref.pos, ref.end);
1506+
processSourceFile(referencedFileName, /*isDefaultLib*/ false, /*isReference*/ true, file, ref.pos, ref.end);
14991507
});
15001508
}
15011509

@@ -1521,7 +1529,7 @@ namespace ts {
15211529
i < file.imports.length;
15221530

15231531
if (shouldAddFile) {
1524-
const importedFile = findSourceFile(resolution.resolvedFileName, toPath(resolution.resolvedFileName, currentDirectory, getCanonicalFileName), /*isDefaultLib*/ false, file, skipTrivia(file.text, file.imports[i].pos), file.imports[i].end);
1532+
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);
15251533

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

src/compiler/types.ts

+10
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,
@@ -1326,6 +1327,12 @@ namespace ts {
13261327
name: Identifier;
13271328
}
13281329

1330+
// @kind(SyntaxKind.GlobalModuleImport)
1331+
export interface GlobalModuleExportDeclaration extends DeclarationStatement {
1332+
name: Identifier;
1333+
moduleReference: LiteralLikeNode;
1334+
}
1335+
13291336
// @kind(SyntaxKind.ExportDeclaration)
13301337
export interface ExportDeclaration extends DeclarationStatement {
13311338
exportClause?: NamedExports;
@@ -1539,6 +1546,8 @@ namespace ts {
15391546
/* @internal */ externalModuleIndicator: Node;
15401547
// The first node that causes this file to be a CommonJS module
15411548
/* @internal */ commonJsModuleIndicator: Node;
1549+
// True if the file was a root file in a compilation or a /// reference targets
1550+
/* @internal */ wasReferenced?: boolean;
15421551

15431552
/* @internal */ identifiers: Map<string>;
15441553
/* @internal */ nodeCount: number;
@@ -1997,6 +2006,7 @@ namespace ts {
19972006

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

src/compiler/utilities.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1474,6 +1474,7 @@ namespace ts {
14741474
// export default ...
14751475
export function isAliasSymbolDeclaration(node: Node): boolean {
14761476
return node.kind === SyntaxKind.ImportEqualsDeclaration ||
1477+
node.kind === SyntaxKind.GlobalModuleExportDeclaration ||
14771478
node.kind === SyntaxKind.ImportClause && !!(<ImportClause>node).name ||
14781479
node.kind === SyntaxKind.NamespaceImport ||
14791480
node.kind === SyntaxKind.ImportSpecifier ||

src/harness/compilerRunner.ts

+1-1
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

+2-1
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>;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
//// [tests/cases/conformance/externalModules/umd-augmentation-1.ts] ////
2+
3+
//// [index.d.ts]
4+
5+
export as namespace Math2d;
6+
7+
export interface Point {
8+
x: number;
9+
y: number;
10+
}
11+
12+
export class Vector implements Point {
13+
x: number;
14+
y: number;
15+
constructor(x: number, y: number);
16+
17+
translate(dx: number, dy: number): Vector;
18+
}
19+
20+
export function getLength(p: Vector): number;
21+
22+
//// [math2d-augment.d.ts]
23+
import * as Math2d from 'math2d';
24+
// Augment the module
25+
declare module 'math2d' {
26+
// Add a method to the class
27+
interface Vector {
28+
reverse(): Math2d.Point;
29+
}
30+
}
31+
32+
//// [b.ts]
33+
/// <reference path="math2d-augment.d.ts" />
34+
import * as m from 'math2d';
35+
let v = new m.Vector(3, 2);
36+
let magnitude = m.getLength(v);
37+
let p: m.Point = v.translate(5, 5);
38+
p = v.reverse();
39+
var t = p.x;
40+
41+
42+
//// [b.js]
43+
"use strict";
44+
/// <reference path="math2d-augment.d.ts" />
45+
var m = require('math2d');
46+
var v = new m.Vector(3, 2);
47+
var magnitude = m.getLength(v);
48+
var p = v.translate(5, 5);
49+
p = v.reverse();
50+
var t = p.x;

0 commit comments

Comments
 (0)