Skip to content

Commit 311f421

Browse files
committed
Make sure to build all solution builder files before and track input and output
1 parent a412371 commit 311f421

File tree

32 files changed

+714
-470
lines changed

32 files changed

+714
-470
lines changed

src/after-compile.ts

+35-23
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 { getEmitFromWatchHost, getEmitOutput } from './instances';
6+
import {
7+
getEmitFromWatchHost,
8+
getEmitOutput,
9+
isReferencedFile
10+
} from './instances';
711
import {
812
TSFile,
913
TSFiles,
@@ -339,25 +343,24 @@ function provideDeclarationFilesToWebpack(
339343
continue;
340344
}
341345

342-
addDeclarationFilesAsAsset(
343-
getEmitOutput(
344-
instance,
345-
filePath,
346-
/*skipActualOutputReadOfReferencedFile*/ true
347-
),
348-
compilation
349-
);
346+
if (!isReferencedFile(instance, filePath)) {
347+
addDeclarationFilesAsAsset(
348+
getEmitOutput(instance, filePath),
349+
compilation
350+
);
351+
}
350352
}
351353
}
352354

353-
function addDeclarationFilesAsAsset(
354-
outputFiles: ts.OutputFile[] | IterableIterator<ts.OutputFile>,
355-
compilation: webpack.compilation.Compilation
355+
function addDeclarationFilesAsAsset<T extends ts.OutputFile>(
356+
outputFiles: T[] | IterableIterator<T>,
357+
compilation: webpack.compilation.Compilation,
358+
skipOutputFile?: (outputFile: T) => boolean
356359
) {
357-
outputFilesToAsset(
358-
outputFiles,
359-
compilation,
360-
outputFile => !outputFile.name.match(constants.dtsDtsxOrDtsDtsxMapRegex)
360+
outputFilesToAsset(outputFiles, compilation, outputFile =>
361+
skipOutputFile && skipOutputFile(outputFile)
362+
? true
363+
: !outputFile.name.match(constants.dtsDtsxOrDtsDtsxMapRegex)
361364
);
362365
}
363366

@@ -375,10 +378,10 @@ function outputFileToAsset(
375378
};
376379
}
377380

378-
function outputFilesToAsset(
379-
outputFiles: ts.OutputFile[] | IterableIterator<ts.OutputFile>,
381+
function outputFilesToAsset<T extends ts.OutputFile>(
382+
outputFiles: T[] | IterableIterator<T>,
380383
compilation: webpack.compilation.Compilation,
381-
skipOutputFile?: (outputFile: ts.OutputFile) => boolean
384+
skipOutputFile?: (outputFile: T) => boolean
382385
) {
383386
for (const outputFile of outputFiles) {
384387
if (!skipOutputFile || !skipOutputFile(outputFile)) {
@@ -417,12 +420,21 @@ function provideAssetsFromSolutionBuilderHost(
417420
// written files
418421
addDeclarationFilesAsAsset(
419422
instance.solutionBuilderHost.outputFiles.values(),
420-
compilation
423+
compilation,
424+
outputFile => !outputFile.isNew
421425
);
422426
// tsbuild infos
423-
outputFilesToAsset(instance.solutionBuilderHost.tsbuildinfos, compilation);
424-
instance.solutionBuilderHost.outputFiles.clear();
425-
instance.solutionBuilderHost.tsbuildinfos.length = 0;
427+
outputFilesToAsset(
428+
instance.solutionBuilderHost.tsbuildinfos.values(),
429+
compilation,
430+
outputFile => !outputFile.isNew
431+
);
432+
instance.solutionBuilderHost.outputFiles.forEach(
433+
outputFile => (outputFile.isNew = false)
434+
);
435+
instance.solutionBuilderHost.tsbuildinfos.forEach(
436+
outputFile => (outputFile.isNew = false)
437+
);
426438
}
427439
}
428440

src/index.ts

+100-84
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ import * as webpack from 'webpack';
55

66
import * as constants from './constants';
77
import {
8+
buildSolutionReferences,
89
getEmitOutput,
910
getInputFileNameFromOutput,
1011
getTypeScriptInstance,
12+
initializeInstance,
1113
isReferencedFile
1214
} from './instances';
1315
import {
@@ -44,36 +46,38 @@ function loader(this: webpack.loader.LoaderContext, contents: string) {
4446
return;
4547
}
4648

47-
return successLoader(
48-
this,
49-
contents,
50-
callback,
51-
options,
52-
instanceOrError.instance!
53-
);
49+
const instance = instanceOrError.instance!;
50+
buildSolutionReferences(instance, this, instance.log);
51+
successLoader(this, contents, callback, instance);
5452
}
5553

5654
function successLoader(
5755
loaderContext: webpack.loader.LoaderContext,
5856
contents: string,
5957
callback: webpack.loader.loaderCallback,
60-
options: LoaderOptions,
6158
instance: TSInstance
6259
) {
60+
initializeInstance(loaderContext, instance);
6361
const rawFilePath = path.normalize(loaderContext.resourcePath);
6462

6563
const filePath =
66-
options.appendTsSuffixTo.length > 0 || options.appendTsxSuffixTo.length > 0
64+
instance.loaderOptions.appendTsSuffixTo.length > 0 ||
65+
instance.loaderOptions.appendTsxSuffixTo.length > 0
6766
? appendSuffixesIfMatch(
6867
{
69-
'.ts': options.appendTsSuffixTo,
70-
'.tsx': options.appendTsxSuffixTo
68+
'.ts': instance.loaderOptions.appendTsSuffixTo,
69+
'.tsx': instance.loaderOptions.appendTsxSuffixTo
7170
},
7271
rawFilePath
7372
)
7473
: rawFilePath;
7574

76-
const fileVersion = updateFileInCache(options, filePath, contents, instance);
75+
const fileVersion = updateFileInCache(
76+
instance.loaderOptions,
77+
filePath,
78+
contents,
79+
instance
80+
);
7781
const referencedProject = getAndCacheProjectReference(filePath, instance);
7882
if (referencedProject !== undefined) {
7983
const [relativeProjectConfigPath, relativeFilePath] = [
@@ -133,13 +137,12 @@ function successLoader(
133137
filePath,
134138
contents,
135139
loaderContext,
136-
options,
137140
fileVersion,
138141
callback,
139142
instance
140143
);
141144
} else {
142-
const { outputText, sourceMapText } = options.transpileOnly
145+
const { outputText, sourceMapText } = instance.loaderOptions.transpileOnly
143146
? getTranspilationEmit(filePath, contents, instance, loaderContext)
144147
: getEmit(rawFilePath, filePath, instance, loaderContext);
145148

@@ -149,7 +152,6 @@ function successLoader(
149152
filePath,
150153
contents,
151154
loaderContext,
152-
options,
153155
fileVersion,
154156
callback,
155157
instance
@@ -163,23 +165,29 @@ function makeSourceMapAndFinish(
163165
filePath: string,
164166
contents: string,
165167
loaderContext: webpack.loader.LoaderContext,
166-
options: LoaderOptions,
167168
fileVersion: number,
168169
callback: webpack.loader.loaderCallback,
169170
instance: TSInstance
170171
) {
171172
if (outputText === null || outputText === undefined) {
173+
setModuleMeta(loaderContext, instance, fileVersion);
172174
const additionalGuidance = isReferencedFile(instance, filePath)
173175
? ' The most common cause for this is having errors when building referenced projects.'
174-
: !options.allowTsInNodeModules && filePath.indexOf('node_modules') !== -1
176+
: !instance.loaderOptions.allowTsInNodeModules &&
177+
filePath.indexOf('node_modules') !== -1
175178
? ' By default, ts-loader will not compile .ts files in node_modules.\n' +
176179
'You should not need to recompile .ts files there, but if you really want to, use the allowTsInNodeModules option.\n' +
177180
'See: https://github.com/Microsoft/TypeScript/issues/12358'
178181
: '';
179182

180-
throw new Error(
181-
`TypeScript emitted no output for ${filePath}.${additionalGuidance}`
183+
callback(
184+
new Error(
185+
`TypeScript emitted no output for ${filePath}.${additionalGuidance}`
186+
),
187+
outputText,
188+
undefined
182189
);
190+
return;
183191
}
184192

185193
const { sourceMap, output } = makeSourceMap(
@@ -190,15 +198,25 @@ function makeSourceMapAndFinish(
190198
loaderContext
191199
);
192200

201+
setModuleMeta(loaderContext, instance, fileVersion);
202+
callback(null, output, sourceMap);
203+
}
204+
205+
function setModuleMeta(
206+
loaderContext: webpack.loader.LoaderContext,
207+
instance: TSInstance,
208+
fileVersion: number
209+
) {
193210
// _module.meta is not available inside happypack
194-
if (!options.happyPackMode && loaderContext._module.buildMeta !== undefined) {
211+
if (
212+
!instance.loaderOptions.happyPackMode &&
213+
loaderContext._module.buildMeta !== undefined
214+
) {
195215
// Make sure webpack is aware that even though the emitted JavaScript may be the same as
196216
// a previously cached version the TypeScript may be different and therefore should be
197217
// treated as new
198218
loaderContext._module.buildMeta.tsLoaderFileVersion = fileVersion;
199219
}
200-
201-
callback(null, output, sourceMap);
202220
}
203221

204222
/**
@@ -395,14 +413,15 @@ function updateFileInCache(
395413
// it is allowed by the options.
396414
(options.allowTsInNodeModules || filePath.indexOf('node_modules') === -1)
397415
) {
398-
instance.version!++;
416+
instance.version++;
399417
instance.rootFileNames.add(filePath);
400418
}
401419

402420
if (file.text !== contents) {
403421
file.version++;
404422
file.text = contents;
405-
instance.version!++;
423+
file.modifiedTime = new Date();
424+
instance.version++;
406425
if (
407426
(instance.watchHost !== undefined ||
408427
instance.solutionBuilderHost !== undefined) &&
@@ -447,73 +466,70 @@ function getEmit(
447466
instance: TSInstance,
448467
loaderContext: webpack.loader.LoaderContext
449468
) {
450-
const outputFiles = getEmitOutput(
451-
instance,
452-
filePath,
453-
/*skipActualOutputReadOfReferencedFile*/ false
454-
);
455-
456-
if (!isReferencedFile(instance, filePath)) {
457-
loaderContext.clearDependencies();
458-
loaderContext.addDependency(rawFilePath);
469+
const outputFiles = getEmitOutput(instance, filePath);
470+
loaderContext.clearDependencies();
471+
loaderContext.addDependency(rawFilePath);
459472

460-
const allDefinitionFiles = [...instance.files.keys()].filter(
461-
defFilePath =>
462-
defFilePath.match(constants.dtsDtsxOrDtsDtsxMapRegex) &&
463-
// Remove the project reference d.ts as we are adding dependency for .ts later
464-
// This removed extra build pass (resulting in new stats object in initial build)
465-
(!instance.solutionBuilderHost ||
466-
!getInputFileNameFromOutput(instance, defFilePath))
467-
);
473+
const allDefinitionFiles = isReferencedFile(instance, filePath)
474+
? []
475+
: [...instance.files.keys()].filter(
476+
defFilePath =>
477+
defFilePath.match(constants.dtsDtsxOrDtsDtsxMapRegex) &&
478+
// Remove the project reference d.ts as we are adding dependency for .ts later
479+
// This removed extra build pass (resulting in new stats object in initial build)
480+
(!instance.solutionBuilderHost ||
481+
instance.solutionBuilderHost.getOutputFileFromReferencedProject(
482+
defFilePath
483+
) !== undefined)
484+
);
468485

469-
// Make this file dependent on *all* definition files in the program
470-
const addDependency = loaderContext.addDependency.bind(loaderContext);
471-
allDefinitionFiles.forEach(addDependency);
472-
473-
// Additionally make this file dependent on all imported files
474-
const fileDependencies = instance.dependencyGraph[filePath];
475-
const additionalDependencies =
476-
fileDependencies === undefined
477-
? []
478-
: fileDependencies.map(({ resolvedFileName, originalFileName }) => {
479-
const projectReference = getAndCacheProjectReference(
486+
// Make this file dependent on *all* definition files in the program
487+
const addDependency = loaderContext.addDependency.bind(loaderContext);
488+
allDefinitionFiles.forEach(addDependency);
489+
490+
// Additionally make this file dependent on all imported files
491+
const fileDependencies = instance.dependencyGraph[filePath];
492+
const additionalDependencies =
493+
fileDependencies === undefined
494+
? []
495+
: fileDependencies.map(({ resolvedFileName, originalFileName }) => {
496+
const projectReference = getAndCacheProjectReference(
497+
resolvedFileName,
498+
instance
499+
);
500+
// In the case of dependencies that are part of a project reference,
501+
// the real dependency that webpack should watch is the JS output file.
502+
if (projectReference !== undefined) {
503+
return getAndCacheOutputJSFileName(
480504
resolvedFileName,
505+
projectReference,
481506
instance
482507
);
483-
// In the case of dependencies that are part of a project reference,
484-
// the real dependency that webpack should watch is the JS output file.
485-
if (projectReference !== undefined) {
486-
return getAndCacheOutputJSFileName(
487-
resolvedFileName,
488-
projectReference,
489-
instance
490-
);
491-
}
492-
return (
493-
getInputFileNameFromOutput(
494-
instance,
495-
path.resolve(resolvedFileName)
496-
) || originalFileName
497-
);
498-
});
499-
500-
if (additionalDependencies.length > 0) {
501-
additionalDependencies.forEach(addDependency);
502-
}
503-
504-
loaderContext._module.buildMeta.tsLoaderDefinitionFileVersions = allDefinitionFiles
505-
.concat(additionalDependencies)
506-
.map(
507-
defFilePath =>
508-
defFilePath +
509-
'@' +
510-
(
511-
instance.files.get(defFilePath) ||
512-
instance.otherFiles.get(defFilePath) || { version: '?' }
513-
).version
514-
);
508+
}
509+
return (
510+
getInputFileNameFromOutput(
511+
instance,
512+
path.resolve(resolvedFileName)
513+
) || originalFileName
514+
);
515+
});
516+
517+
if (additionalDependencies.length > 0) {
518+
additionalDependencies.forEach(addDependency);
515519
}
516520

521+
loaderContext._module.buildMeta.tsLoaderDefinitionFileVersions = allDefinitionFiles
522+
.concat(additionalDependencies)
523+
.map(
524+
defFilePath =>
525+
defFilePath +
526+
'@' +
527+
(
528+
instance.files.get(defFilePath) ||
529+
instance.otherFiles.get(defFilePath) || { version: '?' }
530+
).version
531+
);
532+
517533
const outputFile = outputFiles
518534
.filter(file => file.name.match(constants.jsJsx))
519535
.pop();

0 commit comments

Comments
 (0)