Skip to content

Commit bf49856

Browse files
committed
Add all the files from composite project as dependencies as any change in them can result in errors resulting in changes to the own output of the file
1 parent cdfce32 commit bf49856

File tree

79 files changed

+1703
-165
lines changed

Some content is hidden

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

79 files changed

+1703
-165
lines changed

src/index.ts

+135-54
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ import {
1010
getInputFileNameFromOutput,
1111
getTypeScriptInstance,
1212
initializeInstance,
13-
isReferencedFile
13+
isReferencedFile,
14+
reportTranspileErrors
1415
} from './instances';
1516
import {
1617
LoaderOptions,
@@ -45,9 +46,8 @@ function loader(this: webpack.loader.LoaderContext, contents: string) {
4546
callback(new Error(instanceOrError.error.message));
4647
return;
4748
}
48-
4949
const instance = instanceOrError.instance!;
50-
buildSolutionReferences(instance, this, instance.log);
50+
buildSolutionReferences(instance, this);
5151
successLoader(this, contents, callback, instance);
5252
}
5353

@@ -58,6 +58,7 @@ function successLoader(
5858
instance: TSInstance
5959
) {
6060
initializeInstance(loaderContext, instance);
61+
reportTranspileErrors(instance, loaderContext);
6162
const rawFilePath = path.normalize(loaderContext.resourcePath);
6263

6364
const filePath =
@@ -470,65 +471,70 @@ function getEmit(
470471
loaderContext.clearDependencies();
471472
loaderContext.addDependency(rawFilePath);
472473

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-
);
474+
const dependencies: string[] = [];
475+
const addDependency = (file: string) => {
476+
file = path.resolve(file);
477+
loaderContext.addDependency(file);
478+
dependencies.push(file);
479+
};
485480

486481
// Make this file dependent on *all* definition files in the program
487-
const addDependency = loaderContext.addDependency.bind(loaderContext);
488-
allDefinitionFiles.forEach(addDependency);
482+
if (!isReferencedFile(instance, filePath)) {
483+
for (const defFilePath of instance.files.keys()) {
484+
if (
485+
defFilePath.match(constants.dtsDtsxOrDtsDtsxMapRegex) &&
486+
// Remove the project reference d.ts as we are adding dependency for .ts later
487+
// This removed extra build pass (resulting in new stats object in initial build)
488+
(!instance.solutionBuilderHost ||
489+
instance.solutionBuilderHost.getOutputFileFromReferencedProject(
490+
defFilePath
491+
) !== undefined)
492+
) {
493+
addDependency(defFilePath);
494+
}
495+
}
496+
}
489497

490498
// Additionally make this file dependent on all imported files
491499
const fileDependencies = instance.dependencyGraph[filePath];
492-
const additionalDependencies =
493-
fileDependencies === undefined
494-
? []
495-
: fileDependencies.map(({ resolvedFileName, originalFileName }) => {
496-
const projectReference = getAndCacheProjectReference(
500+
if (fileDependencies) {
501+
for (const { resolvedFileName, originalFileName } of fileDependencies) {
502+
const projectReference = getAndCacheProjectReference(
503+
resolvedFileName,
504+
instance
505+
);
506+
// In the case of dependencies that are part of a project reference,
507+
// the real dependency that webpack should watch is the JS output file.
508+
if (projectReference !== undefined) {
509+
addDependency(
510+
getAndCacheOutputJSFileName(
497511
resolvedFileName,
512+
projectReference,
498513
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(
504-
resolvedFileName,
505-
projectReference,
506-
instance
507-
);
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);
514+
)
515+
);
516+
} else {
517+
addDependency(
518+
getInputFileNameFromOutput(
519+
instance,
520+
path.resolve(resolvedFileName)
521+
) || originalFileName
522+
);
523+
}
524+
}
519525
}
520526

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-
);
527+
addDependenciesFromSolutionBuilder(instance, filePath, addDependency);
528+
529+
loaderContext._module.buildMeta.tsLoaderDefinitionFileVersions = dependencies.map(
530+
defFilePath =>
531+
defFilePath +
532+
'@' +
533+
(
534+
instance.files.get(defFilePath) ||
535+
instance.otherFiles.get(defFilePath) || { version: '?' }
536+
).version
537+
);
532538

533539
const outputFile = outputFiles
534540
.filter(file => file.name.match(constants.jsJsx))
@@ -540,10 +546,81 @@ function getEmit(
540546
.pop();
541547
const sourceMapText =
542548
sourceMapFile === undefined ? undefined : sourceMapFile.text;
543-
544549
return { outputText, sourceMapText };
545550
}
546551

552+
function addDependenciesFromSolutionBuilder(
553+
instance: TSInstance,
554+
filePath: string,
555+
addDependency: (file: string) => void
556+
) {
557+
if (!instance.solutionBuilderHost) {
558+
return;
559+
}
560+
// Add all the input files from the references as
561+
const resolvedFilePath = path.resolve(filePath);
562+
for (const [
563+
configFile,
564+
configInfo
565+
] of instance.solutionBuilderHost.configFileInfo.entries()) {
566+
if (
567+
!configInfo.config ||
568+
!configInfo.config.projectReferences ||
569+
!configInfo.config.projectReferences.length
570+
) {
571+
continue;
572+
}
573+
if (configInfo.outputFileNames) {
574+
if (!configInfo.outputFileNames.has(resolvedFilePath)) {
575+
continue;
576+
}
577+
} else if (
578+
!configInfo.config.fileNames.some(
579+
f => path.resolve(f) === resolvedFilePath
580+
)
581+
) {
582+
continue;
583+
}
584+
585+
// This is the config for the input file
586+
const seenMap = new Map<string, true>();
587+
seenMap.set(configFile, true);
588+
589+
// Depend on all the dts files from the program
590+
if (configInfo.dtsFiles) {
591+
configInfo.dtsFiles.forEach(addDependency);
592+
}
593+
594+
// Add dependencies to all the input files from the project reference files since building them
595+
const queue = configInfo.config.projectReferences.slice();
596+
while (true) {
597+
const currentRef = queue.pop();
598+
if (!currentRef) {
599+
break;
600+
}
601+
const refConfigFile = path.resolve(
602+
instance.compiler.resolveProjectReferencePath(currentRef)
603+
);
604+
if (seenMap.has(refConfigFile)) {
605+
continue;
606+
}
607+
const refConfigInfo = instance.solutionBuilderHost.configFileInfo.get(
608+
refConfigFile
609+
);
610+
if (!refConfigInfo) {
611+
continue;
612+
}
613+
seenMap.set(refConfigFile, true);
614+
if (refConfigInfo.config) {
615+
refConfigInfo.config.fileNames.forEach(addDependency);
616+
if (refConfigInfo.config.projectReferences) {
617+
queue.push(...refConfigInfo.config.projectReferences);
618+
}
619+
}
620+
}
621+
}
622+
}
623+
547624
/**
548625
* Transpile file
549626
*/
@@ -564,6 +641,10 @@ function getTranspilationEmit(
564641
fileName
565642
});
566643

644+
addDependenciesFromSolutionBuilder(instance, fileName, file =>
645+
loaderContext.addDependency(path.resolve(file))
646+
);
647+
567648
// _module.errors is not available inside happypack - see https://github.com/TypeStrong/ts-loader/issues/336
568649
if (
569650
!instance.loaderOptions.happyPackMode &&

src/instances.ts

+64-23
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ function successfulTypeScriptInstance(
168168
transformers: {} as typescript.CustomTransformers, // this is only set temporarily, custom transformers are created further down
169169
colors,
170170
initialSetupPending: true,
171+
reportTranspileErrors: true,
171172
configFilePath,
172173
configParseResult,
173174
log
@@ -273,24 +274,14 @@ export function initializeInstance(
273274
})
274275
: instance.compiler.createProgram([], instance.compilerOptions));
275276

276-
// happypack does not have _module.errors - see https://github.com/TypeStrong/ts-loader/issues/336
277-
if (!instance.loaderOptions.happyPackMode) {
278-
const solutionErrors: WebpackError[] = getSolutionErrors(
279-
instance,
280-
loader.context
281-
);
282-
const diagnostics = program.getOptionsDiagnostics();
283-
const errors = formatErrors(
284-
diagnostics,
285-
instance.loaderOptions,
286-
instance.colors,
287-
instance.compiler,
288-
{ file: instance.configFilePath || 'tsconfig.json' },
289-
loader.context
277+
instance.transformers = getCustomTransformers(program);
278+
// Setup watch run for solution building
279+
if (instance.solutionBuilderHost) {
280+
loader._compiler.hooks.watchRun.tapAsync(
281+
'ts-loader',
282+
makeWatchRun(instance)
290283
);
291-
loader._module.errors.push(...solutionErrors, ...errors);
292284
}
293-
instance.transformers = getCustomTransformers(program);
294285
} else {
295286
if (!loader._compiler.hooks) {
296287
throw new Error(
@@ -307,7 +298,6 @@ export function initializeInstance(
307298
// If there is api available for watch, use it instead of language service
308299
instance.watchHost = makeWatchHost(
309300
getScriptRegexp(instance),
310-
instance.log,
311301
loader,
312302
instance,
313303
instance.configParseResult.projectReferences
@@ -322,7 +312,6 @@ export function initializeInstance(
322312
} else {
323313
instance.servicesHost = makeServicesHost(
324314
getScriptRegexp(instance),
325-
instance.log,
326315
loader,
327316
instance,
328317
instance.loaderOptions.experimentalFileCaching,
@@ -364,21 +353,46 @@ function getScriptRegexp(instance: TSInstance) {
364353
: /\.tsx?$/i;
365354
}
366355

356+
export function reportTranspileErrors(
357+
instance: TSInstance,
358+
loader: webpack.loader.LoaderContext
359+
) {
360+
if (!instance.reportTranspileErrors) {
361+
return;
362+
}
363+
instance.reportTranspileErrors = false;
364+
// happypack does not have _module.errors - see https://github.com/TypeStrong/ts-loader/issues/336
365+
if (!instance.loaderOptions.happyPackMode) {
366+
const solutionErrors: WebpackError[] = getSolutionErrors(
367+
instance,
368+
loader.context
369+
);
370+
const diagnostics = instance.program!.getOptionsDiagnostics();
371+
const errors = formatErrors(
372+
diagnostics,
373+
instance.loaderOptions,
374+
instance.colors,
375+
instance.compiler,
376+
{ file: instance.configFilePath || 'tsconfig.json' },
377+
loader.context
378+
);
379+
loader._module.errors.push(...solutionErrors, ...errors);
380+
}
381+
}
382+
367383
export function buildSolutionReferences(
368384
instance: TSInstance,
369-
loader: webpack.loader.LoaderContext,
370-
log: logger.Logger
385+
loader: webpack.loader.LoaderContext
371386
) {
372387
if (!supportsSolutionBuild(instance)) {
373388
return;
374389
}
375390
if (!instance.solutionBuilderHost) {
376391
// Use solution builder
377-
log.logInfo('Using SolutionBuilder api');
392+
instance.log.logInfo('Using SolutionBuilder api');
378393
const scriptRegex = getScriptRegexp(instance);
379394
instance.solutionBuilderHost = makeSolutionBuilderHost(
380395
scriptRegex,
381-
log,
382396
loader,
383397
instance
384398
);
@@ -387,8 +401,35 @@ export function buildSolutionReferences(
387401
[instance.configFilePath!],
388402
{ verbose: true }
389403
);
404+
instance.solutionBuilder!.buildReferences(instance.configFilePath!);
405+
ensureAllReferences(instance);
406+
} else {
407+
instance.solutionBuilderHost.buildReferences();
408+
}
409+
}
410+
411+
function ensureAllReferences(instance: TSInstance) {
412+
// Return result from the json without errors so that the extra errors from config are digested here
413+
const rootConfigInfo = instance.solutionBuilderHost!.configFileInfo.get(
414+
instance.configFilePath!
415+
);
416+
for (const configInfo of instance.solutionBuilderHost!.configFileInfo.values()) {
417+
if (configInfo === rootConfigInfo || !configInfo.config) {
418+
continue;
419+
}
420+
// Load all the input files
421+
configInfo.config.fileNames.forEach(file => {
422+
const resolvedFileName = path.resolve(file);
423+
const existing = instance.otherFiles.get(resolvedFileName);
424+
if (!existing) {
425+
instance.otherFiles.set(resolvedFileName, {
426+
version: 1,
427+
text: instance.compiler.sys.readFile(file),
428+
modifiedTime: instance.compiler.sys.getModifiedTime!(file)
429+
});
430+
}
431+
});
390432
}
391-
instance.solutionBuilder!.buildReferences(instance.configFilePath!);
392433
}
393434

394435
export function forEachResolvedProjectReference<T>(

0 commit comments

Comments
 (0)