Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UMD support #7264

Merged
merged 11 commits into from
Mar 10, 2016
Merged
29 changes: 29 additions & 0 deletions src/compiler/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1355,6 +1355,8 @@ namespace ts {
case SyntaxKind.ImportSpecifier:
case SyntaxKind.ExportSpecifier:
return declareSymbolAndAddToSymbolTable(<Declaration>node, SymbolFlags.Alias, SymbolFlags.AliasExcludes);
case SyntaxKind.GlobalModuleExportDeclaration:
return bindGlobalModuleExportDeclaration(<GlobalModuleExportDeclaration>node);
case SyntaxKind.ImportClause:
return bindImportClause(<ImportClause>node);
case SyntaxKind.ExportDeclaration:
Expand Down Expand Up @@ -1404,6 +1406,33 @@ namespace ts {
}
}

function bindGlobalModuleExportDeclaration(node: GlobalModuleExportDeclaration) {
if (node.modifiers && node.modifiers.length) {
file.bindDiagnostics.push(createDiagnosticForNode(node, Diagnostics.Modifiers_cannot_appear_here));
}

if (node.parent.kind !== SyntaxKind.SourceFile) {
file.bindDiagnostics.push(createDiagnosticForNode(node, Diagnostics.Global_module_exports_may_only_appear_at_top_level));
return;
}
else {
const parent = node.parent as SourceFile;

if (!isExternalModule(<SourceFile>node.parent)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use parent here

file.bindDiagnostics.push(createDiagnosticForNode(node, Diagnostics.Global_module_exports_may_only_appear_in_module_files));
return;
}

if (!parent.isDeclarationFile) {
file.bindDiagnostics.push(createDiagnosticForNode(node, Diagnostics.Global_module_exports_may_only_appear_in_declaration_files));
return;
}
}

file.symbol.globalExports = file.symbol.globalExports || {};
declareSymbol(file.symbol.globalExports, file.symbol, node, SymbolFlags.Alias, SymbolFlags.AliasExcludes);
}

function bindExportDeclaration(node: ExportDeclaration) {
if (!container.symbol || !container.symbol.exports) {
// Export * in some sort of block construct
Expand Down
10 changes: 9 additions & 1 deletion src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -981,6 +981,10 @@ namespace ts {
return getExternalModuleMember(<ImportDeclaration>node.parent.parent.parent, node);
}

function getTargetOfGlobalModuleExportDeclaration(node: GlobalModuleExportDeclaration): Symbol {
return resolveExternalModuleSymbol(node.parent.symbol);
}

function getTargetOfExportSpecifier(node: ExportSpecifier): Symbol {
return (<ExportDeclaration>node.parent.parent).moduleSpecifier ?
getExternalModuleMember(<ExportDeclaration>node.parent.parent, node) :
Expand All @@ -1005,6 +1009,8 @@ namespace ts {
return getTargetOfExportSpecifier(<ExportSpecifier>node);
case SyntaxKind.ExportAssignment:
return getTargetOfExportAssignment(<ExportAssignment>node);
case SyntaxKind.GlobalModuleExportDeclaration:
return getTargetOfGlobalModuleExportDeclaration(<GlobalModuleExportDeclaration>node);
}
}

Expand Down Expand Up @@ -15220,7 +15226,6 @@ namespace ts {
}
}


function checkSourceElement(node: Node): void {
if (!node) {
return;
Expand Down Expand Up @@ -16331,6 +16336,9 @@ namespace ts {
if (file.moduleAugmentations.length) {
(augmentations || (augmentations = [])).push(file.moduleAugmentations);
}
if (file.wasReferenced && file.symbol && file.symbol.globalExports) {
mergeSymbolTable(globals, file.symbol.globalExports);
}
});

if (augmentations) {
Expand Down
12 changes: 12 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -831,6 +831,18 @@
"category": "Error",
"code": 1313
},
"Global module exports may only appear in module files.": {
"category": "Error",
"code": 1314
},
"Global module exports may only appear in declaration files.": {
"category": "Error",
"code": 1315
},
"Global module exports may only appear at top level.": {
"category": "Error",
"code": 1316
},
"Duplicate identifier '{0}'.": {
"category": "Error",
"code": 2300
Expand Down
37 changes: 31 additions & 6 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,9 @@ namespace ts {
case SyntaxKind.ImportClause:
return visitNode(cbNode, (<ImportClause>node).name) ||
visitNode(cbNode, (<ImportClause>node).namedBindings);
case SyntaxKind.GlobalModuleExportDeclaration:
return visitNode(cbNode, (<GlobalModuleExportDeclaration>node).name);

case SyntaxKind.NamespaceImport:
return visitNode(cbNode, (<NamespaceImport>node).name);
case SyntaxKind.NamedImports:
Expand Down Expand Up @@ -1125,7 +1128,7 @@ namespace ts {
if (token === SyntaxKind.DefaultKeyword) {
return lookAhead(nextTokenIsClassOrFunction);
}
return token !== SyntaxKind.AsteriskToken && token !== SyntaxKind.OpenBraceToken && canFollowModifier();
return token !== SyntaxKind.AsteriskToken && token !== SyntaxKind.AsKeyword && token !== SyntaxKind.OpenBraceToken && canFollowModifier();
}
if (token === SyntaxKind.DefaultKeyword) {
return nextTokenIsClassOrFunction();
Expand Down Expand Up @@ -4400,7 +4403,8 @@ namespace ts {
continue;

case SyntaxKind.GlobalKeyword:
return nextToken() === SyntaxKind.OpenBraceToken;
nextToken();
return token === SyntaxKind.OpenBraceToken || token === SyntaxKind.Identifier || token === SyntaxKind.ExportKeyword;

case SyntaxKind.ImportKeyword:
nextToken();
Expand All @@ -4409,7 +4413,8 @@ namespace ts {
case SyntaxKind.ExportKeyword:
nextToken();
if (token === SyntaxKind.EqualsToken || token === SyntaxKind.AsteriskToken ||
token === SyntaxKind.OpenBraceToken || token === SyntaxKind.DefaultKeyword) {
token === SyntaxKind.OpenBraceToken || token === SyntaxKind.DefaultKeyword ||
token === SyntaxKind.AsKeyword) {
return true;
}
continue;
Expand Down Expand Up @@ -4593,9 +4598,15 @@ namespace ts {
return parseImportDeclarationOrImportEqualsDeclaration(fullStart, decorators, modifiers);
case SyntaxKind.ExportKeyword:
nextToken();
return token === SyntaxKind.DefaultKeyword || token === SyntaxKind.EqualsToken ?
parseExportAssignment(fullStart, decorators, modifiers) :
parseExportDeclaration(fullStart, decorators, modifiers);
switch (token) {
case SyntaxKind.DefaultKeyword:
case SyntaxKind.EqualsToken:
return parseExportAssignment(fullStart, decorators, modifiers);
case SyntaxKind.AsKeyword:
return parseGlobalModuleExportDeclaration(fullStart, decorators, modifiers);
default:
return parseExportDeclaration(fullStart, decorators, modifiers);
}
default:
if (decorators || modifiers) {
// We reached this point because we encountered decorators and/or modifiers and assumed a declaration
Expand Down Expand Up @@ -5264,6 +5275,20 @@ namespace ts {
return nextToken() === SyntaxKind.SlashToken;
}

function parseGlobalModuleExportDeclaration(fullStart: number, decorators: NodeArray<Decorator>, modifiers: ModifiersArray): GlobalModuleExportDeclaration {
const exportDeclaration = <GlobalModuleExportDeclaration>createNode(SyntaxKind.GlobalModuleExportDeclaration, fullStart);
exportDeclaration.decorators = decorators;
exportDeclaration.modifiers = modifiers;
parseExpected(SyntaxKind.AsKeyword);
parseExpected(SyntaxKind.NamespaceKeyword);

exportDeclaration.name = parseIdentifier();

parseExpected(SyntaxKind.SemicolonToken);

return finishNode(exportDeclaration);
}

function parseImportDeclarationOrImportEqualsDeclaration(fullStart: number, decorators: NodeArray<Decorator>, modifiers: ModifiersArray): ImportEqualsDeclaration | ImportDeclaration {
parseExpected(SyntaxKind.ImportKeyword);
const afterImportPos = scanner.getStartPos();
Expand Down
24 changes: 16 additions & 8 deletions src/compiler/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1282,7 +1282,7 @@ namespace ts {
}

function processRootFile(fileName: string, isDefaultLib: boolean) {
processSourceFile(normalizePath(fileName), isDefaultLib);
processSourceFile(normalizePath(fileName), isDefaultLib, /*isReference*/ true);
}

function fileReferenceIsEqualTo(a: FileReference, b: FileReference): boolean {
Expand Down Expand Up @@ -1376,15 +1376,18 @@ namespace ts {
}
}

function processSourceFile(fileName: string, isDefaultLib: boolean, refFile?: SourceFile, refPos?: number, refEnd?: number) {
/**
* 'isReference' indicates whether the file was brought in via a reference directive (rather than an import declaration)
*/
function processSourceFile(fileName: string, isDefaultLib: boolean, isReference: boolean, refFile?: SourceFile, refPos?: number, refEnd?: number) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A little bit of documentation on this would be helpful. From a cursory glance, it doesn't seem obvious that this is differentiated from an import.

Alternatively, make an enum.

let diagnosticArgument: string[];
let diagnostic: DiagnosticMessage;
if (hasExtension(fileName)) {
if (!options.allowNonTsExtensions && !forEach(supportedExtensions, extension => fileExtensionIs(host.getCanonicalFileName(fileName), extension))) {
diagnostic = Diagnostics.File_0_has_unsupported_extension_The_only_supported_extensions_are_1;
diagnosticArgument = [fileName, "'" + supportedExtensions.join("', '") + "'"];
}
else if (!findSourceFile(fileName, toPath(fileName, currentDirectory, getCanonicalFileName), isDefaultLib, refFile, refPos, refEnd)) {
else if (!findSourceFile(fileName, toPath(fileName, currentDirectory, getCanonicalFileName), isDefaultLib, isReference, refFile, refPos, refEnd)) {
diagnostic = Diagnostics.File_0_not_found;
diagnosticArgument = [fileName];
}
Expand All @@ -1394,13 +1397,13 @@ namespace ts {
}
}
else {
const nonTsFile: SourceFile = options.allowNonTsExtensions && findSourceFile(fileName, toPath(fileName, currentDirectory, getCanonicalFileName), isDefaultLib, refFile, refPos, refEnd);
const nonTsFile: SourceFile = options.allowNonTsExtensions && findSourceFile(fileName, toPath(fileName, currentDirectory, getCanonicalFileName), isDefaultLib, isReference, refFile, refPos, refEnd);
if (!nonTsFile) {
if (options.allowNonTsExtensions) {
diagnostic = Diagnostics.File_0_not_found;
diagnosticArgument = [fileName];
}
else if (!forEach(supportedExtensions, extension => findSourceFile(fileName + extension, toPath(fileName + extension, currentDirectory, getCanonicalFileName), isDefaultLib, refFile, refPos, refEnd))) {
else if (!forEach(supportedExtensions, extension => findSourceFile(fileName + extension, toPath(fileName + extension, currentDirectory, getCanonicalFileName), isDefaultLib, isReference, refFile, refPos, refEnd))) {
diagnostic = Diagnostics.File_0_not_found;
fileName += ".ts";
diagnosticArgument = [fileName];
Expand Down Expand Up @@ -1429,7 +1432,7 @@ namespace ts {
}

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

if (file) {
file.wasReferenced = file.wasReferenced || isReference;
}

return file;
}

Expand All @@ -1454,6 +1461,7 @@ namespace ts {

filesByName.set(path, file);
if (file) {
file.wasReferenced = file.wasReferenced || isReference;
file.path = path;

if (host.useCaseSensitiveFileNames()) {
Expand Down Expand Up @@ -1491,7 +1499,7 @@ namespace ts {
function processReferencedFiles(file: SourceFile, basePath: string) {
forEach(file.referencedFiles, ref => {
const referencedFileName = resolveTripleslashReference(ref.fileName, file.fileName);
processSourceFile(referencedFileName, /*isDefaultLib*/ false, file, ref.pos, ref.end);
processSourceFile(referencedFileName, /*isDefaultLib*/ false, /*isReference*/ true, file, ref.pos, ref.end);
});
}

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

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

if (importedFile && resolution.isExternalLibraryImport) {
// Since currently irrespective of allowJs, we only look for supportedTypeScript extension external module files,
Expand Down
10 changes: 10 additions & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ namespace ts {
ModuleDeclaration,
ModuleBlock,
CaseBlock,
GlobalModuleExportDeclaration,
ImportEqualsDeclaration,
ImportDeclaration,
ImportClause,
Expand Down Expand Up @@ -1324,6 +1325,12 @@ namespace ts {
name: Identifier;
}

// @kind(SyntaxKind.GlobalModuleImport)
export interface GlobalModuleExportDeclaration extends DeclarationStatement {
name: Identifier;
moduleReference: LiteralLikeNode;
}

// @kind(SyntaxKind.ExportDeclaration)
export interface ExportDeclaration extends DeclarationStatement {
exportClause?: NamedExports;
Expand Down Expand Up @@ -1537,6 +1544,8 @@ namespace ts {
/* @internal */ externalModuleIndicator: Node;
// The first node that causes this file to be a CommonJS module
/* @internal */ commonJsModuleIndicator: Node;
// True if the file was a root file in a compilation or a /// reference targets
/* @internal */ wasReferenced?: boolean;

/* @internal */ identifiers: Map<string>;
/* @internal */ nodeCount: number;
Expand Down Expand Up @@ -1995,6 +2004,7 @@ namespace ts {

members?: SymbolTable; // Class, interface or literal instance members
exports?: SymbolTable; // Module exports
globalExports?: SymbolTable; // Conditional global UMD exports
/* @internal */ id?: number; // Unique id (used to look up SymbolLinks)
/* @internal */ mergeId?: number; // Merge id (used to look up merged symbol)
/* @internal */ parent?: Symbol; // Parent symbol
Expand Down
1 change: 1 addition & 0 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1471,6 +1471,7 @@ namespace ts {
// export default ...
export function isAliasSymbolDeclaration(node: Node): boolean {
return node.kind === SyntaxKind.ImportEqualsDeclaration ||
node.kind === SyntaxKind.GlobalModuleExportDeclaration ||
node.kind === SyntaxKind.ImportClause && !!(<ImportClause>node).name ||
node.kind === SyntaxKind.NamespaceImport ||
node.kind === SyntaxKind.ImportSpecifier ||
Expand Down
2 changes: 1 addition & 1 deletion src/harness/compilerRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ class CompilerBaselineRunner extends RunnerBase {
toBeCompiled = [];
otherFiles = [];

if (/require\(/.test(lastUnit.content) || /reference\spath/.test(lastUnit.content)) {
if (testCaseContent.settings["noImplicitReferences"] || /require\(/.test(lastUnit.content) || /reference\spath/.test(lastUnit.content)) {
toBeCompiled.push({ unitName: this.makeUnitName(lastUnit.name, rootDir), content: lastUnit.content });
units.forEach(unit => {
if (unit.name !== lastUnit.name) {
Expand Down
3 changes: 2 additions & 1 deletion src/harness/harness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -896,7 +896,8 @@ namespace Harness {
{ name: "fileName", type: "string" },
{ name: "libFiles", type: "string" },
{ name: "noErrorTruncation", type: "boolean" },
{ name: "suppressOutputPathCheck", type: "boolean" }
{ name: "suppressOutputPathCheck", type: "boolean" },
{ name: "noImplicitReferences", type: "boolean" }
];

let optionsIndex: ts.Map<ts.CommandLineOption>;
Expand Down
50 changes: 50 additions & 0 deletions tests/baselines/reference/umd-augmentation-1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//// [tests/cases/conformance/externalModules/umd-augmentation-1.ts] ////

//// [index.d.ts]

export as namespace Math2d;

export interface Point {
x: number;
y: number;
}

export class Vector implements Point {
x: number;
y: number;
constructor(x: number, y: number);

translate(dx: number, dy: number): Vector;
}

export function getLength(p: Vector): number;

//// [math2d-augment.d.ts]
import * as Math2d from 'math2d';
// Augment the module
declare module 'math2d' {
// Add a method to the class
interface Vector {
reverse(): Math2d.Point;
}
}

//// [b.ts]
/// <reference path="math2d-augment.d.ts" />
import * as m from 'math2d';
let v = new m.Vector(3, 2);
let magnitude = m.getLength(v);
let p: m.Point = v.translate(5, 5);
p = v.reverse();
var t = p.x;


//// [b.js]
"use strict";
/// <reference path="math2d-augment.d.ts" />
var m = require('math2d');
var v = new m.Vector(3, 2);
var magnitude = m.getLength(v);
var p = v.translate(5, 5);
p = v.reverse();
var t = p.x;
Loading