diff --git a/packages/angular_devkit/build_angular/src/browser/specs/inline-critical-css-optimization_spec.ts b/packages/angular_devkit/build_angular/src/browser/specs/inline-critical-css-optimization_spec.ts deleted file mode 100644 index 05a9663b2409..000000000000 --- a/packages/angular_devkit/build_angular/src/browser/specs/inline-critical-css-optimization_spec.ts +++ /dev/null @@ -1,66 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import { Architect } from '@angular-devkit/architect'; -import { browserBuild, createArchitect, host } from '../../test-utils'; - -describe('Browser Builder inline critical CSS optimization', () => { - const target = { project: 'app', target: 'build' }; - const overrides = { - optimization: { - scripts: false, - styles: { - minify: true, - inlineCritical: true, - }, - fonts: false, - }, - }; - - let architect: Architect; - - beforeEach(async () => { - await host.initialize().toPromise(); - architect = (await createArchitect(host.root())).architect; - host.writeMultipleFiles({ - 'src/styles.css': ` - body { color: #000 } - `, - }); - }); - - afterEach(async () => host.restore().toPromise()); - - it('works', async () => { - const { files } = await browserBuild(architect, host, target, overrides); - const html = await files['index.html']; - expect(html).toContain( - ``, - ); - expect(html).toContain(`body{color:#000;}`); - }); - - it('works with deployUrl', async () => { - const { files } = await browserBuild(architect, host, target, { - ...overrides, - deployUrl: 'http://cdn.com/', - }); - const html = await files['index.html']; - expect(html).toContain( - ``, - ); - expect(html).toContain(`body{color:#000;}`); - }); - - it('should not inline critical css when option is disabled', async () => { - const { files } = await browserBuild(architect, host, target, { optimization: false }); - const html = await files['index.html']; - expect(html).toContain(``); - expect(html).not.toContain(`body{color:#000;}`); - }); -}); diff --git a/packages/angular_devkit/build_angular/src/browser/tests/options/inline-critical_spec.ts b/packages/angular_devkit/build_angular/src/browser/tests/options/inline-critical_spec.ts new file mode 100644 index 000000000000..99eaade9d0a8 --- /dev/null +++ b/packages/angular_devkit/build_angular/src/browser/tests/options/inline-critical_spec.ts @@ -0,0 +1,163 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import { buildWebpackBrowser } from '../../index'; +import { BASE_OPTIONS, BROWSER_BUILDER_INFO, describeBuilder } from '../setup'; + +describeBuilder(buildWebpackBrowser, BROWSER_BUILDER_INFO, (harness) => { + describe('Option: "inlineCritical"', () => { + beforeEach(async () => { + await harness.writeFile('src/styles.css', 'body { color: #000 }'); + }); + + it(`should extract critical css when 'inlineCritical' is true`, async () => { + harness.useTarget('build', { + ...BASE_OPTIONS, + optimization: { + scripts: false, + styles: { + minify: true, + inlineCritical: true, + }, + fonts: false, + }, + styles: ['src/styles.css'], + }); + + const { result } = await harness.executeOnce(); + + expect(result?.success).toBe(true); + harness + .expectFile('dist/index.html') + .content.toContain( + ``, + ); + harness.expectFile('dist/index.html').content.toContain(`body{color:#000;}`); + }); + + it(`should extract critical css when 'optimization' is unset`, async () => { + harness.useTarget('build', { + ...BASE_OPTIONS, + styles: ['src/styles.css'], + optimization: undefined, + }); + + const { result } = await harness.executeOnce(); + + expect(result?.success).toBe(true); + harness + .expectFile('dist/index.html') + .content.toContain( + ``, + ); + harness.expectFile('dist/index.html').content.toContain(`body{color:#000;}`); + }); + + it(`should extract critical css when 'optimization' is true`, async () => { + harness.useTarget('build', { + ...BASE_OPTIONS, + styles: ['src/styles.css'], + optimization: true, + }); + + const { result } = await harness.executeOnce(); + + expect(result?.success).toBe(true); + harness + .expectFile('dist/index.html') + .content.toContain( + ``, + ); + harness.expectFile('dist/index.html').content.toContain(`body{color:#000;}`); + }); + + it(`should not extract critical css when 'optimization' is false`, async () => { + harness.useTarget('build', { + ...BASE_OPTIONS, + styles: ['src/styles.css'], + optimization: false, + }); + + const { result } = await harness.executeOnce(); + + expect(result?.success).toBe(true); + harness.expectFile('dist/index.html').content.not.toContain(` { + harness.useTarget('build', { + ...BASE_OPTIONS, + styles: ['src/styles.css'], + optimization: { + scripts: false, + styles: { + minify: false, + inlineCritical: false, + }, + fonts: false, + }, + }); + + const { result } = await harness.executeOnce(); + + expect(result?.success).toBe(true); + harness.expectFile('dist/index.html').content.not.toContain(` { + harness.useTarget('build', { + ...BASE_OPTIONS, + deployUrl: 'http://cdn.com/', + optimization: { + scripts: false, + styles: { + minify: true, + inlineCritical: true, + }, + fonts: false, + }, + styles: ['src/styles.css'], + }); + + const { result } = await harness.executeOnce(); + expect(result?.success).toBe(true); + harness + .expectFile('dist/index.html') + .content.toContain( + ``, + ); + harness.expectFile('dist/index.html').content.toContain(`body{color:#000;}`); + }); + + it(`should extract critical css when using '@media all {}' and 'minify' is set to true`, async () => { + harness.useTarget('build', { + ...BASE_OPTIONS, + styles: ['src/styles.css'], + optimization: { + scripts: false, + styles: { + minify: true, + inlineCritical: true, + }, + fonts: false, + }, + }); + + await harness.writeFile('src/styles.css', '@media all { body { color: #000 } }'); + + const { result } = await harness.executeOnce(); + expect(result?.success).toBe(true); + harness + .expectFile('dist/index.html') + .content.toContain( + ``, + ); + harness.expectFile('dist/index.html').content.toContain(`body{color:#000;}`); + }); + }); +}); diff --git a/packages/angular_devkit/build_angular/src/webpack/configs/styles.ts b/packages/angular_devkit/build_angular/src/webpack/configs/styles.ts index 8d5b8132b1bd..40162eb5975c 100644 --- a/packages/angular_devkit/build_angular/src/webpack/configs/styles.ts +++ b/packages/angular_devkit/build_angular/src/webpack/configs/styles.ts @@ -287,6 +287,9 @@ export function getStylesConfig(wco: WebpackConfigOptions): webpack.Configuratio calc: false, // Disable CSS rules sorted due to several issues #20693, https://github.com/ionic-team/ionic-framework/issues/23266 and https://github.com/cssnano/cssnano/issues/1054 cssDeclarationSorter: false, + // Workaround for Critters as it doesn't work when `@media all {}` is minified to `@media {}`. + // TODO: Remove once they move to postcss. + minifyParams: !buildOptions.optimization.styles.inlineCritical, }, ], };