From 8886cefe58c4674589f8f96067effcbb3dc77aa8 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Fri, 20 Jan 2017 08:12:00 -0800 Subject: [PATCH] Clean up code for getting emitted files --- src/compiler/emitter.ts | 4 +- src/compiler/program.ts | 2 +- src/compiler/utilities.ts | 186 +++++------------- .../reference/noBundledEmitFromNodeModules.js | 20 ++ .../noBundledEmitFromNodeModules.symbols | 9 + .../noBundledEmitFromNodeModules.types | 9 + .../compiler/noBundledEmitFromNodeModules.ts | 10 + 7 files changed, 98 insertions(+), 142 deletions(-) create mode 100644 tests/baselines/reference/noBundledEmitFromNodeModules.js create mode 100644 tests/baselines/reference/noBundledEmitFromNodeModules.symbols create mode 100644 tests/baselines/reference/noBundledEmitFromNodeModules.types create mode 100644 tests/cases/compiler/noBundledEmitFromNodeModules.ts diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 2ff342138c8..1c4662fa587 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -73,7 +73,7 @@ namespace ts { // Emit each output file performance.mark("beforePrint"); - forEachTransformedEmitFile(host, transformed, emitFile, emitOnlyDtsFiles); + forEachEmittedFile(host, transformed, emitFile, emitOnlyDtsFiles); performance.measure("printTime", "beforePrint"); // Clean up emit nodes on parse tree @@ -88,7 +88,7 @@ namespace ts { sourceMaps: sourceMapDataList }; - function emitFile(jsFilePath: string, sourceMapFilePath: string, declarationFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean) { + function emitFile({ jsFilePath, sourceMapFilePath, declarationFilePath }: EmitFileNames, sourceFiles: SourceFile[], isBundledEmit: boolean) { // Make sure not to write js file and source map file if any of them cannot be written if (!host.isEmitBlocked(jsFilePath) && !compilerOptions.noEmit) { if (!emitOnlyDtsFiles) { diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 324a21f229e..851e206d448 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -439,7 +439,7 @@ namespace ts { function getCommonSourceDirectory() { if (commonSourceDirectory === undefined) { - const emittedFiles = filterSourceFilesInDirectory(files, isSourceFileFromExternalLibrary); + const emittedFiles = filter(files, file => sourceFileMayBeEmitted(file, options, isSourceFileFromExternalLibrary)); if (options.rootDir && checkSourceFilesBelongToPath(emittedFiles, options.rootDir)) { // If a rootDir is specified and is valid use it as the commonSourceDirectory commonSourceDirectory = getNormalizedAbsolutePath(options.rootDir, currentDirectory); diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 932e75d0345..f59d384e490 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2568,108 +2568,25 @@ namespace ts { * @param host An EmitHost. * @param targetSourceFile An optional target source file to emit. */ - export function getSourceFilesToEmit(host: EmitHost, targetSourceFile?: SourceFile) { + export function getSourceFilesToEmit(host: EmitHost, targetSourceFile?: SourceFile): SourceFile[] { const options = host.getCompilerOptions(); + const isSourceFileFromExternalLibrary = (file: SourceFile) => host.isSourceFileFromExternalLibrary(file); if (options.outFile || options.out) { const moduleKind = getEmitModuleKind(options); const moduleEmitEnabled = moduleKind === ModuleKind.AMD || moduleKind === ModuleKind.System; - const sourceFiles = getAllEmittableSourceFiles(); // Can emit only sources that are not declaration file and are either non module code or module with --module or --target es6 specified - return filter(sourceFiles, moduleEmitEnabled ? isNonDeclarationFile : isBundleEmitNonExternalModule); + return filter(host.getSourceFiles(), sourceFile => + (moduleEmitEnabled || !isExternalModule(sourceFile)) && sourceFileMayBeEmitted(sourceFile, options, isSourceFileFromExternalLibrary)); } else { - const sourceFiles = targetSourceFile === undefined ? getAllEmittableSourceFiles() : [targetSourceFile]; - return filterSourceFilesInDirectory(sourceFiles, file => host.isSourceFileFromExternalLibrary(file)); - } - - function getAllEmittableSourceFiles() { - return options.noEmitForJsFiles ? filter(host.getSourceFiles(), sourceFile => !isSourceFileJavaScript(sourceFile)) : host.getSourceFiles(); + const sourceFiles = targetSourceFile === undefined ? host.getSourceFiles() : [targetSourceFile]; + return filter(sourceFiles, sourceFile => sourceFileMayBeEmitted(sourceFile, options, isSourceFileFromExternalLibrary)); } } - /** Don't call this for `--outFile`, just for `--outDir` or plain emit. */ - export function filterSourceFilesInDirectory(sourceFiles: SourceFile[], isSourceFileFromExternalLibrary: (file: SourceFile) => boolean): SourceFile[] { - return filter(sourceFiles, file => shouldEmitInDirectory(file, isSourceFileFromExternalLibrary)); - } - - function isNonDeclarationFile(sourceFile: SourceFile) { - return !isDeclarationFile(sourceFile); - } - - /** - * Whether a file should be emitted in a non-`--outFile` case. - * Don't emit if source file is a declaration file, or was located under node_modules - */ - function shouldEmitInDirectory(sourceFile: SourceFile, isSourceFileFromExternalLibrary: (file: SourceFile) => boolean): boolean { - return isNonDeclarationFile(sourceFile) && !isSourceFileFromExternalLibrary(sourceFile); - } - - function isBundleEmitNonExternalModule(sourceFile: SourceFile) { - return isNonDeclarationFile(sourceFile) && !isExternalModule(sourceFile); - } - - /** - * Iterates over each source file to emit. The source files are expected to have been - * transformed for use by the pretty printer. - * - * Originally part of `forEachExpectedEmitFile`, this functionality was extracted to support - * transformations. - * - * @param host An EmitHost. - * @param sourceFiles The transformed source files to emit. - * @param action The action to execute. - */ - export function forEachTransformedEmitFile(host: EmitHost, sourceFiles: SourceFile[], - action: (jsFilePath: string, sourceMapFilePath: string, declarationFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean) => void, - emitOnlyDtsFiles?: boolean) { - const options = host.getCompilerOptions(); - // Emit on each source file - if (options.outFile || options.out) { - onBundledEmit(sourceFiles); - } - else { - for (const sourceFile of sourceFiles) { - // Don't emit if source file is a declaration file, or was located under node_modules - if (!isDeclarationFile(sourceFile) && !host.isSourceFileFromExternalLibrary(sourceFile)) { - onSingleFileEmit(host, sourceFile); - } - } - } - - function onSingleFileEmit(host: EmitHost, sourceFile: SourceFile) { - // JavaScript files are always LanguageVariant.JSX, as JSX syntax is allowed in .js files also. - // So for JavaScript files, '.jsx' is only emitted if the input was '.jsx', and JsxEmit.Preserve. - // For TypeScript, the only time to emit with a '.jsx' extension, is on JSX input, and JsxEmit.Preserve - let extension = ".js"; - if (options.jsx === JsxEmit.Preserve) { - if (isSourceFileJavaScript(sourceFile)) { - if (fileExtensionIs(sourceFile.fileName, ".jsx")) { - extension = ".jsx"; - } - } - else if (sourceFile.languageVariant === LanguageVariant.JSX) { - // TypeScript source file preserving JSX syntax - extension = ".jsx"; - } - } - const jsFilePath = getOwnEmitOutputFilePath(sourceFile, host, extension); - const sourceMapFilePath = getSourceMapFilePath(jsFilePath, options); - const declarationFilePath = !isSourceFileJavaScript(sourceFile) && (options.declaration || emitOnlyDtsFiles) ? getDeclarationEmitOutputFilePath(sourceFile, host) : undefined; - action(jsFilePath, sourceMapFilePath, declarationFilePath, [sourceFile], /*isBundledEmit*/ false); - } - - function onBundledEmit(sourceFiles: SourceFile[]) { - if (sourceFiles.length) { - const jsFilePath = options.outFile || options.out; - const sourceMapFilePath = getSourceMapFilePath(jsFilePath, options); - const declarationFilePath = options.declaration ? removeFileExtension(jsFilePath) + ".d.ts" : undefined; - action(jsFilePath, sourceMapFilePath, declarationFilePath, sourceFiles, /*isBundledEmit*/ true); - } - } - } - - function getSourceMapFilePath(jsFilePath: string, options: CompilerOptions) { - return options.sourceMap ? jsFilePath + ".map" : undefined; + /** Don't call this for `--outFile`, just for `--outDir` or plain emit. `--outFile` needs additional checks. */ + export function sourceFileMayBeEmitted(sourceFile: SourceFile, options: CompilerOptions, isSourceFileFromExternalLibrary: (file: SourceFile) => boolean) { + return !(options.noEmitForJsFiles && isSourceFileJavaScript(sourceFile)) && !isDeclarationFile(sourceFile) && !isSourceFileFromExternalLibrary(sourceFile); } /** @@ -2685,64 +2602,55 @@ namespace ts { action: (emitFileNames: EmitFileNames, sourceFiles: SourceFile[], isBundledEmit: boolean, emitOnlyDtsFiles: boolean) => void, targetSourceFile?: SourceFile, emitOnlyDtsFiles?: boolean) { + forEachEmittedFile(host, getSourceFilesToEmit(host, targetSourceFile), action, emitOnlyDtsFiles); + } + + /** + * Iterates over each source file to emit. + */ + export function forEachEmittedFile(host: EmitHost, sourceFiles: SourceFile[], + action: (emitFileNames: EmitFileNames, sourceFiles: SourceFile[], isBundledEmit: boolean, emitOnlyDtsFiles: boolean) => void, + emitOnlyDtsFiles?: boolean) { const options = host.getCompilerOptions(); - // Emit on each source file if (options.outFile || options.out) { - onBundledEmit(host); + if (sourceFiles.length) { + const jsFilePath = options.outFile || options.out; + const sourceMapFilePath = getSourceMapFilePath(jsFilePath, options); + const declarationFilePath = options.declaration ? removeFileExtension(jsFilePath) + ".d.ts" : undefined; + action({ jsFilePath, sourceMapFilePath, declarationFilePath }, sourceFiles, /*isBundledEmit*/true, emitOnlyDtsFiles); + } } else { - const sourceFiles = targetSourceFile === undefined ? getSourceFilesToEmit(host) : [targetSourceFile]; for (const sourceFile of sourceFiles) { - if (shouldEmitInDirectory(sourceFile, file => host.isSourceFileFromExternalLibrary(file))) { - onSingleFileEmit(host, sourceFile); - } + const options = host.getCompilerOptions(); + const jsFilePath = getOwnEmitOutputFilePath(sourceFile, host, getOutputExtension(sourceFile, options)); + const sourceMapFilePath = getSourceMapFilePath(jsFilePath, options); + const declarationFilePath = !isSourceFileJavaScript(sourceFile) && (emitOnlyDtsFiles || options.declaration) ? getDeclarationEmitOutputFilePath(sourceFile, host) : undefined; + action({ jsFilePath, sourceMapFilePath, declarationFilePath }, [sourceFile], /*isBundledEmit*/false, emitOnlyDtsFiles); } } + } - function onSingleFileEmit(host: EmitHost, sourceFile: SourceFile) { - // JavaScript files are always LanguageVariant.JSX, as JSX syntax is allowed in .js files also. - // So for JavaScript files, '.jsx' is only emitted if the input was '.jsx', and JsxEmit.Preserve. - // For TypeScript, the only time to emit with a '.jsx' extension, is on JSX input, and JsxEmit.Preserve - let extension = ".js"; - if (options.jsx === JsxEmit.Preserve) { - if (isSourceFileJavaScript(sourceFile)) { - if (fileExtensionIs(sourceFile.fileName, ".jsx")) { - extension = ".jsx"; - } - } - else if (sourceFile.languageVariant === LanguageVariant.JSX) { - // TypeScript source file preserving JSX syntax - extension = ".jsx"; - } - } - const jsFilePath = getOwnEmitOutputFilePath(sourceFile, host, extension); - const declarationFilePath = !isSourceFileJavaScript(sourceFile) && (emitOnlyDtsFiles || options.declaration) ? getDeclarationEmitOutputFilePath(sourceFile, host) : undefined; - const emitFileNames: EmitFileNames = { - jsFilePath, - sourceMapFilePath: getSourceMapFilePath(jsFilePath, options), - declarationFilePath - }; - action(emitFileNames, [sourceFile], /*isBundledEmit*/false, emitOnlyDtsFiles); - } + function getSourceMapFilePath(jsFilePath: string, options: CompilerOptions) { + return options.sourceMap ? jsFilePath + ".map" : undefined; + } - function onBundledEmit(host: EmitHost) { - // Can emit only sources that are not declaration file and are either non module code or module with - // --module or --target es6 specified. Files included by searching under node_modules are also not emitted. - const bundledSources = filter(getSourceFilesToEmit(host), - sourceFile => !isDeclarationFile(sourceFile) && - !host.isSourceFileFromExternalLibrary(sourceFile) && - (!isExternalModule(sourceFile) || - !!getEmitModuleKind(options))); - if (bundledSources.length) { - const jsFilePath = options.outFile || options.out; - const emitFileNames: EmitFileNames = { - jsFilePath, - sourceMapFilePath: getSourceMapFilePath(jsFilePath, options), - declarationFilePath: options.declaration ? removeFileExtension(jsFilePath) + ".d.ts" : undefined - }; - action(emitFileNames, bundledSources, /*isBundledEmit*/true, emitOnlyDtsFiles); + // JavaScript files are always LanguageVariant.JSX, as JSX syntax is allowed in .js files also. + // So for JavaScript files, '.jsx' is only emitted if the input was '.jsx', and JsxEmit.Preserve. + // For TypeScript, the only time to emit with a '.jsx' extension, is on JSX input, and JsxEmit.Preserve + function getOutputExtension(sourceFile: SourceFile, options: CompilerOptions): string { + if (options.jsx === JsxEmit.Preserve) { + if (isSourceFileJavaScript(sourceFile)) { + if (fileExtensionIs(sourceFile.fileName, ".jsx")) { + return ".jsx"; + } + } + else if (sourceFile.languageVariant === LanguageVariant.JSX) { + // TypeScript source file preserving JSX syntax + return ".jsx"; } } + return ".js"; } export function getSourceFilePathInNewDir(sourceFile: SourceFile, host: EmitHost, newDirPath: string) { diff --git a/tests/baselines/reference/noBundledEmitFromNodeModules.js b/tests/baselines/reference/noBundledEmitFromNodeModules.js new file mode 100644 index 00000000000..67216a408af --- /dev/null +++ b/tests/baselines/reference/noBundledEmitFromNodeModules.js @@ -0,0 +1,20 @@ +//// [tests/cases/compiler/noBundledEmitFromNodeModules.ts] //// + +//// [index.ts] + +export class C {} + +//// [a.ts] +import { C } from "projB"; + + +//// [out.js] +System.register("a", [], function (exports_1, context_1) { + "use strict"; + var __moduleName = context_1 && context_1.id; + return { + setters: [], + execute: function () { + } + }; +}); diff --git a/tests/baselines/reference/noBundledEmitFromNodeModules.symbols b/tests/baselines/reference/noBundledEmitFromNodeModules.symbols new file mode 100644 index 00000000000..a1f6c67e4b4 --- /dev/null +++ b/tests/baselines/reference/noBundledEmitFromNodeModules.symbols @@ -0,0 +1,9 @@ +=== /a.ts === +import { C } from "projB"; +>C : Symbol(C, Decl(a.ts, 0, 8)) + +=== /node_modules/projB/index.ts === + +export class C {} +>C : Symbol(C, Decl(index.ts, 0, 0)) + diff --git a/tests/baselines/reference/noBundledEmitFromNodeModules.types b/tests/baselines/reference/noBundledEmitFromNodeModules.types new file mode 100644 index 00000000000..a11bad24aaf --- /dev/null +++ b/tests/baselines/reference/noBundledEmitFromNodeModules.types @@ -0,0 +1,9 @@ +=== /a.ts === +import { C } from "projB"; +>C : typeof C + +=== /node_modules/projB/index.ts === + +export class C {} +>C : C + diff --git a/tests/cases/compiler/noBundledEmitFromNodeModules.ts b/tests/cases/compiler/noBundledEmitFromNodeModules.ts new file mode 100644 index 00000000000..66c71a58489 --- /dev/null +++ b/tests/cases/compiler/noBundledEmitFromNodeModules.ts @@ -0,0 +1,10 @@ +// @outFile: out.js +// @module: system +// @moduleResolution: node +// @noImplicitReferences: true + +// @fileName: /node_modules/projB/index.ts +export class C {} + +// @fileName: /a.ts +import { C } from "projB";