Skip to content

fix(@ngtools/webpack): add local dts file as dependencies #16992

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,41 @@ describe('Browser Builder rebuilds', () => {
.toPromise();
});

it('rebuilds on transitive non node package DTS file changes', async () => {
host.writeMultipleFiles({
'src/interface1.d.ts': `
import { Interface2 } from './interface2';
export interface Interface1 extends Interface2 { }
`,
'src/interface2.d.ts': `
import { Interface3 } from './interface3';
export interface Interface2 extends Interface3 { }
`,
'src/interface3.d.ts': `export interface Interface3 { nbr: number; }`,
});
host.appendToFile('src/main.ts', `
import { Interface1 } from './interface1';
const something: Interface1 = { nbr: 43 };
`);

const overrides = { watch: true };
const run = await architect.scheduleTarget(target, overrides);
let buildNumber = 0;
await run.output
.pipe(
debounceTime(rebuildDebounceTime),
tap(buildEvent => expect(buildEvent.success).toBe(true)),
tap(() => {
buildNumber++;
if (buildNumber === 1) {
host.appendToFile('src/interface3.d.ts', 'export declare type MyType = string;');
}
}),
take(2),
)
.toPromise();
});

it('rebuilds after errors in JIT', async () => {
const origContent = virtualFs.fileBufferToString(
host.scopedSync().read(normalize('src/app/app.component.ts')),
Expand Down
50 changes: 41 additions & 9 deletions packages/ngtools/webpack/src/angular_compiler_plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -385,13 +385,44 @@ export class AngularCompilerPlugin {
}

const newTsProgram = this._getTsProgram();
if (oldTsProgram && newTsProgram) {
const newProgramSourceFiles = newTsProgram?.getSourceFiles();
const localDtsFiles = new Set(
newProgramSourceFiles?.filter(
f => f.isDeclarationFile && !this._nodeModulesRegExp.test(f.fileName),
)
.map(f => this._compilerHost.denormalizePath(f.fileName)),
);

if (!oldTsProgram) {
// Add all non node package dts files as depedencies when not having an old program
for (const dts of localDtsFiles) {
this._typeDeps.add(dts);
}
} else if (oldTsProgram && newProgramSourceFiles) {
// The invalidation should only happen if we have an old program
// as otherwise we will invalidate all the sourcefiles.
const oldFiles = new Set(oldTsProgram.getSourceFiles().map(sf => sf.fileName));
const newFiles = newTsProgram.getSourceFiles().filter(sf => !oldFiles.has(sf.fileName));
for (const newFile of newFiles) {
this._compilerHost.invalidate(newFile.fileName);
const newProgramFiles = new Set(newProgramSourceFiles.map(sf => sf.fileName));

for (const dependency of this._typeDeps) {
// Remove type dependencies of no longer existing files
if (!newProgramFiles.has(forwardSlashPath(dependency))) {
this._typeDeps.delete(dependency);
}
}

for (const fileName of newProgramFiles) {
if (oldFiles.has(fileName)) {
continue;
}

this._compilerHost.invalidate(fileName);

const denormalizedFileName = this._compilerHost.denormalizePath(fileName);
if (localDtsFiles.has(denormalizedFileName)) {
// Add new dts file as a type depedency
this._typeDeps.add(denormalizedFileName);
}
}
}

Expand Down Expand Up @@ -630,8 +661,7 @@ export class AngularCompilerPlugin {

// This function removes a source file name and all its dependencies from the set.
const removeSourceFile = (fileName: string, originalModule = false) => {
if (unusedSourceFileNames.has(fileName)
|| (originalModule && typeDepFileNames.has(fileName))) {
if (unusedSourceFileNames.has(fileName) || (originalModule && typeDepFileNames.has(fileName))) {
unusedSourceFileNames.delete(fileName);
if (originalModule) {
typeDepFileNames.delete(fileName);
Expand All @@ -649,7 +679,7 @@ export class AngularCompilerPlugin {
compilation.warnings.push(
`${fileName} is part of the TypeScript compilation but it's unused.\n` +
`Add only entry points to the 'files' or 'include' properties in your tsconfig.`,
);
);
this._unusedFiles.add(fileName);
// Remove the truly unused from the type dep list.
typeDepFileNames.delete(fileName);
Expand All @@ -659,7 +689,9 @@ export class AngularCompilerPlugin {
// These are the TS files that weren't part of the compilation modules, aren't unused, but were
// part of the TS original source list.
// Next build we add them to the TS entry points so that they trigger rebuilds.
this._typeDeps = typeDepFileNames;
for (const fileName of typeDepFileNames) {
this._typeDeps.add(fileName);
}
}

// Registration hook for webpack plugin.
Expand Down Expand Up @@ -1233,7 +1265,7 @@ export class AngularCompilerPlugin {

for (const resource of resourceImports) {
for (const dep of this.getResourceDependencies(
this._compilerHost.denormalizePath(resource))) {
this._compilerHost.denormalizePath(resource))) {
resourceDependencies.push(dep);
}
}
Expand Down