Skip to content

Commit

Permalink
feat(output): Adds webpack analyzer (#573)
Browse files Browse the repository at this point in the history
  • Loading branch information
jaysoo authored Mar 13, 2019
1 parent 1c66569 commit 786585a
Show file tree
Hide file tree
Showing 15 changed files with 598 additions and 65 deletions.
2 changes: 1 addition & 1 deletion apps/angular-console-e2e/src/integration/tasks.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ describe('Tasks', () => {

cy.contains('.summary .content', 'Started', { timeout: 220000 });

cy.get('.header .mat-select-trigger').click({ force: true });
cy.get('.summary .header .mat-select-trigger').click({ force: true });

cy.get('.mat-option-text')
.contains('Parsed')
Expand Down
7 changes: 2 additions & 5 deletions libs/server/src/lib/api/detailed-status-calculator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,10 +144,7 @@ export class BuildDetailedStatusCalculator

if (existsSync(statsPath)) {
const statsJson = JSON.parse(readFileSync(statsPath).toString());
nextStatus.stats = parseStats(
statsJson,
join(this.cwd, this.architectOptions.outputPath)
);
nextStatus.stats = parseStats(statsJson, this.cwd);
} else {
nextStatus.stats = calculateStatsFromChunks(nextStatus.chunks);
}
Expand Down Expand Up @@ -181,7 +178,7 @@ export class BuildDetailedStatusCalculator

progress = getNextProgress(progress, value);

if (value.indexOf('chunk') > -1 && value.indexOf('Hash:') > -1) {
if (value.indexOf('Hash:') > -1) {
buildStatus =
buildStatus !== 'build_failure' ? 'build_success' : 'build_failure';
progress = 100;
Expand Down
7 changes: 5 additions & 2 deletions libs/server/src/lib/utils/architect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@ export const SUPPORTED_KARMA_TEST_BUILDERS = [
];
export const SUPPORTED_NG_BUILD_BUILDERS = [
'@angular-devkit/build-angular:dev-server',
'@angular-devkit/build-angular:browser'
'@angular-devkit/build-angular:browser',
'@nrwl/builders:web-build',
'@nrwl/builders:web-dev-server'
];
export const SUPPORTED_NG_BUILD_BUILDERS_WITH_STATS = [
'@angular-devkit/build-angular:browser'
'@angular-devkit/build-angular:browser',
'@nrwl/builders:web-build'
];

// For some operations we need to add additional flags or configuration
Expand Down
1 change: 0 additions & 1 deletion libs/server/src/lib/utils/file-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@ export class FileUtils {

findClosestNg(d: string): string {
const dir = this.convertToWslPath(d);
console.log('normalized dir', d, dir);
if (this.directoryExists(this.joinForCommandRun(dir, 'node_modules'))) {
if (platform() === 'win32' && !this.isWsl()) {
if (this.fileExistsSync(this.joinForCommandRun(dir, 'ng.cmd'))) {
Expand Down
2 changes: 1 addition & 1 deletion libs/server/src/lib/utils/stats.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ describe('stats utils', () => {

expect(result.chunks[0].file).toEqual('runtime.a5dd35324ddfd942bef1.js');

expect(result.modulesByChunkId.length).toEqual(5);
expect(Object.keys(result.modulesByChunkId).length).toEqual(5);
expect(result.modulesByChunkId[0].length).toEqual(0);
expect(result.modulesByChunkId[1].length).toEqual(234);

Expand Down
86 changes: 55 additions & 31 deletions libs/server/src/lib/utils/stats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export function parseStats(
) {
const outputPath = stats.outputPath;
// grouped by index as id since webpack ids are sequential numbers
const modulesByChunkId: ModuleData[][] = [];
const modulesByChunkId: { [key: string]: ModuleData[] } = {};
const summary = {
assets: createSizeData(),
modules: createSizeData(),
Expand All @@ -54,7 +54,7 @@ export function parseStats(
if (asset.name.endsWith('.map')) {
return;
}
const sizes = fileSizeGetter.read(asset.name, cwd);
const sizes = fileSizeGetter.read(asset.name, outputPath);
summary.assets.parsed += sizes.parsed;
summary.assets.gzipped += sizes.gzipped;
assets.push({
Expand All @@ -63,6 +63,7 @@ export function parseStats(
sizes: sizes
});
});
const pathPrefixRegexp = new RegExp(`^(\./|${cwd}/)`);

stats.chunks.forEach((chunk: any) => {
const chunkData = getChunkData(chunk);
Expand All @@ -73,23 +74,40 @@ export function parseStats(
chunkData.sizes.gzipped = chunkSizes.gzipped;
chunks.push(chunkData);

walkModules(chunk.modules, module => {
if (module.path.startsWith('multi')) return;
if (isBundle(module)) return;
const moduleSizes = fileSizeGetter.read(module.path);

module.sizes.parsed = moduleSizes.parsed;
module.sizes.gzipped = moduleSizes.gzipped;
chunk.modules = flattenMultiModules(chunk.modules);
chunk.modules = chunk.modules.filter(
(m: Module) => !m.name.startsWith('multi')
);

summary.modules.parsed += module.sizes.parsed;
summary.modules.gzipped += module.sizes.gzipped;

if (module.isDep) {
summary.dependencies.parsed += module.sizes.parsed;
summary.dependencies.gzipped += module.sizes.gzipped;
walkModules(chunk.modules, module => {
try {
const moduleSizes = fileSizeGetter.read(module.path);

module.sizes.parsed = moduleSizes.parsed;
module.sizes.gzipped = moduleSizes.gzipped;

summary.modules.parsed += module.sizes.parsed;
summary.modules.gzipped += module.sizes.gzipped;

if (module.isDep) {
summary.dependencies.parsed += module.sizes.parsed;
summary.dependencies.gzipped += module.sizes.gzipped;
}

// Strip out cwd from module name and path.
module.path = module.name = module.path
.split(pathPrefixRegexp)
.slice(2)
.join('');

modules.push(module);
} catch (err) {
if (err.code === 'ENOENT') {
// File may not exist when read, so we ca ignore it.
} else {
throw err;
}
}

modules.push(module);
});

modulesByChunkId[chunkData.id] = modules;
Expand All @@ -116,7 +134,7 @@ export function calculateStatsFromChunks(cs: Chunk[]) {

cs.forEach((c, idx) => {
const chunkData = {
id: idx,
id: String(idx),
file: c.file,
name: c.name,
sizes: createSizeData()
Expand Down Expand Up @@ -147,7 +165,7 @@ export function calculateStatsFromChunks(cs: Chunk[]) {
/* ------------------------------------------------------------------------------------------------------------------ */

interface ChunkData {
id: number;
id: string;
name: string;
file: string;
sizes: SizeData;
Expand All @@ -159,15 +177,14 @@ interface SizeData {
}

interface Module {
id: number;
identifier: string;
name: string;
size: number;
chunks: any[];
modules: Module[];
}

interface ModuleData {
id: number;
identifier: string;
name: string;
size: number;
Expand All @@ -180,6 +197,21 @@ function createSizeData(): SizeData {
return { gzipped: 0, parsed: 0 };
}

const BUNDLED_MODULE_REGEXP = /(.*)\s+\+\s+\d+\smodules$/;

function flattenMultiModules(modules: Module[]) {
let flattened = [] as Module[];
modules.forEach(module => {
const matched = module.name.match(BUNDLED_MODULE_REGEXP);
if (matched) {
flattened = flattened.concat(module.modules);
module.name = matched[1];
}
flattened.push(module);
});
return flattened;
}

function walkModules(
modules: Module[],
visitor: (module: ModuleData) => void,
Expand All @@ -192,18 +224,14 @@ function walkModules(
const isDep = path.indexOf('node_modules') !== -1;
const sizes = createSizeData();
const mm: ModuleData = {
id: m.id,
name: m.name,
name: path,
identifier: m.identifier,
size: m.size,
path,
isDep,
sizes
};

if (isDep) {
}

if (!shouldInclude(mm)) return;
if (seen.has(mm.path)) return;

Expand All @@ -217,10 +245,6 @@ function getModulePath(m: Module) {
return m.identifier.replace(/.*!/, '').replace(/\\/g, '/');
}

function isBundle(m: ModuleData) {
return /^.* [a-zA-Z0-9]+$/.test(m.path);
}

function shouldInclude(m: ModuleData) {
return (
m.identifier.indexOf('(webpack)') === -1 &&
Expand All @@ -230,7 +254,7 @@ function shouldInclude(m: ModuleData) {

function getChunkData(chunk: any): ChunkData {
return {
id: chunk.id,
id: String(chunk.id),
name: chunk.names[0],
file: chunk.files[0],
sizes: createSizeData()
Expand Down
66 changes: 58 additions & 8 deletions libs/ui/src/lib/build-status/build-status.component.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<div class="container">
<div class="row" fxLayout="row" fxLayoutGap="20px">
<div class="row summary" fxLayout="row" fxLayoutGap="20px">
<mat-card fxFlex>
<div class="header" fxLayout="row" fxLayoutAlign="center">
<div fxFlex>
Expand All @@ -12,14 +12,16 @@ <h3>Summary</h3>
[value]="currentSizeGroup$ | async"
(selectionChange)="handleSizeGroupSelection($event)"
>
<mat-option *ngFor="let group of sizeGroups" [value]="group">{{
getSizeGroupLabel(group)
}}</mat-option>
<mat-option
*ngFor="let group of sizeGroups; trackBy: trackByString"
[value]="group"
>{{ getSizeGroupLabel(group) }}</mat-option
>
</mat-select>
<mat-icon [matTooltip]="sizeGroupHelpText$ | async">help</mat-icon>
</div>
</div>
<div class="summary grid grid-3-columns">
<div class="grid grid-3-columns">
<div>
<div class="title">Status</div>
<div class="content">
Expand Down Expand Up @@ -133,20 +135,68 @@ <h3>Problems</h3>
</div>
<ng-container *ngIf="(errors$ | async) as errors">
<div class="problem-list">
<div class="problem-list-item" *ngFor="let error of errors">
<div
class="problem-list-item"
*ngFor="let error of errors; trackBy: trackByString"
>
{{ error }}
</div>
</div>
</ng-container>
<ng-container *ngIf="(warnings$ | async) as warnings">
<div class="problem-list">
<div class="problem-list-item" *ngFor="let warning of warnings">
<div
class="problem-list-item"
*ngFor="let warning of warnings; trackBy: trackByString"
>
{{ warning }}
</div>
</div>
</ng-container>
</mat-card>
</div>
<div
class="row"
[ngClass]="'row ' + (analyzeAnimationState$ | async)"
[@growShrinkVert]="analyzeAnimationState$ | async"
>
<mat-card>
<div class="header" fxLayout="row" fxLayoutAlign="center">
<div fxFlex>
<h3>Analyze</h3>
</div>
<div class="actions" fxLayout="row" fxLayoutGap="20px">
<mat-select
matRipple
fxFlex
[value]="currentChunkId$ | async"
(selectionChange)="handleChunkFileSelection($event)"
>
<mat-option
*ngFor="let chunk of (chunks$ | async); trackBy: trackById"
[value]="chunk.id"
>
{{ chunk.file }}
</mat-option>
</mat-select>
</div>
</div>
<ng-container
*ngIf="
(viewingModules$ | async) as viewingModules;
else noViewingModules
"
>
<ui-modules-graph
[chunk]="currentChunk$ | async"
[data]="viewingModules"
></ui-modules-graph>
</ng-container>
<ng-template #noViewingModules>
<mat-icon class="pending large">more_horiz</mat-icon>
</ng-template>
</mat-card>
</div>
<div class="row">
<mat-card>
<div class="header" fxLayout="row" fxLayoutAlign="center">
Expand Down Expand Up @@ -188,7 +238,7 @@ <h3>Assets</h3>
<mat-grid-tile
class="title"
colspan="1"
*ngFor="let key of displayAssetSpeeds"
*ngFor="let key of displayAssetSpeeds; trackBy: trackByString"
>
{{ getSpeedLabel(key) }}
</mat-grid-tile>
Expand Down
1 change: 0 additions & 1 deletion libs/ui/src/lib/build-status/build-status.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,6 @@ h3 {
}

.problem-list {
white-space: pre;
width: 100%;
overflow-x: auto;

Expand Down
Loading

0 comments on commit 786585a

Please sign in to comment.