From b6a57ea8afa8072a87a46ba3841ed82af1112004 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Thu, 1 Oct 2015 12:44:24 -0700 Subject: [PATCH] Concatenated module emit fixes up all included paths --- src/compiler/emitter.ts | 56 ++++++++++++++++++++++++++++++++------- src/compiler/program.ts | 6 +++++ src/compiler/utilities.ts | 32 ++++++++++++++++++++-- 3 files changed, 82 insertions(+), 12 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 60864e411fc..df2687768fc 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -448,7 +448,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi /** If removeComments is true, no leading-comments needed to be emitted **/ let emitLeadingCommentsOfPosition = compilerOptions.removeComments ? function (pos: number) { } : emitLeadingCommentsOfPositionWorker; - let moduleEmitDelegates: Map<(node: SourceFile, startIndex: number) => void> = { + let moduleEmitDelegates: Map<(node: SourceFile, startIndex: number, resolvePath?: boolean) => void> = { [ModuleKind.ES6]: emitES6Module, [ModuleKind.AMD]: emitAMDModule, [ModuleKind.System]: emitSystemModule, @@ -456,6 +456,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi [ModuleKind.CommonJS]: emitCommonJSModule, }; + let bundleEmitDelegates: Map<(node: SourceFile, startIndex: number, resolvePath?: boolean) => void> = { + [ModuleKind.ES6]: () => {}, + [ModuleKind.AMD]: emitAMDModule, + [ModuleKind.System]: emitSystemModule, + [ModuleKind.UMD]: emitUMDModule, + [ModuleKind.CommonJS]: () => {}, + }; + if (compilerOptions.sourceMap || compilerOptions.inlineSourceMap) { initializeEmitterWithSourceMaps(); } @@ -465,6 +473,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi emitSourceFile(root); } else { + forEach(host.getSourceFiles(), emitEmitHelpers); forEach(host.getSourceFiles(), sourceFile => { if (!isExternalModuleOrDeclarationFile(sourceFile)) { emitSourceFile(sourceFile); @@ -488,7 +497,16 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi function emitConcatenatedModule(sourceFile: SourceFile): void { currentSourceFile = sourceFile; exportFunctionForFile = undefined; - moduleEmitDelegates[modulekind](sourceFile, 0); + let canonicalName = resolveToSemiabsolutePath(sourceFile.fileName); + sourceFile.moduleName = sourceFile.moduleName || canonicalName; + bundleEmitDelegates[modulekind](sourceFile, 0, /*resolvePath*/true); + } + + function resolveToSemiabsolutePath(path: string): string { + let dir = host.getCurrentDirectory(); + return removeFileExtension( + getRelativePathToDirectoryOrUrl(dir, path, dir, f => host.getCanonicalFileName(f), /*isAbsolutePathAnUrl*/false) + ); } function isUniqueName(name: string): boolean { @@ -6620,7 +6638,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi write("}"); // execute } - function emitSystemModule(node: SourceFile, startIndex: number): void { + function emitSystemModule(node: SourceFile, startIndex: number, resolvePath?: boolean): void { collectExternalModuleInfo(node); // System modules has the following shape // System.register(['dep-1', ... 'dep-n'], function(exports) {/* module body function */}) @@ -6660,6 +6678,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi write(", "); } + if (resolvePath) { + text = makeModulePathSemiabsolute(text); + } write(text); } write(`], function(${exportFunctionForFile}) {`); @@ -6679,7 +6700,18 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi importAliasNames: string[]; } - function getAMDDependencyNames(node: SourceFile, includeNonAmdDependencies: boolean): AMDDependencyNames { + function makeModulePathSemiabsolute(externalModuleName: string): string { + let quotemark = externalModuleName.charAt(0); + let unquotedModuleName = externalModuleName.substring(1, externalModuleName.length - 1); + let resolvedFileName = host.resolveModuleName(unquotedModuleName, currentSourceFile.fileName); + if (resolvedFileName) { + let semiabsoluteName = resolveToSemiabsolutePath(resolvedFileName); + externalModuleName = quoteString(semiabsoluteName, quotemark); + } + return externalModuleName; + } + + function getAMDDependencyNames(node: SourceFile, includeNonAmdDependencies: boolean, resolvePath?: boolean): AMDDependencyNames { // names of modules with corresponding parameter in the factory function let aliasedModuleNames: string[] = []; // names of modules with no corresponding parameters in factory function @@ -6703,6 +6735,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi // Find the name of the external module let externalModuleName = getExternalModuleNameText(importNode); + if (resolvePath) { + externalModuleName = makeModulePathSemiabsolute(externalModuleName); + } + // Find the name of the module alias, if there is one let importAliasName = getLocalNameForExternalImport(importNode); if (includeNonAmdDependencies && importAliasName) { @@ -6717,7 +6753,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi return { aliasedModuleNames, unaliasedModuleNames, importAliasNames }; } - function emitAMDDependencies(node: SourceFile, includeNonAmdDependencies: boolean) { + function emitAMDDependencies(node: SourceFile, includeNonAmdDependencies: boolean, resolvePath?: boolean) { // An AMD define function has the following shape: // define(id?, dependencies?, factory); // @@ -6730,7 +6766,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi // `import "module"` or `` // we need to add modules without alias names to the end of the dependencies list - let dependencyNames = getAMDDependencyNames(node, includeNonAmdDependencies); + let dependencyNames = getAMDDependencyNames(node, includeNonAmdDependencies, resolvePath); emitAMDDependencyList(dependencyNames); write(", "); emitAMDFactoryHeader(dependencyNames); @@ -6758,7 +6794,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi write(") {"); } - function emitAMDModule(node: SourceFile, startIndex: number) { + function emitAMDModule(node: SourceFile, startIndex: number, resolvePath?: boolean) { emitEmitHelpers(node); collectExternalModuleInfo(node); @@ -6767,7 +6803,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi if (node.moduleName) { write("\"" + node.moduleName + "\", "); } - emitAMDDependencies(node, /*includeNonAmdDependencies*/ true); + emitAMDDependencies(node, /*includeNonAmdDependencies*/ true, resolvePath); increaseIndent(); emitExportStarHelper(); emitCaptureThisForNodeIfNecessary(node); @@ -6789,11 +6825,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi emitExportEquals(/*emitAsReturn*/ false); } - function emitUMDModule(node: SourceFile, startIndex: number) { + function emitUMDModule(node: SourceFile, startIndex: number, resolvePath?: boolean) { emitEmitHelpers(node); collectExternalModuleInfo(node); - let dependencyNames = getAMDDependencyNames(node, /*includeNonAmdDependencies*/ false); + let dependencyNames = getAMDDependencyNames(node, /*includeNonAmdDependencies*/ false, resolvePath); // Module is detected first to support Browserify users that load into a browser with an AMD loader writeLines(`(function (factory) { diff --git a/src/compiler/program.ts b/src/compiler/program.ts index c6d3a245a8d..b595d48dc57 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -522,6 +522,12 @@ namespace ts { getSourceFiles: program.getSourceFiles, writeFile: writeFileCallback || ( (fileName, data, writeByteOrderMark, onError) => host.writeFile(fileName, data, writeByteOrderMark, onError)), + resolveModuleName: (name: string, containingFile?: string) => { + let resolvedModule = resolveModuleNamesWorker([name], containingFile || "dummy.ts")[0]; + if (!resolvedModule) + return; + return resolvedModule.resolvedFileName; + }, }; } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 452f59f0f03..9ae65af046e 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -40,6 +40,7 @@ namespace ts { getNewLine(): string; writeFile: WriteFileCallback; + resolveModuleName(path: string, containingFile?: string): string; } // Pool writers to avoid needing to allocate them for every symbol we write. @@ -1619,21 +1620,48 @@ namespace ts { "\u0085": "\\u0085" // nextLine }; + let singleQuoteEscapedCharsRegExp = /[\\\'\u0000-\u001f\t\v\f\b\r\n\u2028\u2029\u0085]/g; + let singleQuoteEscapedCharsMap: Map = { + "\0": "\\0", + "\t": "\\t", + "\v": "\\v", + "\f": "\\f", + "\b": "\\b", + "\r": "\\r", + "\n": "\\n", + "\\": "\\\\", + "\'": "\\\'", + "\u2028": "\\u2028", // lineSeparator + "\u2029": "\\u2029", // paragraphSeparator + "\u0085": "\\u0085" // nextLine + }; + /** * Based heavily on the abstract 'Quote'/'QuoteJSONString' operation from ECMA-262 (24.3.2.2), * but augmented for a few select characters (e.g. lineSeparator, paragraphSeparator, nextLine) * Note that this doesn't actually wrap the input in double quotes. */ export function escapeString(s: string): string { - s = escapedCharsRegExp.test(s) ? s.replace(escapedCharsRegExp, getReplacement) : s; + return escapeStringByQuote(s, "\""); + } + + export function escapeStringByQuote(s: string, quotemark: string): string { + let regex = quotemark === "'" ? singleQuoteEscapedCharsRegExp : escapedCharsRegExp; + let replacementMap = quotemark === "'" ? singleQuoteEscapedCharsMap : escapedCharsMap; + + s = regex.test(s) ? s.replace(regex, getReplacement) : s; return s; function getReplacement(c: string) { - return escapedCharsMap[c] || get16BitUnicodeEscapeSequence(c.charCodeAt(0)); + return replacementMap[c] || get16BitUnicodeEscapeSequence(c.charCodeAt(0)); } } + export function quoteString(s: string, quotemark: string): string { + return quotemark + escapeStringByQuote(s, quotemark) + quotemark; + } + export function isIntrinsicJsxName(name: string) { let ch = name.substr(0, 1); return ch.toLowerCase() === ch;