Skip to content

Commit 74739f3

Browse files
filipesilvadond2clouds
authored andcommitted
fix(@angular/cli): fix asset watching
https://github.com/kevlened/copy-webpack-plugin is now used instead of the custom plugin, it has since implemented the features we needed. Fix angular#7521
1 parent 7fc88de commit 74739f3

File tree

10 files changed

+187
-829
lines changed

10 files changed

+187
-829
lines changed

package-lock.json

+107-796
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+2
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
"chalk": "^2.0.1",
4949
"circular-dependency-plugin": "^3.0.0",
5050
"common-tags": "^1.3.1",
51+
"copy-webpack-plugin": "^4.0.1",
5152
"core-object": "^3.1.0",
5253
"css-loader": "^0.28.1",
5354
"cssnano": "^3.10.0",
@@ -105,6 +106,7 @@
105106
"@angular/core": "^4.0.0",
106107
"@types/chalk": "^0.4.28",
107108
"@types/common-tags": "^1.2.4",
109+
"@types/copy-webpack-plugin": "^4.0.0",
108110
"@types/denodeify": "^1.2.30",
109111
"@types/express": "^4.0.32",
110112
"@types/fs-extra": "^4.0.0",

packages/@angular/cli/models/webpack-configs/common.ts

+41-6
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import * as webpack from 'webpack';
22
import * as path from 'path';
3-
import { GlobCopyWebpackPlugin } from '../../plugins/glob-copy-webpack-plugin';
3+
import * as CopyWebpackPlugin from 'copy-webpack-plugin';
44
import { NamedLazyChunksWebpackPlugin } from '../../plugins/named-lazy-chunks-webpack-plugin';
55
import { InsertConcatAssetsWebpackPlugin } from '../../plugins/insert-concat-assets-webpack-plugin';
6-
import { extraEntryParser, getOutputHashFormat } from './utils';
6+
import { extraEntryParser, getOutputHashFormat, AssetPattern } from './utils';
7+
import { isDirectory } from '../../utilities/is-directory';
78
import { WebpackConfigOptions } from '../webpack-config';
89

910
const ConcatPlugin = require('webpack-concat-plugin');
@@ -84,10 +85,44 @@ export function getCommonConfig(wco: WebpackConfigOptions) {
8485

8586
// process asset entries
8687
if (appConfig.assets) {
87-
extraPlugins.push(new GlobCopyWebpackPlugin({
88-
patterns: appConfig.assets,
89-
globOptions: { cwd: appRoot, dot: true, ignore: '**/.gitkeep' }
90-
}));
88+
const copyWebpackPluginPatterns = appConfig.assets.map((asset: string | AssetPattern) => {
89+
// Convert all string assets to object notation.
90+
asset = typeof asset === 'string' ? { glob: asset } : asset;
91+
// Add defaults.
92+
// Input is always resolved relative to the appRoot.
93+
asset.input = path.resolve(appRoot, asset.input || '');
94+
asset.output = asset.output || '';
95+
asset.glob = asset.glob || '';
96+
97+
// Ensure trailing slash.
98+
if (isDirectory(path.resolve(asset.input))) {
99+
asset.input += '/';
100+
}
101+
102+
// Convert dir patterns to globs.
103+
if (isDirectory(path.resolve(asset.input, asset.glob))) {
104+
asset.glob = asset.glob + '/**/*';
105+
}
106+
107+
return {
108+
context: asset.input,
109+
to: asset.output,
110+
from: {
111+
glob: asset.glob,
112+
dot: true
113+
}
114+
};
115+
});
116+
const copyWebpackPluginOptions = { ignore: ['.gitkeep'] };
117+
118+
const copyWebpackPluginInstance = new CopyWebpackPlugin(copyWebpackPluginPatterns,
119+
copyWebpackPluginOptions);
120+
121+
// Save options so we can use them in eject.
122+
(copyWebpackPluginInstance as any)['copyWebpackPluginPatterns'] = copyWebpackPluginPatterns;
123+
(copyWebpackPluginInstance as any)['copyWebpackPluginOptions'] = copyWebpackPluginOptions;
124+
125+
extraPlugins.push(copyWebpackPluginInstance);
91126
}
92127

93128
if (buildOptions.progress) {

packages/@angular/cli/models/webpack-configs/production.ts

+5
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ export const getProdConfig = function (wco: WebpackConfigOptions) {
5858
`);
5959
}
6060

61+
// CopyWebpackPlugin replaces GlobCopyWebpackPlugin, but AngularServiceWorkerPlugin depends
62+
// on specific behaviour from latter.
63+
// AngularServiceWorkerPlugin expects the ngsw-manifest.json to be present in the 'emit' phase
64+
// but with CopyWebpackPlugin it's only there on 'after-emit'.
65+
// So for now we keep it here, but if AngularServiceWorkerPlugin changes we remove it.
6166
extraPlugins.push(new GlobCopyWebpackPlugin({
6267
patterns: [
6368
'ngsw-manifest.json',

packages/@angular/cli/models/webpack-configs/utils.ts

+6
Original file line numberDiff line numberDiff line change
@@ -87,3 +87,9 @@ export function getOutputHashFormat(option: string, length = 20): HashFormat {
8787
/* tslint:enable:max-line-length */
8888
return hashFormats[option] || hashFormats['none'];
8989
}
90+
91+
export interface AssetPattern {
92+
glob: string;
93+
input?: string;
94+
output?: string;
95+
}

packages/@angular/cli/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
"chalk": "^2.0.1",
3737
"circular-dependency-plugin": "^3.0.0",
3838
"common-tags": "^1.3.1",
39+
"copy-webpack-plugin": "^4.0.1",
3940
"core-object": "^3.1.0",
4041
"css-loader": "^0.28.1",
4142
"cssnano": "^3.10.0",

packages/@angular/cli/plugins/glob-copy-webpack-plugin.ts

+4-16
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,21 @@ import * as fs from 'fs';
22
import * as path from 'path';
33
import * as glob from 'glob';
44
import * as denodeify from 'denodeify';
5+
import { AssetPattern } from '../models/webpack-configs/utils';
6+
import { isDirectory } from '../utilities/is-directory';
57

68
const flattenDeep = require('lodash/flattenDeep');
79
const globPromise = <any>denodeify(glob);
810
const statPromise = <any>denodeify(fs.stat);
911

10-
function isDirectory(path: string) {
11-
try {
12-
return fs.statSync(path).isDirectory();
13-
} catch (_) {
14-
return false;
15-
}
16-
}
17-
1812
interface Asset {
1913
originPath: string;
2014
destinationPath: string;
2115
relativePath: string;
2216
}
2317

24-
export interface Pattern {
25-
glob: string;
26-
input?: string;
27-
output?: string;
28-
}
29-
3018
export interface GlobCopyWebpackPluginOptions {
31-
patterns: (string | Pattern)[];
19+
patterns: (string | AssetPattern)[];
3220
globOptions: any;
3321
}
3422

@@ -79,7 +67,7 @@ export class GlobCopyWebpackPlugin {
7967

8068
compiler.plugin('emit', (compilation: any, cb: any) => {
8169
// Create an array of promises for each pattern glob
82-
const globs = patterns.map((pattern: Pattern) => new Promise((resolve, reject) =>
70+
const globs = patterns.map((pattern: AssetPattern) => new Promise((resolve, reject) =>
8371
// Individual patterns can override cwd
8472
globPromise(pattern.glob, Object.assign({}, globOptions, { cwd: pattern.input }))
8573
// Map the results onto an Asset

packages/@angular/cli/plugins/karma.ts

+3-10
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import * as glob from 'glob';
44
import * as webpack from 'webpack';
55
const webpackDevMiddleware = require('webpack-dev-middleware');
66

7-
import { Pattern } from './glob-copy-webpack-plugin';
7+
import { AssetPattern } from '../models/webpack-configs/utils';
8+
import { isDirectory } from '../utilities/is-directory';
89
import { WebpackTestConfig, WebpackTestOptions } from '../models/webpack-test-config';
910
import { KarmaWebpackThrowError } from './karma-webpack-throw-error';
1011

@@ -22,14 +23,6 @@ const getAppFromConfig = require('../utilities/app-utils').getAppFromConfig;
2223
let blocked: any[] = [];
2324
let isBlocked = false;
2425

25-
function isDirectory(path: string) {
26-
try {
27-
return fs.statSync(path).isDirectory();
28-
} catch (_) {
29-
return false;
30-
}
31-
}
32-
3326
// Add files to the Karma files array.
3427
function addKarmaFiles(files: any[], newFiles: any[], prepend = false) {
3528
const defaults = {
@@ -82,7 +75,7 @@ const init: any = (config: any, emitter: any, customFileHandlers: any) => {
8275
// Add assets. This logic is mimics the one present in GlobCopyWebpackPlugin.
8376
if (appConfig.assets) {
8477
config.proxies = config.proxies || {};
85-
appConfig.assets.forEach((pattern: Pattern) => {
78+
appConfig.assets.forEach((pattern: AssetPattern) => {
8679
// Convert all string patterns to Pattern type.
8780
pattern = typeof pattern === 'string' ? { glob: pattern } : pattern;
8881
// Add defaults.

packages/@angular/cli/tasks/eject.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ class JsonWebpackSerializer {
161161
private _pluginsReplacer(plugins: any[]) {
162162
return plugins.map(plugin => {
163163
let args = plugin.options || undefined;
164+
const serializer = (args: any) => JSON.stringify(args, (k, v) => this._replacer(k, v), 2);
164165

165166
switch (plugin.constructor) {
166167
case ProgressPlugin:
@@ -231,11 +232,17 @@ class JsonWebpackSerializer {
231232
default:
232233
if (plugin.constructor.name == 'AngularServiceWorkerPlugin') {
233234
this._addImport('@angular/service-worker/build/webpack', plugin.constructor.name);
235+
} else if (plugin['copyWebpackPluginPatterns']) {
236+
// CopyWebpackPlugin doesn't have a constructor nor save args.
237+
this.variableImports['copy-webpack-plugin'] = 'CopyWebpackPlugin';
238+
const patternsSerialized = serializer(plugin['copyWebpackPluginPatterns']);
239+
const optionsSerialized = serializer(plugin['copyWebpackPluginOptions']) || 'undefined';
240+
return `\uFF02CopyWebpackPlugin(${patternsSerialized}, ${optionsSerialized})\uFF02`;
234241
}
235242
break;
236243
}
237244

238-
const argsSerialized = JSON.stringify(args, (k, v) => this._replacer(k, v), 2) || '';
245+
const argsSerialized = serializer(args) || '';
239246
return `\uFF02${plugin.constructor.name}(${argsSerialized})\uFF02`;
240247
});
241248
}
@@ -538,6 +545,7 @@ export default Task.extend({
538545
'url-loader',
539546
'circular-dependency-plugin',
540547
'webpack-concat-plugin',
548+
'copy-webpack-plugin',
541549
].forEach((packageName: string) => {
542550
packageJson['devDependencies'][packageName] = ourPackageJson['dependencies'][packageName];
543551
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import * as fs from 'fs';
2+
3+
export function isDirectory(path: string) {
4+
try {
5+
return fs.statSync(path).isDirectory();
6+
} catch (_) {
7+
return false;
8+
}
9+
}

0 commit comments

Comments
 (0)