Skip to content

[WIP] Use Webpack 5 and add cache option #13348

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

Closed
wants to merge 2 commits into from
Closed
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
2 changes: 1 addition & 1 deletion packages/angular_devkit/build_angular/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"stylus-loader": "3.0.2",
"tree-kill": "1.2.1",
"terser-webpack-plugin": "1.2.0",
"webpack": "4.28.2",
"webpack": "5.0.0-alpha.3",
"webpack-dev-middleware": "3.4.0",
"webpack-dev-server": "3.1.14",
"webpack-merge": "4.1.5",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ export interface BuildOptions {
statsJson: boolean;
forkTypeChecker: boolean;
profile?: boolean;
persistentCache?: boolean;
clearPersistentCache?: boolean;

main: string;
index: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,9 @@ export function getBrowserConfig(wco: WebpackConfigOptions) {
name: 'vendor',
chunks: 'initial',
enforce: true,
test: (module: { nameForCondition?: Function }, chunks: Array<{ name: string }>) => {
test: (module: any) => {
const moduleName = module.nameForCondition ? module.nameForCondition() : '';
const chunks = module.getChunks();

return /[\\/]node_modules[\\/]/.test(moduleName)
&& !chunks.some(({ name }) => name === 'polyfills'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
*/
import * as CopyWebpackPlugin from 'copy-webpack-plugin';
import * as path from 'path';
import { HashedModuleIdsPlugin, debug } from 'webpack';
import * as fs from 'fs';
import { ids, debug } from 'webpack';
import { AssetPatternObject } from '../../../browser/schema';
import { BundleBudgetPlugin } from '../../plugins/bundle-budget';
import { CleanCssWebpackPlugin } from '../../plugins/cleancss-webpack-plugin';
Expand Down Expand Up @@ -154,15 +155,47 @@ export function getCommonConfig(wco: WebpackConfigOptions) {
}

if (buildOptions.showCircularDependencies) {
extraPlugins.push(new CircularDependencyPlugin({
exclude: /([\\\/]node_modules[\\\/])|(ngfactory\.js$)/,
}));
// Breaks with Webpack 5:
// Error: module property was removed from Dependency (use compilation.moduleGraph.getModule(dependency) instead)
// extraPlugins.push(new CircularDependencyPlugin({
// exclude: /([\\\/]node_modules[\\\/])|(ngfactory\.js$)/,
// }));
}

if (buildOptions.statsJson) {
extraPlugins.push(new StatsPlugin('stats.json', 'verbose'));
}

const cacheDirectory = path.resolve(nodeModules, '.cache');
const cacheName = 'angular-devkit_build-angular';
if (buildOptions.clearPersistentCache) {
console.log('clearing cache')
const cacheFile = path.resolve(cacheDirectory, `${cacheName}.pack`);
if (fs.existsSync(cacheFile)) {
fs.unlinkSync(cacheFile);
}
}

let cache: any = false;
if (buildOptions.persistentCache) {
cache = {
type: 'filesystem',
cacheDirectory,
name: cacheName,
loglevel: 'warning',
// Need to calculate version from: dependencies plus configuration.
// For dependencies we can use package-lock.json / yarn.lock.
// For config maybe we can just hash our own config option status instead of webpacks whole
// configuration object.
version: 'abcd',
// This controls when the cache is written.
// Default is 'idle' and means cache is written when idle.
// Not sure how to detect if cache is still being written so waiting 20s instead in tests.
store: 'idle',
}

}

let sourceMapUseRule;
if ((scriptsSourceMap || stylesSourceMap) && vendorSourceMap) {
sourceMapUseRule = {
Expand Down Expand Up @@ -266,6 +299,7 @@ export function getCommonConfig(wco: WebpackConfigOptions) {
? 'production'
: 'development',
devtool: false,
cache,
resolve: {
extensions: ['.ts', '.tsx', '.mjs', '.js'],
symlinks: !buildOptions.preserveSymlinks,
Expand Down Expand Up @@ -306,6 +340,8 @@ export function getCommonConfig(wco: WebpackConfigOptions) {
// Mark files inside `@angular/core` as using SystemJS style dynamic imports.
// Removing this will cause deprecation warnings to appear.
test: /[\/\\]@angular[\/\\]core[\/\\].+\.js$/,
// Seems like this doesn't hide the warning anymore. Might need to use a filter on the
// warnings instead.
parser: { system: true },
},
{
Expand All @@ -323,7 +359,7 @@ export function getCommonConfig(wco: WebpackConfigOptions) {
optimization: {
noEmitOnErrors: true,
minimizer: [
new HashedModuleIdsPlugin(),
new ids.HashedModuleIdsPlugin(),
// TODO: check with Mike what this feature needs.
new BundleBudgetPlugin({ budgets: buildOptions.budgets }),
...extraMinimizers,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,13 @@ export function statsWarningsToString(json: any, statsConfig: any) {
const rs = (x: string) => colors ? reset(x) : x;
const y = (x: string) => colors ? bold(yellow(x)) : x;

return rs('\n' + json.warnings.map((warning: any) => y(`WARNING in ${warning}`)).join('\n\n'));
return rs('\n' + json.warnings.map((warning: any) => y(`WARNING in ${warning.message}`)).join('\n\n'));
}

export function statsErrorsToString(json: any, statsConfig: any) {
const colors = statsConfig.colors;
const rs = (x: string) => colors ? reset(x) : x;
const r = (x: string) => colors ? bold(red(x)) : x;

return rs('\n' + json.errors.map((error: any) => r(`ERROR in ${error}`)).join('\n'));
return rs('\n' + json.errors.map((error: any) => r(`ERROR in ${error.message}`)).join('\n'));
}
10 changes: 10 additions & 0 deletions packages/angular_devkit/build_angular/src/browser/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,16 @@
"type": "boolean",
"description": "Output profile events for Chrome profiler.",
"default": false
},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: let’s remember to add these in the CLI schema

"persistentCache": {
"type": "boolean",
"description": "**EXPERIMENTAL** Use a persistent cache between builds.",
"default": false
},
"clearPersistentCache": {
"type": "boolean",
"description": "**EXPERIMENTAL** Clear the persistent cache.",
"default": false
}
},
"additionalProperties": false,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* @license
* Copyright Google Inc. 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 { DefaultTimeout, TestLogger, runTargetSpec } from '@angular-devkit/architect/testing';
import { concatMap, delay, tap } from 'rxjs/operators';
import { browserTargetSpec, host } from '../utils';


describe('Browser Builder cache test', () => {
beforeEach(done => host.initialize().toPromise().then(done, done.fail));
afterEach(done => host.restore().toPromise().then(done, done.fail));

it('works', (done) => {
const logger = new TestLogger('persistent-cache');
const overrides = { persistentCache: true };
const clearCacheOverrides = { clearPersistentCache: true, ...overrides };
runTargetSpec(host, browserTargetSpec, clearCacheOverrides, DefaultTimeout, logger).pipe(
// Wait 20s for cache to be written. See note about cache 'store' property on common.ts.
delay(20 * 1000),
concatMap(() => runTargetSpec(host, browserTargetSpec, overrides, DefaultTimeout, logger)),
// There are 5 chunks total, so the compiler should say nothing changed.
tap(() => expect(logger.includes('5 unchanged chunks')).toBe(true)),
).toPromise().then(done, done.fail);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,17 @@ import { runTargetSpec } from '@angular-devkit/architect/testing';
import { join, normalize } from '@angular-devkit/core';
import { tap } from 'rxjs/operators';
import { browserTargetSpec, host } from '../utils';
import { createConsoleLogger } from '@angular-devkit/core/node';


describe('Browser Builder basic test', () => {
fdescribe('Browser Builder basic test', () => {
const outputPath = normalize('dist');

beforeEach(done => host.initialize().toPromise().then(done, done.fail));
afterEach(done => host.restore().toPromise().then(done, done.fail));

it('works', (done) => {
runTargetSpec(host, browserTargetSpec).pipe(
runTargetSpec(host, browserTargetSpec, {}, 10000000, createConsoleLogger()).pipe(
tap((buildEvent) => expect(buildEvent.success).toBe(true)),
tap(() => {
// Default files should be in outputPath.
Expand Down
2 changes: 1 addition & 1 deletion packages/angular_devkit/build_webpack/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"webpack-dev-server": "^3.1.4"
},
"devDependencies": {
"webpack": "^4.6.0",
"webpack": "5.0.0-alpha.3",
"webpack-dev-server": "^3.1.4"
}
}
2 changes: 1 addition & 1 deletion packages/ngtools/webpack/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,6 @@
"@angular/compiler": "^7.2.0-rc.0",
"@angular/compiler-cli": "^7.2.0-rc.0",
"typescript": "~3.2.2",
"webpack": "^4.0.0"
"webpack": "5.0.0-alpha.3"
}
}
Loading