Skip to content

Commit 64ff2d6

Browse files
committed
Update analyzer
1 parent 747d0f7 commit 64ff2d6

34 files changed

+17295
-57
lines changed

package-lock.json

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,15 @@
1717
"url": "https://github.com/dojo/cli-build-app.git"
1818
},
1919
"scripts": {
20+
"webpack-bundle-analyzer": "cd src/webpack-bundle-analyzer/client && npm run build:dist && shx mv output/dist ../../../dist/release/webpack-bundle-analyzer/reporter && cd ../../../",
2021
"artifact:build:pwa": "cd test-app && npm run build:dist:pwa:evergreen && shx mv output/dist output/dist-pwa-evergreen && npm run build:dev:pwa:evergreen && shx mv output/dev output/dev-pwa-evergreen && npm run build:dist:pwa && shx mv output/dist output/dist-pwa && npm run build:dev:pwa && shx mv output/dev output/dev-pwa",
2122
"artifact:build:app": "cd test-app && npm run build:dist:evergreen && shx mv output/dist output/dist-app-evergreen && npm run build:dev:evergreen && shx mv output/dev output/dev-app-evergreen && npm run build:dist && shx mv output/dist output/dist-app && npm run build:dev && shx mv output/dev output/dev-app",
2223
"artifact:install": "cd test-app && shx rm -rf node_modules && npm i && npm run install-build-app",
2324
"artifact:package": "cd dist && npm pack -q ./release && shx mv dojo-cli-build-app-* dojo-cli-build-app.tgz && cd ..",
24-
"build:static:dev": "cpx \"{src,tests}/**/*.{d.ts,html,md,json,js,css}\" dist/dev",
25-
"build:static:release": "cpx \"src/**/*.{d.ts,json}\" dist/release",
25+
"build:static:dev": "cpx \"{src,tests}/**/*.{d.ts,html,md,json,js,css}\" dist/dev && rimraf dist/dev/src/webpack-bundle-analyzer/client",
26+
"build:static:release": "cpx \"src/**/*.{d.ts,json}\" dist/release && rimraf dist/release/webpack-bundle-analyzer/client",
2627
"build:cjs": "tsc",
27-
"build": "npm-run-all -p build:** -s dojo-package",
28+
"build": "npm-run-all -p build:** -s webpack-bundle-analyzer -s dojo-package && rimraf src/webpack-bundle-analyzer/client/node_modules",
2829
"clean": "rimraf dist coverage output test-app/output test-app/node_modules",
2930
"dojo-package": "dojo-package",
3031
"dojo-release": "dojo-release",
@@ -68,6 +69,7 @@
6869
"@types/execa": "0.8.0",
6970
"@types/express": "4.11.0",
7071
"@types/express-serve-static-core": "4.16.4",
72+
"@types/fs-extra": "^8.1.0",
7173
"@types/globby": "6.1.0",
7274
"@types/gzip-size": "4.0.0",
7375
"@types/html-webpack-plugin": "3.2.0",
@@ -79,6 +81,7 @@
7981
"@types/log-update": "2.0.0",
8082
"@types/mini-css-extract-plugin": "0.2.0",
8183
"@types/minimatch": "3.0.3",
84+
"@types/mkdirp": "0.5.2",
8285
"@types/mockery": "1.4.29",
8386
"@types/node": "~9.6.5",
8487
"@types/ora": "1.3.1",
@@ -121,6 +124,7 @@
121124
"extra-watch-webpack-plugin": "1.0.3",
122125
"file-loader": "2.0.0",
123126
"globby": "7.1.1",
127+
"mkdirp": "0.5.1",
124128
"gzip-size": "4.1.0",
125129
"html-loader": "0.5.5",
126130
"html-webpack-plugin": "3.2.0",

src/dist.config.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import BuildTimeRender from '@dojo/webpack-contrib/build-time-render/BuildTimeRender';
22
import ExternalLoaderPlugin from '@dojo/webpack-contrib/external-loader-plugin/ExternalLoaderPlugin';
3-
import BundleAnalyzerPlugin from '@dojo/webpack-contrib/webpack-bundle-analyzer/BundleAnalyzerPlugin';
43
import ServiceWorkerPlugin, {
54
ServiceWorkerOptions
65
} from '@dojo/webpack-contrib/service-worker-plugin/ServiceWorkerPlugin';
@@ -62,13 +61,6 @@ function webpackConfig(args: any): webpack.Configuration {
6261
config.plugins = [
6362
...plugins!,
6463
assetsDirExists && new CopyWebpackPlugin([{ from: assetsDir, to: path.join(outputPath, 'assets') }]),
65-
new BundleAnalyzerPlugin({
66-
analyzerMode: 'static',
67-
openAnalyzer: false,
68-
generateStatsFile: true,
69-
reportFilename: '../info/report.html',
70-
statsFilename: '../info/stats.json'
71-
}),
7264
new HtmlWebpackPlugin({
7365
base,
7466
inject: true,

src/logger.ts

Lines changed: 48 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import * as fs from 'fs';
21
import * as path from 'path';
32
import * as logUpdate from 'log-update';
43
import * as logSymbols from 'log-symbols';
5-
import * as gzipSize from 'gzip-size';
64
import * as typescript from 'typescript';
75
import * as jsonFile from 'jsonfile';
6+
import BundleAnalyzer from './webpack-bundle-analyzer/BundleAnalyzer';
87
import chalk from 'chalk';
8+
import { findLargestGroup } from './webpack-bundle-analyzer/parseUtils';
99

1010
const pkgDir = require('pkg-dir');
1111
const columns = require('cli-columns');
@@ -15,54 +15,64 @@ const version = jsonFile.readFileSync(path.join(pkgDir.sync(__dirname), 'package
1515
export default function logger(stats: any, config: any, runningMessage: string = '', args: any = {}): boolean {
1616
const singleConfig = Array.isArray(config) ? config[0] : config;
1717
const outputPath = singleConfig.output.path;
18-
const manifestPath = path.join(outputPath, 'manifest.json');
19-
let assets: undefined | string[];
18+
const loggerStats = stats.toJson({ warningsFilter });
2019
let chunks: undefined | string[];
21-
if (fs.existsSync(manifestPath)) {
22-
const manifestContent = JSON.parse(fs.readFileSync(path.join(outputPath, 'manifest.json'), 'utf8'));
23-
assets = Object.keys(manifestContent).map((item) => {
24-
const assetName = manifestContent[item];
25-
const filePath = path.join(outputPath, assetName);
26-
if (fs.existsSync(filePath)) {
27-
if (args.mode === 'dev' || args.mode === 'test') {
28-
return `${assetName}`;
29-
} else {
30-
const fileStats = fs.statSync(filePath);
31-
const size = (fileStats.size / 1000).toFixed(2);
32-
const assetInfo = `${assetName} ${chalk.yellow(`(${size}kb)`)}`;
33-
const content = fs.readFileSync(filePath, 'utf8');
34-
const compressedSize = (gzipSize.sync(content) / 1000).toFixed(2);
35-
return `${assetInfo} / ${chalk.blue(`(${compressedSize}kb gz)`)}`;
36-
}
37-
}
38-
return '';
39-
});
4020

41-
chunks = (Array.isArray(config)
42-
? stats.children.reduce((chunks: any[], current: any) => [...chunks, ...current.chunks], [])
43-
: stats.chunks
44-
).map((chunk: any) => `${chunk.names[0]}`);
21+
let chunkMap: { [chunk: string]: any };
22+
if (args.mode === 'dist') {
23+
chunkMap = new BundleAnalyzer({
24+
analyzerMode: 'static',
25+
openAnalyzer: false,
26+
generateStatsFile: true,
27+
reportFilename: '../info/report.html',
28+
statsFilename: '../info/stats.json'
29+
}).analyze(stats, config);
4530
}
31+
chunks = (Array.isArray(config)
32+
? loggerStats.children.reduce((chunks: any[], current: any) => [...chunks, ...current.chunks], [])
33+
: loggerStats.chunks
34+
).map((chunk: any) => {
35+
const chunkName: string = chunk.names[0];
36+
if (!chunkMap) {
37+
return chunkName;
38+
} else {
39+
const chunkStats = chunkMap[chunkName];
40+
const size = chunkStats && (chunkStats.parsedSize || chunkStats.statSize);
41+
const gzipSize = chunkStats && chunkStats.gzipSize;
42+
43+
const chunkInfo = `${chunkName} ${chalk.yellow(`(${size}kb)`)}${
44+
gzipSize ? `/ ${chalk.blue(`(${gzipSize}kb gz)`)}` : ''
45+
}`;
46+
47+
if (size > 1000) {
48+
const largestGroup = findLargestGroup(chunkStats, 'node_modules');
49+
if (largestGroup) {
50+
return `${chunkInfo}\nDependency: ${largestGroup.label} is ${largestGroup.statSize}kb`;
51+
}
52+
}
53+
return chunkInfo;
54+
}
55+
});
4656

4757
let errors = '';
4858
let warnings = '';
4959
let chunkAndAssetLog = '';
5060
let signOff = chalk.green('The build completed successfully.');
5161

52-
if (stats.warnings.length) {
62+
if (loggerStats.warnings.length) {
5363
signOff = chalk.yellow('The build completed with warnings.');
5464
warnings = `
5565
${chalk.yellow('warnings:')}${chalk.gray(
56-
stats.warnings.reduce((warnings: string, warning: string) => `${warnings}\n${stripAnsi(warning)}`, '')
66+
loggerStats.warnings.reduce((warnings: string, warning: string) => `${warnings}\n${stripAnsi(warning)}`, '')
5767
)}
5868
`;
5969
}
6070

61-
if (stats.errors.length) {
71+
if (loggerStats.errors.length) {
6272
signOff = chalk.red('The build completed with errors.');
6373
errors = `
6474
${chalk.yellow('errors:')}${chalk.red(
65-
stats.errors.reduce((errors: string, error: string) => `${errors}\n${stripAnsi(error)}`, '')
75+
loggerStats.errors.reduce((errors: string, error: string) => `${errors}\n${stripAnsi(error)}`, '')
6676
)}
6777
`;
6878
}
@@ -76,18 +86,12 @@ ${chalk.yellow('errors:')}${chalk.red(
7686
${columns(chunks)}`;
7787
}
7888

79-
if (assets) {
80-
chunkAndAssetLog = `${chunkAndAssetLog}
81-
${chalk.yellow('assets:')}
82-
${columns(assets)}`;
83-
}
84-
8589
logUpdate(`
8690
${logSymbols.info} cli-build-app: ${version}
8791
${logSymbols.info} typescript: ${typescript.version}
88-
${logSymbols.success} hash: ${stats.hash}
89-
${logSymbols.error} errors: ${stats.errors.length}
90-
${logSymbols.warning} warnings: ${stats.warnings.length}
92+
${logSymbols.success} hash: ${loggerStats.hash}
93+
${logSymbols.error} errors: ${loggerStats.errors.length}
94+
${logSymbols.warning} warnings: ${loggerStats.warnings.length}
9195
${errors}${warnings}
9296
${chunkAndAssetLog}
9397
${chalk.yellow(`output at: ${chalk.cyan(chalk.underline(`file:///${outputPath}`))}`)}
@@ -96,3 +100,7 @@ ${signOff}
96100
`);
97101
return !!errors;
98102
}
103+
104+
function warningsFilter(warning: string) {
105+
return warning.includes('[mini-css-extract-plugin]\nConflicting order between');
106+
}

src/main.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ function build(config: webpack.Configuration, args: any) {
9494
}
9595
if (stats) {
9696
const runningMessage = args.serve ? `Listening on port ${args.port}...` : '';
97-
const hasErrors = logger(stats.toJson({ warningsFilter }), config, runningMessage, args);
97+
const hasErrors = logger(stats, config, runningMessage, args);
9898
if (hasErrors) {
9999
reject({});
100100
return;
@@ -139,7 +139,7 @@ async function fileWatch(config: webpack.Configuration, args: any) {
139139
args.port
140140
}\nPlease note the serve option is not intended to be used to serve applications in production.`
141141
: 'watching...';
142-
logger(stats.toJson({ warningsFilter }), config, runningMessage, args);
142+
logger(stats, config, runningMessage, args);
143143
}
144144
resolve(compiler);
145145
});
@@ -281,10 +281,6 @@ async function serve(config: webpack.Configuration, args: any) {
281281
});
282282
}
283283

284-
function warningsFilter(warning: string) {
285-
return warning.includes('[mini-css-extract-plugin]\nConflicting order between');
286-
}
287-
288284
const command: Command = {
289285
group: 'build',
290286
name: 'app',
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import * as path from 'path';
2+
import * as fs from 'fs';
3+
import * as mkdir from 'mkdirp';
4+
import * as viewer from './viewer';
5+
const bfj = require('bfj');
6+
7+
export interface BundleAnalyzerOptions {
8+
reportFilename: string;
9+
generateStatsFile: boolean;
10+
statsFilename: string;
11+
statsOptions: string | null;
12+
analyzerMode: string;
13+
openAnalyzer: boolean;
14+
excludeBundles?: string;
15+
}
16+
17+
export default class BundleAnalyzer {
18+
private opts: BundleAnalyzerOptions;
19+
private _outputDirectory!: string;
20+
private _outputPath!: string;
21+
private _webpackOutputPath!: string;
22+
23+
constructor(opts: Partial<BundleAnalyzerOptions>) {
24+
this.opts = {
25+
reportFilename: 'report.html',
26+
generateStatsFile: false,
27+
statsFilename: 'stats.json',
28+
statsOptions: null,
29+
analyzerMode: '',
30+
openAnalyzer: false,
31+
...opts
32+
};
33+
}
34+
35+
analyze(stats: any, config: any) {
36+
const singleConfig = Array.isArray(config) ? config[0] : config;
37+
this._webpackOutputPath = singleConfig.output.path;
38+
this._outputPath = path.resolve(singleConfig.output.path, this.opts.statsFilename);
39+
this._outputDirectory = path.dirname(this._outputPath);
40+
stats = stats.toJson(this.opts.statsOptions);
41+
stats = this.updateStatsHash(stats);
42+
if (this.opts.generateStatsFile) {
43+
this.generateStatsFile(stats);
44+
}
45+
return this.generateStaticReport(stats);
46+
}
47+
48+
updateStatsHash(stats: any): any {
49+
try {
50+
const manifest = JSON.parse(fs.readFileSync(path.join(this._webpackOutputPath, 'manifest.json'), 'utf8'));
51+
const originalManifest = JSON.parse(
52+
fs.readFileSync(path.join(this._outputDirectory, 'manifest.original.json'), 'utf8')
53+
);
54+
let updatedStats = JSON.stringify(stats);
55+
Object.keys(manifest).forEach((key) => {
56+
if (originalManifest[key]) {
57+
updatedStats = updatedStats.replace(new RegExp(originalManifest[key], 'g'), manifest[key]);
58+
}
59+
});
60+
return JSON.parse(updatedStats);
61+
} catch (e) {
62+
return stats;
63+
}
64+
}
65+
66+
async generateStatsFile(stats: any) {
67+
mkdir.sync(this._outputDirectory);
68+
69+
try {
70+
await bfj.write(this._outputPath, stats, {
71+
promises: 'ignore',
72+
buffers: 'ignore',
73+
maps: 'ignore',
74+
iterables: 'ignore',
75+
circular: 'ignore'
76+
});
77+
} catch {}
78+
}
79+
80+
generateStaticReport(stats: any) {
81+
return viewer.generateReportData(stats, {
82+
reportFilename: path.resolve(this._webpackOutputPath, this.opts.reportFilename),
83+
bundleDir: this._webpackOutputPath,
84+
excludeBundle: this.opts.excludeBundles
85+
});
86+
}
87+
}

0 commit comments

Comments
 (0)