Skip to content

Commit

Permalink
fix(@angular-devkit/build-angular): ensure latest inline stylesheet d…
Browse files Browse the repository at this point in the history
…ata is used during rebuilds

Fixes: #20904
  • Loading branch information
clydin committed May 25, 2021
1 parent c44100b commit 9433bb6
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/

import { concatMap, count, take, timeout } from 'rxjs/operators';
import { buildWebpackBrowser } from '../../index';
import { InlineStyleLanguage } from '../../schema';
import { BASE_OPTIONS, BROWSER_BUILDER_INFO, describeBuilder } from '../setup';
Expand Down Expand Up @@ -106,6 +107,67 @@ describeBuilder(buildWebpackBrowser, BROWSER_BUILDER_INFO, (harness) => {
harness.expectFile('dist/main.js').content.toContain('color: green');
});
});

it('updates produced stylesheet in watch mode', async () => {
harness.useTarget('build', {
...BASE_OPTIONS,
main: 'src/main.ts',
inlineStyleLanguage: InlineStyleLanguage.Scss,
aot,
watch: true,
});

await harness.modifyFile('src/app/app.component.ts', (content) =>
content.replace(
'__STYLE_MARKER__',
'$primary-color: green;\\nh1 { color: $primary-color; }',
),
);

const buildCount = await harness
.execute()
.pipe(
timeout(30000),
concatMap(async ({ result }, index) => {
expect(result?.success).toBe(true);

switch (index) {
case 0:
harness.expectFile('dist/main.js').content.toContain('color: green');
harness.expectFile('dist/main.js').content.not.toContain('color: aqua');

await harness.modifyFile('src/app/app.component.ts', (content) =>
content.replace(
'$primary-color: green;\\nh1 { color: $primary-color; }',
'$primary-color: aqua;\\nh1 { color: $primary-color; }',
),
);
break;
case 1:
harness.expectFile('dist/main.js').content.not.toContain('color: green');
harness.expectFile('dist/main.js').content.toContain('color: aqua');

await harness.modifyFile('src/app/app.component.ts', (content) =>
content.replace(
'$primary-color: aqua;\\nh1 { color: $primary-color; }',
'$primary-color: blue;\\nh1 { color: $primary-color; }',
),
);
break;
case 2:
harness.expectFile('dist/main.js').content.not.toContain('color: green');
harness.expectFile('dist/main.js').content.not.toContain('color: aqua');
harness.expectFile('dist/main.js').content.toContain('color: blue');
break;
}
}),
take(3),
count(),
)
.toPromise();

expect(buildCount).toBe(3);
});
}
});
});
7 changes: 6 additions & 1 deletion packages/ngtools/webpack/src/ivy/host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,12 @@ export function augmentHostWithResources(
}

if (options.inlineStyleMimeType) {
const content = await resourceLoader.process(data, options.inlineStyleMimeType);
const content = await resourceLoader.process(
data,
options.inlineStyleMimeType,
context.type,
context.containingFile,
);

return { content };
}
Expand Down
36 changes: 31 additions & 5 deletions packages/ngtools/webpack/src/resource_loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,16 @@ export class WebpackResourceLoader {
filePath?: string,
data?: string,
mimeType?: string,
resourceType?: 'style' | 'template',
hash?: string,
containingFile?: string,
): Promise<CompilationOutput> {
if (!this._parentCompilation) {
throw new Error('WebpackResourceLoader cannot be used without parentCompilation');
}

// Create a special URL for reading the resource from memory
const entry = data ? 'angular-resource://' : filePath;
const entry = data ? `angular-resource:${resourceType},${hash}` : filePath;
if (!entry) {
throw new Error(`"filePath" or "data" must be specified.`);
}
Expand All @@ -116,7 +119,11 @@ export class WebpackResourceLoader {
);
}

const outputFilePath = filePath || `angular-resource-output-${this.outputPathCounter++}.css`;
const outputFilePath =
filePath ||
`${containingFile}-angular-inline--${this.outputPathCounter++}.${
resourceType === 'template' ? 'html' : 'css'
}`;
const outputOptions = {
filename: outputFilePath,
library: {
Expand Down Expand Up @@ -215,7 +222,14 @@ export class WebpackResourceLoader {
if (parent) {
parent.children = parent.children.filter((child) => child !== childCompilation);

parent.fileDependencies.addAll(childCompilation.fileDependencies);
for (const fileDependency of childCompilation.fileDependencies) {
if (data && containingFile && fileDependency.endsWith(entry)) {
// use containing file if the resource was inline
parent.fileDependencies.add(containingFile);
} else {
parent.fileDependencies.add(fileDependency);
}
}
parent.contextDependencies.addAll(childCompilation.contextDependencies);
parent.missingDependencies.addAll(childCompilation.missingDependencies);
parent.buildDependencies.addAll(childCompilation.buildDependencies);
Expand Down Expand Up @@ -298,7 +312,12 @@ export class WebpackResourceLoader {
return compilationResult.content;
}

async process(data: string, mimeType: string): Promise<string> {
async process(
data: string,
mimeType: string,
resourceType: 'template' | 'style',
containingFile?: string,
): Promise<string> {
if (data.trim().length === 0) {
return '';
}
Expand All @@ -307,7 +326,14 @@ export class WebpackResourceLoader {
let compilationResult = this.inlineCache?.get(cacheKey);

if (compilationResult === undefined) {
compilationResult = await this._compile(undefined, data, mimeType);
compilationResult = await this._compile(
undefined,
data,
mimeType,
resourceType,
cacheKey,
containingFile,
);

if (this.inlineCache && compilationResult.success) {
this.inlineCache.set(cacheKey, compilationResult);
Expand Down

0 comments on commit 9433bb6

Please sign in to comment.