Skip to content

Commit ce39c25

Browse files
sheetalkamatjohnnyreilly
authored andcommitted
Emitting .tsbuildinfo when using watch api (#1017)
* Watch api tests * Updater the .tsbuildinfo * Write tsbuild info when available using experimentalWatchApi * update CHANGELOG.md * Fix running command for webpack when karmaConfPath is absent in the test
1 parent ed8d596 commit ce39c25

154 files changed

Lines changed: 9322 additions & 162 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Changelog
22

3+
## v6.2.0
4+
* [Emitting .tsbuildinfo when using watch api](https://github.com/TypeStrong/ts-loader/pull/1017) - thanks @sheetalkamat!
5+
36
## v6.1.2
47
* [don't emit declaration files for a declaration file](https://github.com/TypeStrong/ts-loader/pull/1015) (#1014) - thanks @gvinaccia!
58
* [Consume typescript apis from typescript nightly](https://github.com/TypeStrong/ts-loader/pull/1016) - thanks @sheetalkamat!

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ts-loader",
3-
"version": "6.1.2",
3+
"version": "6.2.0",
44
"description": "TypeScript loader for webpack",
55
"main": "index.js",
66
"types": "dist/types/index.d.ts",

src/after-compile.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@ import * as ts from 'typescript';
33
import * as webpack from 'webpack';
44

55
import * as constants from './constants';
6-
import { forEachResolvedProjectReference, getEmitOutput } from './instances';
6+
import {
7+
forEachResolvedProjectReference,
8+
getEmitFromWatchHost,
9+
getEmitOutput
10+
} from './instances';
711
import {
812
TSFile,
913
TSFiles,
@@ -406,6 +410,25 @@ function provideTsBuildInfoFilesToWebpack(
406410
);
407411
}
408412
}
413+
414+
if (instance.watchHost) {
415+
// Ensure emit is complete
416+
getEmitFromWatchHost(instance);
417+
if (instance.watchHost.tsbuildinfo) {
418+
const { tsbuildinfo } = instance.watchHost;
419+
const assetPath = path.relative(
420+
compilation.compiler.outputPath,
421+
path.resolve(tsbuildinfo.name)
422+
);
423+
compilation.assets[assetPath] = {
424+
source: () => tsbuildinfo.text,
425+
size: () => tsbuildinfo.text.length
426+
};
427+
}
428+
429+
instance.watchHost.outputFiles.clear();
430+
instance.watchHost.tsbuildinfo = undefined;
431+
}
409432
}
410433

411434
/**

src/instances.ts

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -307,9 +307,8 @@ function successfulTypeScriptInstance(
307307
instance.watchOfFilesAndCompilerOptions = compiler.createWatchProgram(
308308
instance.watchHost
309309
);
310-
instance.program = instance.watchOfFilesAndCompilerOptions
311-
.getProgram()
312-
.getProgram();
310+
instance.builderProgram = instance.watchOfFilesAndCompilerOptions.getProgram();
311+
instance.program = instance.builderProgram.getProgram();
313312

314313
instance.transformers = getCustomTransformers(instance.program);
315314
} else {
@@ -571,6 +570,60 @@ export function isReferencedFile(instance: TSInstance, filePath: string) {
571570
);
572571
}
573572

573+
export function getEmitFromWatchHost(instance: TSInstance, filePath?: string) {
574+
const program = ensureProgram(instance);
575+
const builderProgram = instance.builderProgram;
576+
if (builderProgram && program) {
577+
if (filePath) {
578+
const existing = instance.watchHost!.outputFiles.get(filePath);
579+
if (existing) {
580+
return existing;
581+
}
582+
}
583+
584+
const outputFiles: typescript.OutputFile[] = [];
585+
const writeFile: typescript.WriteFileCallback = (
586+
fileName,
587+
text,
588+
writeByteOrderMark
589+
) => {
590+
if (fileName.endsWith('.tsbuildinfo')) {
591+
instance.watchHost!.tsbuildinfo = {
592+
name: fileName,
593+
writeByteOrderMark,
594+
text
595+
};
596+
} else {
597+
outputFiles.push({ name: fileName, writeByteOrderMark, text });
598+
}
599+
};
600+
601+
const sourceFile = filePath ? program.getSourceFile(filePath) : undefined;
602+
// Try emit Next file
603+
while (true) {
604+
const result = builderProgram.emitNextAffectedFile(
605+
writeFile,
606+
/*cancellationToken*/ undefined,
607+
/*emitOnlyDtsFiles*/ false,
608+
instance.transformers
609+
);
610+
if (!result) {
611+
break;
612+
}
613+
if ((result.affected as typescript.SourceFile).fileName) {
614+
instance.watchHost!.outputFiles.set(
615+
path.resolve((result.affected as typescript.SourceFile).fileName),
616+
outputFiles.slice()
617+
);
618+
}
619+
if (result.affected === sourceFile) {
620+
return outputFiles;
621+
}
622+
}
623+
}
624+
return undefined;
625+
}
626+
574627
export function getEmitOutput(instance: TSInstance, filePath: string) {
575628
if (fileExtensionIs(filePath, instance.compiler.Extension.Dts)) {
576629
return [];
@@ -596,6 +649,10 @@ export function getEmitOutput(instance: TSInstance, filePath: string) {
596649
) => outputFiles.push({ name: fileName, writeByteOrderMark, text });
597650
// The source file will be undefined if it’s part of an unbuilt project reference
598651
if (sourceFile !== undefined || !isUsingProjectReferences(instance)) {
652+
const outputFilesFromWatch = getEmitFromWatchHost(instance, filePath);
653+
if (outputFilesFromWatch) {
654+
return outputFilesFromWatch;
655+
}
599656
program.emit(
600657
sourceFile,
601658
writeFile,

src/interfaces.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,16 @@ export type ResolveSync = (
3939

4040
export interface WatchHost
4141
extends typescript.WatchCompilerHostOfFilesAndCompilerOptions<
42-
typescript.BuilderProgram
42+
typescript.EmitAndSemanticDiagnosticsBuilderProgram
4343
> {
4444
invokeFileWatcher(
4545
fileName: string,
4646
eventKind: typescript.FileWatcherEventKind
4747
): void;
4848
invokeDirectoryWatcher(directory: string, fileAddedOrRemoved: string): void;
4949
updateRootFileNames(): void;
50+
outputFiles: Map<string, typescript.OutputFile[]>;
51+
tsbuildinfo?: typescript.OutputFile;
5052
}
5153

5254
export type WatchCallbacks<T> = Map<string, T[]>;
@@ -121,8 +123,9 @@ export interface TSInstance {
121123
otherFiles: TSFiles;
122124
watchHost?: WatchHost;
123125
watchOfFilesAndCompilerOptions?: typescript.WatchOfFilesAndCompilerOptions<
124-
typescript.BuilderProgram
126+
typescript.EmitAndSemanticDiagnosticsBuilderProgram
125127
>;
128+
builderProgram?: typescript.EmitAndSemanticDiagnosticsBuilderProgram;
126129
program?: typescript.Program;
127130
hasUnaccountedModifiedFiles?: boolean;
128131
changedFilesList?: boolean;

src/servicesHost.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -517,8 +517,10 @@ export function makeWatchHost(
517517
},
518518
createProgram:
519519
projectReferences === undefined
520-
? compiler.createAbstractBuilder
521-
: createBuilderProgramWithReferences
520+
? compiler.createEmitAndSemanticDiagnosticsBuilderProgram
521+
: createBuilderProgramWithReferences,
522+
523+
outputFiles: new Map()
522524
};
523525
return watchHost;
524526

@@ -549,7 +551,7 @@ export function makeWatchHost(
549551
rootNames: ReadonlyArray<string> | undefined,
550552
options: typescript.CompilerOptions | undefined,
551553
host: typescript.CompilerHost | undefined,
552-
oldProgram: typescript.BuilderProgram | undefined,
554+
oldProgram: typescript.EmitAndSemanticDiagnosticsBuilderProgram | undefined,
553555
configFileParsingDiagnostics:
554556
| ReadonlyArray<typescript.Diagnostic>
555557
| undefined
@@ -564,7 +566,7 @@ export function makeWatchHost(
564566
});
565567

566568
const builderProgramHost: typescript.BuilderProgramHost = host!;
567-
return compiler.createAbstractBuilder(
569+
return compiler.createEmitAndSemanticDiagnosticsBuilderProgram(
568570
program,
569571
builderProgramHost,
570572
oldProgram,

src/utils.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -244,9 +244,8 @@ export function ensureProgram(instance: TSInstance) {
244244
instance.watchHost.updateRootFileNames();
245245
}
246246
if (instance.watchOfFilesAndCompilerOptions) {
247-
instance.program = instance.watchOfFilesAndCompilerOptions
248-
.getProgram()
249-
.getProgram();
247+
instance.builderProgram = instance.watchOfFilesAndCompilerOptions.getProgram();
248+
instance.program = instance.builderProgram.getProgram();
250249
}
251250
instance.hasUnaccountedModifiedFiles = false;
252251
}

test/comparison-tests/create-and-execute-test.js

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,24 @@ function createTest(test, testPath, options) {
8989

9090
// copy all input to a staging area
9191
mkdirp.sync(paths.testStagingPath);
92+
const nonWatchNonCompositePath = testPath.replace(/(_Composite)?_WatchApi$/, "");
93+
if (nonWatchNonCompositePath !== testPath) {
94+
const nonWatchPath = testPath.replace(/_WatchApi$/, "");
95+
// Copy things from non watch path
96+
copySync(nonWatchNonCompositePath, paths.testStagingPath);
97+
if (nonWatchPath !== nonWatchNonCompositePath) {
98+
// Change the tsconfig to be composite
99+
const configPath = path.resolve(paths.testStagingPath, "tsconfig.json");
100+
const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
101+
config.files = [ "./app.ts"];
102+
config.compilerOptions = { composite: true };
103+
fs.writeFileSync(configPath, JSON.stringify(config, /*replacer*/ undefined, " "));
104+
}
105+
}
92106
copySync(testPath, paths.testStagingPath);
93-
if (test === "projectReferencesWatchRefWithTwoFilesAlreadyBuilt") {
107+
if (test.startsWith("projectReferencesWatchRefWithTwoFilesAlreadyBuilt")) {
94108
// Copy output
95-
copySync(path.resolve(testPath, "libOutput"), path.resolve(paths.testStagingPath, "lib"));
109+
copySync(path.resolve(paths.testStagingPath, "libOutput"), path.resolve(paths.testStagingPath, "lib"));
96110
// Change the buildinfo to use typescript version we have
97111
const buildInfoPath = path.resolve(paths.testStagingPath, "lib/tsconfig.tsbuildinfo");
98112
fs.writeFileSync(buildInfoPath, fs.readFileSync(buildInfoPath, "utf8").replace("FakeTSVersion", typescript.version));
@@ -104,7 +118,7 @@ function createTest(test, testPath, options) {
104118

105119
// execute webpack
106120
testState.watcher = webpack(
107-
createWebpackConfig(paths, options)
121+
createWebpackConfig(paths, options, nonWatchNonCompositePath !== testPath)
108122
).watch({ aggregateTimeout: 1500 }, createWebpackWatchHandler(done, paths, testState, outputs, options, test));
109123
};
110124
}
@@ -153,7 +167,7 @@ function storeSavedOutputs(saveOutputMode, outputs, test, options, paths) {
153167
}
154168
}
155169

156-
function createWebpackConfig(paths, optionsOriginal) {
170+
function createWebpackConfig(paths, optionsOriginal, useWatchApi) {
157171
const config = require(path.join(paths.testStagingPath, 'webpack.config'));
158172

159173
const extraOptionMaybe = extraOption ? { [extraOption]: true } : {};
@@ -162,7 +176,8 @@ function createWebpackConfig(paths, optionsOriginal) {
162176
silent: true,
163177
compilerOptions: {
164178
newLine: 'LF'
165-
}
179+
},
180+
experimentalWatchApi: !!useWatchApi
166181
}, optionsOriginal, extraOptionMaybe);
167182

168183
const tsLoaderPath = require('path').join(__dirname, "../../index.js");
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/******/ (function(modules) { // webpackBootstrap
2+
/******/ // The module cache
3+
/******/ var installedModules = {};
4+
/******/
5+
/******/ // The require function
6+
/******/ function __webpack_require__(moduleId) {
7+
/******/
8+
/******/ // Check if module is in cache
9+
/******/ if(installedModules[moduleId]) {
10+
/******/ return installedModules[moduleId].exports;
11+
/******/ }
12+
/******/ // Create a new module (and put it into the cache)
13+
/******/ var module = installedModules[moduleId] = {
14+
/******/ i: moduleId,
15+
/******/ l: false,
16+
/******/ exports: {}
17+
/******/ };
18+
/******/
19+
/******/ // Execute the module function
20+
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
21+
/******/
22+
/******/ // Flag the module as loaded
23+
/******/ module.l = true;
24+
/******/
25+
/******/ // Return the exports of the module
26+
/******/ return module.exports;
27+
/******/ }
28+
/******/
29+
/******/
30+
/******/ // expose the modules object (__webpack_modules__)
31+
/******/ __webpack_require__.m = modules;
32+
/******/
33+
/******/ // expose the module cache
34+
/******/ __webpack_require__.c = installedModules;
35+
/******/
36+
/******/ // define getter function for harmony exports
37+
/******/ __webpack_require__.d = function(exports, name, getter) {
38+
/******/ if(!__webpack_require__.o(exports, name)) {
39+
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
40+
/******/ }
41+
/******/ };
42+
/******/
43+
/******/ // define __esModule on exports
44+
/******/ __webpack_require__.r = function(exports) {
45+
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
46+
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
47+
/******/ }
48+
/******/ Object.defineProperty(exports, '__esModule', { value: true });
49+
/******/ };
50+
/******/
51+
/******/ // create a fake namespace object
52+
/******/ // mode & 1: value is a module id, require it
53+
/******/ // mode & 2: merge all properties of value into the ns
54+
/******/ // mode & 4: return value when already ns object
55+
/******/ // mode & 8|1: behave like require
56+
/******/ __webpack_require__.t = function(value, mode) {
57+
/******/ if(mode & 1) value = __webpack_require__(value);
58+
/******/ if(mode & 8) return value;
59+
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
60+
/******/ var ns = Object.create(null);
61+
/******/ __webpack_require__.r(ns);
62+
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
63+
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
64+
/******/ return ns;
65+
/******/ };
66+
/******/
67+
/******/ // getDefaultExport function for compatibility with non-harmony modules
68+
/******/ __webpack_require__.n = function(module) {
69+
/******/ var getter = module && module.__esModule ?
70+
/******/ function getDefault() { return module['default']; } :
71+
/******/ function getModuleExports() { return module; };
72+
/******/ __webpack_require__.d(getter, 'a', getter);
73+
/******/ return getter;
74+
/******/ };
75+
/******/
76+
/******/ // Object.prototype.hasOwnProperty.call
77+
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
78+
/******/
79+
/******/ // __webpack_public_path__
80+
/******/ __webpack_require__.p = "";
81+
/******/
82+
/******/
83+
/******/ // Load entry module and return exports
84+
/******/ return __webpack_require__(__webpack_require__.s = "./app.ts");
85+
/******/ })
86+
/************************************************************************/
87+
/******/ ({
88+
89+
/***/ "./app.ts":
90+
/*!****************!*\
91+
!*** ./app.ts ***!
92+
\****************/
93+
/*! no static exports found */
94+
/***/ (function(module, exports, __webpack_require__) {
95+
96+
"use strict";
97+
eval("\nexports.__esModule = true;\nvar lib_1 = __webpack_require__(/*! ./lib */ \"./lib/index.ts\");\nconsole.log(lib_1.lib.one, lib_1.lib.two, lib_1.lib.three);\n\n\n//# sourceURL=webpack:///./app.ts?");
98+
99+
/***/ }),
100+
101+
/***/ "./lib/index.ts":
102+
/*!**********************!*\
103+
!*** ./lib/index.ts ***!
104+
\**********************/
105+
/*! no static exports found */
106+
/***/ (function(module, exports, __webpack_require__) {
107+
108+
"use strict";
109+
eval("\r\nexports.__esModule = true;\r\nexports.lib = {\r\n one: 1,\r\n two: 2,\r\n three: 3\r\n};\r\n\n\n//# sourceURL=webpack:///./lib/index.ts?");
110+
111+
/***/ })
112+
113+
/******/ });

0 commit comments

Comments
 (0)