Skip to content

Commit 6a192ce

Browse files
author
Andy
authored
Merge pull request microsoft#13598 from Microsoft/node_modules_bundled_emit
Clean up code for getting emitted files
2 parents 3cf326a + a32914f commit 6a192ce

File tree

8 files changed

+93
-145
lines changed

8 files changed

+93
-145
lines changed

src/compiler/declarationEmitter.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ namespace ts {
3232

3333
export function getDeclarationDiagnostics(host: EmitHost, resolver: EmitResolver, targetSourceFile: SourceFile): Diagnostic[] {
3434
const declarationDiagnostics = createDiagnosticCollection();
35-
forEachExpectedEmitFile(host, getDeclarationDiagnosticsFromFile, targetSourceFile);
35+
forEachEmittedFile(host, getDeclarationDiagnosticsFromFile, targetSourceFile);
3636
return declarationDiagnostics.getDiagnostics(targetSourceFile ? targetSourceFile.fileName : undefined);
3737

3838
function getDeclarationDiagnosticsFromFile({ declarationFilePath }: EmitFileNames, sources: SourceFile[], isBundledEmit: boolean) {
@@ -1788,7 +1788,7 @@ namespace ts {
17881788
}
17891789
else {
17901790
// Get the declaration file path
1791-
forEachExpectedEmitFile(host, getDeclFileName, referencedFile, emitOnlyDtsFiles);
1791+
forEachEmittedFile(host, getDeclFileName, referencedFile, emitOnlyDtsFiles);
17921792
}
17931793

17941794
if (declFileName) {

src/compiler/emitter.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ namespace ts {
7373

7474
// Emit each output file
7575
performance.mark("beforePrint");
76-
forEachTransformedEmitFile(host, transformed, emitFile, emitOnlyDtsFiles);
76+
forEachEmittedFile(host, emitFile, transformed, emitOnlyDtsFiles);
7777
performance.measure("printTime", "beforePrint");
7878

7979
// Clean up emit nodes on parse tree
@@ -88,7 +88,7 @@ namespace ts {
8888
sourceMaps: sourceMapDataList
8989
};
9090

91-
function emitFile(jsFilePath: string, sourceMapFilePath: string, declarationFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean) {
91+
function emitFile({ jsFilePath, sourceMapFilePath, declarationFilePath }: EmitFileNames, sourceFiles: SourceFile[], isBundledEmit: boolean) {
9292
// Make sure not to write js file and source map file if any of them cannot be written
9393
if (!host.isEmitBlocked(jsFilePath) && !compilerOptions.noEmit) {
9494
if (!emitOnlyDtsFiles) {

src/compiler/program.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -439,7 +439,7 @@ namespace ts {
439439

440440
function getCommonSourceDirectory() {
441441
if (commonSourceDirectory === undefined) {
442-
const emittedFiles = filterSourceFilesInDirectory(files, isSourceFileFromExternalLibrary);
442+
const emittedFiles = filter(files, file => sourceFileMayBeEmitted(file, options, isSourceFileFromExternalLibrary));
443443
if (options.rootDir && checkSourceFilesBelongToPath(emittedFiles, options.rootDir)) {
444444
// If a rootDir is specified and is valid use it as the commonSourceDirectory
445445
commonSourceDirectory = getNormalizedAbsolutePath(options.rootDir, currentDirectory);
@@ -1707,7 +1707,7 @@ namespace ts {
17071707
if (!options.noEmit && !options.suppressOutputPathCheck) {
17081708
const emitHost = getEmitHost();
17091709
const emitFilesSeen = createFileMap<boolean>(!host.useCaseSensitiveFileNames() ? key => key.toLocaleLowerCase() : undefined);
1710-
forEachExpectedEmitFile(emitHost, (emitFileNames) => {
1710+
forEachEmittedFile(emitHost, (emitFileNames) => {
17111711
verifyEmitFilePath(emitFileNames.jsFilePath, emitFilesSeen);
17121712
verifyEmitFilePath(emitFileNames.declarationFilePath, emitFilesSeen);
17131713
});

src/compiler/utilities.ts

Lines changed: 39 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -2568,102 +2568,57 @@ namespace ts {
25682568
* @param host An EmitHost.
25692569
* @param targetSourceFile An optional target source file to emit.
25702570
*/
2571-
export function getSourceFilesToEmit(host: EmitHost, targetSourceFile?: SourceFile) {
2571+
export function getSourceFilesToEmit(host: EmitHost, targetSourceFile?: SourceFile): SourceFile[] {
25722572
const options = host.getCompilerOptions();
2573+
const isSourceFileFromExternalLibrary = (file: SourceFile) => host.isSourceFileFromExternalLibrary(file);
25732574
if (options.outFile || options.out) {
25742575
const moduleKind = getEmitModuleKind(options);
25752576
const moduleEmitEnabled = moduleKind === ModuleKind.AMD || moduleKind === ModuleKind.System;
2576-
const sourceFiles = getAllEmittableSourceFiles();
25772577
// Can emit only sources that are not declaration file and are either non module code or module with --module or --target es6 specified
2578-
return filter(sourceFiles, moduleEmitEnabled ? isNonDeclarationFile : isBundleEmitNonExternalModule);
2578+
return filter(host.getSourceFiles(), sourceFile =>
2579+
(moduleEmitEnabled || !isExternalModule(sourceFile)) && sourceFileMayBeEmitted(sourceFile, options, isSourceFileFromExternalLibrary));
25792580
}
25802581
else {
2581-
const sourceFiles = targetSourceFile === undefined ? getAllEmittableSourceFiles() : [targetSourceFile];
2582-
return filterSourceFilesInDirectory(sourceFiles, file => host.isSourceFileFromExternalLibrary(file));
2582+
const sourceFiles = targetSourceFile === undefined ? host.getSourceFiles() : [targetSourceFile];
2583+
return filter(sourceFiles, sourceFile => sourceFileMayBeEmitted(sourceFile, options, isSourceFileFromExternalLibrary));
25832584
}
2584-
2585-
function getAllEmittableSourceFiles() {
2586-
return options.noEmitForJsFiles ? filter(host.getSourceFiles(), sourceFile => !isSourceFileJavaScript(sourceFile)) : host.getSourceFiles();
2587-
}
2588-
}
2589-
2590-
/** Don't call this for `--outFile`, just for `--outDir` or plain emit. */
2591-
export function filterSourceFilesInDirectory(sourceFiles: SourceFile[], isSourceFileFromExternalLibrary: (file: SourceFile) => boolean): SourceFile[] {
2592-
return filter(sourceFiles, file => shouldEmitInDirectory(file, isSourceFileFromExternalLibrary));
2593-
}
2594-
2595-
function isNonDeclarationFile(sourceFile: SourceFile) {
2596-
return !isDeclarationFile(sourceFile);
2597-
}
2598-
2599-
/**
2600-
* Whether a file should be emitted in a non-`--outFile` case.
2601-
* Don't emit if source file is a declaration file, or was located under node_modules
2602-
*/
2603-
function shouldEmitInDirectory(sourceFile: SourceFile, isSourceFileFromExternalLibrary: (file: SourceFile) => boolean): boolean {
2604-
return isNonDeclarationFile(sourceFile) && !isSourceFileFromExternalLibrary(sourceFile);
26052585
}
26062586

2607-
function isBundleEmitNonExternalModule(sourceFile: SourceFile) {
2608-
return isNonDeclarationFile(sourceFile) && !isExternalModule(sourceFile);
2587+
/** Don't call this for `--outFile`, just for `--outDir` or plain emit. `--outFile` needs additional checks. */
2588+
export function sourceFileMayBeEmitted(sourceFile: SourceFile, options: CompilerOptions, isSourceFileFromExternalLibrary: (file: SourceFile) => boolean) {
2589+
return !(options.noEmitForJsFiles && isSourceFileJavaScript(sourceFile)) && !isDeclarationFile(sourceFile) && !isSourceFileFromExternalLibrary(sourceFile);
26092590
}
26102591

26112592
/**
2612-
* Iterates over each source file to emit. The source files are expected to have been
2613-
* transformed for use by the pretty printer.
2614-
*
2615-
* Originally part of `forEachExpectedEmitFile`, this functionality was extracted to support
2616-
* transformations.
2593+
* Iterates over the source files that are expected to have an emit output.
26172594
*
26182595
* @param host An EmitHost.
2619-
* @param sourceFiles The transformed source files to emit.
26202596
* @param action The action to execute.
2597+
* @param sourceFilesOrTargetSourceFile
2598+
* If an array, the full list of source files to emit.
2599+
* Else, calls `getSourceFilesToEmit` with the (optional) target source file to determine the list of source files to emit.
26212600
*/
2622-
export function forEachTransformedEmitFile(host: EmitHost, sourceFiles: SourceFile[],
2623-
action: (jsFilePath: string, sourceMapFilePath: string, declarationFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean) => void,
2601+
export function forEachEmittedFile(
2602+
host: EmitHost, action: (emitFileNames: EmitFileNames, sourceFiles: SourceFile[], isBundledEmit: boolean, emitOnlyDtsFiles: boolean) => void,
2603+
sourceFilesOrTargetSourceFile?: SourceFile[] | SourceFile,
26242604
emitOnlyDtsFiles?: boolean) {
2605+
2606+
const sourceFiles = isArray(sourceFilesOrTargetSourceFile) ? sourceFilesOrTargetSourceFile : getSourceFilesToEmit(host, sourceFilesOrTargetSourceFile);
26252607
const options = host.getCompilerOptions();
2626-
// Emit on each source file
26272608
if (options.outFile || options.out) {
2628-
onBundledEmit(sourceFiles);
2629-
}
2630-
else {
2631-
for (const sourceFile of sourceFiles) {
2632-
// Don't emit if source file is a declaration file, or was located under node_modules
2633-
if (!isDeclarationFile(sourceFile) && !host.isSourceFileFromExternalLibrary(sourceFile)) {
2634-
onSingleFileEmit(host, sourceFile);
2635-
}
2636-
}
2637-
}
2638-
2639-
function onSingleFileEmit(host: EmitHost, sourceFile: SourceFile) {
2640-
// JavaScript files are always LanguageVariant.JSX, as JSX syntax is allowed in .js files also.
2641-
// So for JavaScript files, '.jsx' is only emitted if the input was '.jsx', and JsxEmit.Preserve.
2642-
// For TypeScript, the only time to emit with a '.jsx' extension, is on JSX input, and JsxEmit.Preserve
2643-
let extension = ".js";
2644-
if (options.jsx === JsxEmit.Preserve) {
2645-
if (isSourceFileJavaScript(sourceFile)) {
2646-
if (fileExtensionIs(sourceFile.fileName, ".jsx")) {
2647-
extension = ".jsx";
2648-
}
2649-
}
2650-
else if (sourceFile.languageVariant === LanguageVariant.JSX) {
2651-
// TypeScript source file preserving JSX syntax
2652-
extension = ".jsx";
2653-
}
2654-
}
2655-
const jsFilePath = getOwnEmitOutputFilePath(sourceFile, host, extension);
2656-
const sourceMapFilePath = getSourceMapFilePath(jsFilePath, options);
2657-
const declarationFilePath = !isSourceFileJavaScript(sourceFile) && (options.declaration || emitOnlyDtsFiles) ? getDeclarationEmitOutputFilePath(sourceFile, host) : undefined;
2658-
action(jsFilePath, sourceMapFilePath, declarationFilePath, [sourceFile], /*isBundledEmit*/ false);
2659-
}
2660-
2661-
function onBundledEmit(sourceFiles: SourceFile[]) {
26622609
if (sourceFiles.length) {
26632610
const jsFilePath = options.outFile || options.out;
26642611
const sourceMapFilePath = getSourceMapFilePath(jsFilePath, options);
26652612
const declarationFilePath = options.declaration ? removeFileExtension(jsFilePath) + ".d.ts" : undefined;
2666-
action(jsFilePath, sourceMapFilePath, declarationFilePath, sourceFiles, /*isBundledEmit*/ true);
2613+
action({ jsFilePath, sourceMapFilePath, declarationFilePath }, sourceFiles, /*isBundledEmit*/true, emitOnlyDtsFiles);
2614+
}
2615+
}
2616+
else {
2617+
for (const sourceFile of sourceFiles) {
2618+
const jsFilePath = getOwnEmitOutputFilePath(sourceFile, host, getOutputExtension(sourceFile, options));
2619+
const sourceMapFilePath = getSourceMapFilePath(jsFilePath, options);
2620+
const declarationFilePath = !isSourceFileJavaScript(sourceFile) && (emitOnlyDtsFiles || options.declaration) ? getDeclarationEmitOutputFilePath(sourceFile, host) : undefined;
2621+
action({ jsFilePath, sourceMapFilePath, declarationFilePath }, [sourceFile], /*isBundledEmit*/false, emitOnlyDtsFiles);
26672622
}
26682623
}
26692624
}
@@ -2672,77 +2627,22 @@ namespace ts {
26722627
return options.sourceMap ? jsFilePath + ".map" : undefined;
26732628
}
26742629

2675-
/**
2676-
* Iterates over the source files that are expected to have an emit output. This function
2677-
* is used by the legacy emitter and the declaration emitter and should not be used by
2678-
* the tree transforming emitter.
2679-
*
2680-
* @param host An EmitHost.
2681-
* @param action The action to execute.
2682-
* @param targetSourceFile An optional target source file to emit.
2683-
*/
2684-
export function forEachExpectedEmitFile(host: EmitHost,
2685-
action: (emitFileNames: EmitFileNames, sourceFiles: SourceFile[], isBundledEmit: boolean, emitOnlyDtsFiles: boolean) => void,
2686-
targetSourceFile?: SourceFile,
2687-
emitOnlyDtsFiles?: boolean) {
2688-
const options = host.getCompilerOptions();
2689-
// Emit on each source file
2690-
if (options.outFile || options.out) {
2691-
onBundledEmit(host);
2692-
}
2693-
else {
2694-
const sourceFiles = targetSourceFile === undefined ? getSourceFilesToEmit(host) : [targetSourceFile];
2695-
for (const sourceFile of sourceFiles) {
2696-
if (shouldEmitInDirectory(sourceFile, file => host.isSourceFileFromExternalLibrary(file))) {
2697-
onSingleFileEmit(host, sourceFile);
2698-
}
2699-
}
2700-
}
2701-
2702-
function onSingleFileEmit(host: EmitHost, sourceFile: SourceFile) {
2703-
// JavaScript files are always LanguageVariant.JSX, as JSX syntax is allowed in .js files also.
2704-
// So for JavaScript files, '.jsx' is only emitted if the input was '.jsx', and JsxEmit.Preserve.
2705-
// For TypeScript, the only time to emit with a '.jsx' extension, is on JSX input, and JsxEmit.Preserve
2706-
let extension = ".js";
2707-
if (options.jsx === JsxEmit.Preserve) {
2708-
if (isSourceFileJavaScript(sourceFile)) {
2709-
if (fileExtensionIs(sourceFile.fileName, ".jsx")) {
2710-
extension = ".jsx";
2711-
}
2712-
}
2713-
else if (sourceFile.languageVariant === LanguageVariant.JSX) {
2714-
// TypeScript source file preserving JSX syntax
2715-
extension = ".jsx";
2630+
// JavaScript files are always LanguageVariant.JSX, as JSX syntax is allowed in .js files also.
2631+
// So for JavaScript files, '.jsx' is only emitted if the input was '.jsx', and JsxEmit.Preserve.
2632+
// For TypeScript, the only time to emit with a '.jsx' extension, is on JSX input, and JsxEmit.Preserve
2633+
function getOutputExtension(sourceFile: SourceFile, options: CompilerOptions): string {
2634+
if (options.jsx === JsxEmit.Preserve) {
2635+
if (isSourceFileJavaScript(sourceFile)) {
2636+
if (fileExtensionIs(sourceFile.fileName, ".jsx")) {
2637+
return ".jsx";
27162638
}
27172639
}
2718-
const jsFilePath = getOwnEmitOutputFilePath(sourceFile, host, extension);
2719-
const declarationFilePath = !isSourceFileJavaScript(sourceFile) && (emitOnlyDtsFiles || options.declaration) ? getDeclarationEmitOutputFilePath(sourceFile, host) : undefined;
2720-
const emitFileNames: EmitFileNames = {
2721-
jsFilePath,
2722-
sourceMapFilePath: getSourceMapFilePath(jsFilePath, options),
2723-
declarationFilePath
2724-
};
2725-
action(emitFileNames, [sourceFile], /*isBundledEmit*/false, emitOnlyDtsFiles);
2726-
}
2727-
2728-
function onBundledEmit(host: EmitHost) {
2729-
// Can emit only sources that are not declaration file and are either non module code or module with
2730-
// --module or --target es6 specified. Files included by searching under node_modules are also not emitted.
2731-
const bundledSources = filter(getSourceFilesToEmit(host),
2732-
sourceFile => !isDeclarationFile(sourceFile) &&
2733-
!host.isSourceFileFromExternalLibrary(sourceFile) &&
2734-
(!isExternalModule(sourceFile) ||
2735-
!!getEmitModuleKind(options)));
2736-
if (bundledSources.length) {
2737-
const jsFilePath = options.outFile || options.out;
2738-
const emitFileNames: EmitFileNames = {
2739-
jsFilePath,
2740-
sourceMapFilePath: getSourceMapFilePath(jsFilePath, options),
2741-
declarationFilePath: options.declaration ? removeFileExtension(jsFilePath) + ".d.ts" : undefined
2742-
};
2743-
action(emitFileNames, bundledSources, /*isBundledEmit*/true, emitOnlyDtsFiles);
2640+
else if (sourceFile.languageVariant === LanguageVariant.JSX) {
2641+
// TypeScript source file preserving JSX syntax
2642+
return ".jsx";
27442643
}
27452644
}
2645+
return ".js";
27462646
}
27472647

27482648
export function getSourceFilePathInNewDir(sourceFile: SourceFile, host: EmitHost, newDirPath: string) {
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//// [tests/cases/compiler/noBundledEmitFromNodeModules.ts] ////
2+
3+
//// [index.ts]
4+
5+
export class C {}
6+
7+
//// [a.ts]
8+
import { C } from "projB";
9+
10+
11+
//// [out.js]
12+
System.register("a", [], function (exports_1, context_1) {
13+
"use strict";
14+
var __moduleName = context_1 && context_1.id;
15+
return {
16+
setters: [],
17+
execute: function () {
18+
}
19+
};
20+
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
=== /a.ts ===
2+
import { C } from "projB";
3+
>C : Symbol(C, Decl(a.ts, 0, 8))
4+
5+
=== /node_modules/projB/index.ts ===
6+
7+
export class C {}
8+
>C : Symbol(C, Decl(index.ts, 0, 0))
9+
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
=== /a.ts ===
2+
import { C } from "projB";
3+
>C : typeof C
4+
5+
=== /node_modules/projB/index.ts ===
6+
7+
export class C {}
8+
>C : C
9+
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// @outFile: out.js
2+
// @module: system
3+
// @moduleResolution: node
4+
// @noImplicitReferences: true
5+
6+
// @fileName: /node_modules/projB/index.ts
7+
export class C {}
8+
9+
// @fileName: /a.ts
10+
import { C } from "projB";

0 commit comments

Comments
 (0)