Skip to content

Commit

Permalink
Prototype UMD support
Browse files Browse the repository at this point in the history
  • Loading branch information
RyanCavanaugh committed Feb 18, 2016
1 parent 8a050ea commit b3a91d6
Show file tree
Hide file tree
Showing 26 changed files with 503 additions and 11 deletions.
7 changes: 7 additions & 0 deletions src/compiler/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1345,6 +1345,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 @@ -1394,6 +1396,11 @@ namespace ts {
}
}

function bindGlobalModuleExportDeclaration(node: GlobalModuleExportDeclaration) {
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
9 changes: 9 additions & 0 deletions 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 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 @@ -16185,6 +16191,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
31 changes: 30 additions & 1 deletion 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 @@ -4394,7 +4397,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 Down Expand Up @@ -4580,6 +4584,12 @@ namespace ts {
case SyntaxKind.EnumKeyword:
return parseEnumDeclaration(fullStart, decorators, modifiers);
case SyntaxKind.GlobalKeyword:
if (lookAhead(isGlobalModuleExportDeclaration)) {
return parseGlobalModuleExportDeclaration(fullStart, decorators, modifiers);
}
else {
return parseModuleDeclaration(fullStart, decorators, modifiers);
}
case SyntaxKind.ModuleKeyword:
case SyntaxKind.NamespaceKeyword:
return parseModuleDeclaration(fullStart, decorators, modifiers);
Expand All @@ -4603,6 +4613,11 @@ namespace ts {
}
}

function isGlobalModuleExportDeclaration() {
nextToken();
return token === SyntaxKind.ExportKeyword;
}

function nextTokenIsIdentifierOrStringLiteralOnSameLine() {
nextToken();
return !scanner.hasPrecedingLineBreak() && (isIdentifier() || token === SyntaxKind.StringLiteral);
Expand Down Expand Up @@ -5258,6 +5273,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.GlobalKeyword);
parseExpected(SyntaxKind.ExportKeyword);

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
21 changes: 13 additions & 8 deletions src/compiler/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1273,7 +1273,7 @@ namespace ts {
}

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

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

function processSourceFile(fileName: string, isDefaultLib: boolean, refFile?: SourceFile, refPos?: number, refEnd?: number) {
function processSourceFile(fileName: string, isDefaultLib: boolean, isReference: boolean, refFile?: SourceFile, refPos?: number, refEnd?: number) {
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 @@ -1385,13 +1385,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 @@ -1420,7 +1420,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 @@ -1429,6 +1429,10 @@ namespace ts {
reportFileNamesDifferOnlyInCasingError(fileName, file.fileName, refFile, refPos, refEnd);
}

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

return file;
}

Expand All @@ -1445,6 +1449,7 @@ namespace ts {

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

if (host.useCaseSensitiveFileNames()) {
Expand Down Expand Up @@ -1482,7 +1487,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 @@ -1508,7 +1513,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 @@ -273,6 +273,7 @@ namespace ts {
ModuleDeclaration,
ModuleBlock,
CaseBlock,
GlobalModuleExportDeclaration,
ImportEqualsDeclaration,
ImportDeclaration,
ImportClause,
Expand Down Expand Up @@ -1323,6 +1324,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 @@ -1535,6 +1542,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
wasReferenced?: boolean;

/* @internal */ identifiers: Map<string>;
/* @internal */ nodeCount: number;
Expand Down Expand Up @@ -1991,6 +2000,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 @@ -1475,6 +1475,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
21 changes: 21 additions & 0 deletions tests/baselines/reference/umd1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//// [tests/cases/conformance/externalModules/umd1.ts] ////

//// [foo.d.ts]

export var x: number;
export function fn(): void;
export interface Thing { n: typeof x }
declare global export Foo;

//// [a.ts]
/// <reference path="foo.d.ts" />
Foo.fn();
let x: Foo.Thing;
let y: number = x.n;


//// [a.js]
/// <reference path="foo.d.ts" />
exports.Foo.fn();
var x;
var y = x.n;
33 changes: 33 additions & 0 deletions tests/baselines/reference/umd1.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
=== tests/cases/conformance/externalModules/a.ts ===
/// <reference path="foo.d.ts" />
Foo.fn();
>Foo.fn : Symbol(Foo.fn, Decl(foo.d.ts, 1, 21))
>Foo : Symbol(Foo, Decl(foo.d.ts, 3, 38))
>fn : Symbol(Foo.fn, Decl(foo.d.ts, 1, 21))

let x: Foo.Thing;
>x : Symbol(x, Decl(a.ts, 2, 3))
>Foo : Symbol(Foo, Decl(foo.d.ts, 3, 38))
>Thing : Symbol(Foo.Thing, Decl(foo.d.ts, 2, 27))

let y: number = x.n;
>y : Symbol(y, Decl(a.ts, 3, 3))
>x.n : Symbol(Foo.Thing.n, Decl(foo.d.ts, 3, 24))
>x : Symbol(x, Decl(a.ts, 2, 3))
>n : Symbol(Foo.Thing.n, Decl(foo.d.ts, 3, 24))

=== tests/cases/conformance/externalModules/foo.d.ts ===

export var x: number;
>x : Symbol(x, Decl(foo.d.ts, 1, 10))

export function fn(): void;
>fn : Symbol(fn, Decl(foo.d.ts, 1, 21))

export interface Thing { n: typeof x }
>Thing : Symbol(Thing, Decl(foo.d.ts, 2, 27))
>n : Symbol(n, Decl(foo.d.ts, 3, 24))
>x : Symbol(x, Decl(foo.d.ts, 1, 10))

declare global export Foo;

35 changes: 35 additions & 0 deletions tests/baselines/reference/umd1.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
=== tests/cases/conformance/externalModules/a.ts ===
/// <reference path="foo.d.ts" />
Foo.fn();
>Foo.fn() : void
>Foo.fn : () => void
>Foo : typeof Foo
>fn : () => void

let x: Foo.Thing;
>x : Foo.Thing
>Foo : any
>Thing : Foo.Thing

let y: number = x.n;
>y : number
>x.n : number
>x : Foo.Thing
>n : number

=== tests/cases/conformance/externalModules/foo.d.ts ===

export var x: number;
>x : number

export function fn(): void;
>fn : () => void

export interface Thing { n: typeof x }
>Thing : Thing
>n : number
>x : number

declare global export Foo;
>Foo : any

19 changes: 19 additions & 0 deletions tests/baselines/reference/umd2.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
tests/cases/conformance/externalModules/a.ts(1,1): error TS2304: Cannot find name 'Foo'.
tests/cases/conformance/externalModules/a.ts(2,8): error TS2503: Cannot find namespace 'Foo'.


==== tests/cases/conformance/externalModules/a.ts (2 errors) ====
Foo.fn();
~~~
!!! error TS2304: Cannot find name 'Foo'.
let x: Foo.Thing;
~~~
!!! error TS2503: Cannot find namespace 'Foo'.
let y: number = x.n;

==== tests/cases/conformance/externalModules/foo.d.ts (0 errors) ====

export var x: number;
export function fn(): void;
declare global export Foo;

Loading

0 comments on commit b3a91d6

Please sign in to comment.