Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"test": "npm run build && node --test-reporter spec --test-force-exit --test 'build/tests/**/*.test.js'",
"test:only": "npm run build && node --test-reporter spec --test-force-exit --test --test-only 'build/tests/**/*.test.js'",
"test:only:no-build": "node --test-reporter spec --test-force-exit --test --test-only 'build/tests/**/*.test.js'",
"test:update-snapshots": "npm run build && node --test-force-exit --test --test-update-snapshots 'build/tests/**/*.test.js'",
"prepare": "node --experimental-strip-types scripts/prepare.ts"
},
"files": [
Expand Down Expand Up @@ -47,7 +48,7 @@
"@types/yargs": "^17.0.33",
"@typescript-eslint/eslint-plugin": "^8.43.0",
"@typescript-eslint/parser": "^8.43.0",
"chrome-devtools-frontend": "1.0.1514545",
"chrome-devtools-frontend": "1.0.1515446",
"eslint": "^9.35.0",
"globals": "^16.4.0",
"prettier": "^3.6.2",
Expand Down
50 changes: 40 additions & 10 deletions scripts/post-build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,42 @@ function main(): void {
const i18nDir = path.join(BUILD_DIR, devtoolsFrontEndCorePath, 'i18n');
fs.mkdirSync(i18nDir, {recursive: true});
const i18nFile = path.join(i18nDir, 'i18n.js');
const i18nContent = `export const i18n = {
registerUIStrings: () => {},
getLocalizedString: (_, str) => {
// So that the string passed in gets output verbatim.
return str;
},
lockedLazyString: () => {},
getLazilyComputedLocalizedString: () => {}
};`;
const i18nContent = `
export const i18n = {
registerUIStrings: () => {},
getLocalizedString: (_, str) => {
// So that the string passed in gets output verbatim.
return str;
},
lockedLazyString: () => {},
getLazilyComputedLocalizedString: () => {},
};
// TODO(jacktfranklin): once the DocumentLatency insight does not depend on
// this method, we can remove this stub.
export const TimeUtilities = {
millisToString(x) {
const separator = '\xA0';
const formatter = new Intl.NumberFormat('en-US', {
style: 'unit',
unitDisplay: 'narrow',
minimumFractionDigits: 0,
maximumFractionDigits: 1,
unit: 'millisecond',
});
const parts = formatter.formatToParts(x);
for (const part of parts) {
if (part.type === 'literal') {
if (part.value === ' ') {
part.value = separator;
}
}
}
return parts.map(part => part.value).join('');
}
};`;
writeFile(i18nFile, i18nContent);

// Create codemirror.next mock.
Expand All @@ -94,7 +121,10 @@ function main(): void {
const rootDir = path.join(BUILD_DIR, devtoolsFrontEndCorePath, 'root');
fs.mkdirSync(rootDir, {recursive: true});
const runtimeFile = path.join(rootDir, 'Runtime.js');
const runtimeContent = 'export default {};';
const runtimeContent = `
export function getChromeVersion() { return ''; };
export const hostConfig = {};
`;
writeFile(runtimeFile, runtimeContent);

// Update protocol_client to remove:
Expand Down
4 changes: 4 additions & 0 deletions src/devtools.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,7 @@
*/

type CSSInJS = string & {_tag: 'CSS-in-JS'};
declare module '*.css.js' {
const styles: CSSInJS;
export default styles;
}
42 changes: 7 additions & 35 deletions src/trace-processing/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
* SPDX-License-Identifier: Apache-2.0
*/

import {PerformanceInsightFormatter} from '../../node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.js';
import {PerformanceTraceFormatter} from '../../node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.js';
import * as TraceEngine from '../../node_modules/chrome-devtools-frontend/front_end/models/trace/trace.js';
import {logger} from '../logger.js';
import {AgentFocus} from '../../node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/performance/AIContext.js';

const engine = TraceEngine.TraceModel.Model.createWithAllHandlers();

Expand Down Expand Up @@ -62,38 +63,9 @@ export async function parseRawTraceBuffer(
// TODO(jactkfranklin): move the formatters from DevTools to use here.
// This is a very temporary helper to output some text from the tool call to aid development.
export function insightOutput(result: TraceResult): string {
const mainNavigationId =
result.parsedTrace.data.Meta.mainFrameNavigations.at(0)?.args.data
?.navigationId;
if (!mainNavigationId) {
return '';
}

let text = '';
const insightsForNav = result.insights.get(mainNavigationId);
if (!insightsForNav) {
text += 'No Performance insights were found for this trace.';
return text;
}

const failingInsightKeys = Object.keys(insightsForNav.model).filter(
insightKey => {
const key = insightKey as keyof TraceEngine.Insights.Types.InsightModels;
const data = insightsForNav.model[key] ?? null;
return data?.state === 'fail';
},
) as Array<keyof TraceEngine.Insights.Types.InsightModels>;
logger(`Found failing Insight keys: ${failingInsightKeys.join(', ')}`);

for (const failingKey of failingInsightKeys) {
const modelData = insightsForNav.model[failingKey];
const formatter = new PerformanceInsightFormatter(
result.parsedTrace,
modelData,
);

const output = formatter.formatInsight();
text += `${output}\n`;
}
return text;
const focus = AgentFocus.full(result.parsedTrace);
const serializer = new TraceEngine.EventsSerializer.EventsSerializer();
const formatter = new PerformanceTraceFormatter(focus, serializer);
const output = formatter.formatTraceSummary();
return output;
}
43 changes: 43 additions & 0 deletions tests/trace-processing/parse.test.js.snapshot
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
exports[`Trace parsing > can format results of a trace 1`] = `
URL: https://web.dev/
Bounds: {min: 122410994891, max: 122416385853}
CPU throttling: none
Network throttling: none
Metrics (lab / observed):
- LCP: 129 ms, event: (eventKey: r-6063, ts: 122411126100)
- LCP breakdown:
- TTFB: 7.9 ms, bounds: {min: 122410996889, max: 122411004828}
- Load delay: 33.2 ms, bounds: {min: 122411004828, max: 122411037986}
- Load duration: 14.7 ms, bounds: {min: 122411037986, max: 122411052690}
- Render delay: 73.4 ms, bounds: {min: 122411052690, max: 122411126100}
- CLS: 0.00
Available insights:
- insight name: LCPBreakdown
description: Each [subpart has specific improvement strategies](https://web.dev/articles/optimize-lcp#lcp-breakdown). Ideally, most of the LCP time should be spent on loading the resources, not within delays.
relevant trace bounds: {min: 122410996889, max: 122411126100}
example question: Help me optimize my LCP score
example question: Which LCP phase was most problematic?
example question: What can I do to reduce the LCP time for this page load?
- insight name: LCPDiscovery
description: Optimize LCP by making the LCP image [discoverable](https://web.dev/articles/optimize-lcp#1_eliminate_resource_load_delay) from the HTML immediately, and [avoiding lazy-loading](https://web.dev/articles/lcp-lazy-loading)
relevant trace bounds: {min: 122411004828, max: 122411055039}
example question: Suggest fixes to reduce my LCP
example question: What can I do to reduce my LCP discovery time?
example question: Why is LCP discovery time important?
- insight name: RenderBlocking
description: Requests are blocking the page's initial render, which may delay LCP. [Deferring or inlining](https://web.dev/learn/performance/understanding-the-critical-path#render-blocking_resources) can move these network requests out of the critical path.
relevant trace bounds: {min: 122411037528, max: 122411053852}
example question: Show me the most impactful render blocking requests that I should focus on
example question: How can I reduce the number of render blocking requests?
- insight name: DocumentLatency
description: Your first network request is the most important. Reduce its latency by avoiding redirects, ensuring a fast server response, and enabling text compression.
relevant trace bounds: {min: 122410998910, max: 122411043781}
estimated metric savings: FCP 0 ms, LCP 0 ms
estimated wasted bytes: 77.1 kB
example question: How do I decrease the initial loading time of my page?
example question: Did anything slow down the request for this document?
- insight name: ThirdParties
description: 3rd party code can significantly impact load performance. [Reduce and defer loading of 3rd party code](https://web.dev/articles/optimizing-content-efficiency-loading-third-party-javascript/) to prioritize your page's content.
relevant trace bounds: {min: 122411037881, max: 122416229595}
example question: Which third parties are having the largest impact on my page performance?
`;
27 changes: 15 additions & 12 deletions tests/trace-processing/parse.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,40 @@
* SPDX-License-Identifier: Apache-2.0
*/
import {describe, it} from 'node:test';
import assert from 'assert';
import assert from 'node:assert';
import {
insightOutput,
parseRawTraceBuffer,
} from '../../src/trace-processing/parse.js';
import {loadTraceAsBuffer} from './fixtures/load.js';

describe('Trace parsing', async () => {
it.snapshot.setResolveSnapshotPath(testPath => {
// By default the snapshots go into the build directory, but we want them
// in the tests/ directory.
const correctPath = testPath?.replace('/build/tests', '/tests');
return correctPath + '.snapshot';
});

// The default serializer is JSON.stringify which outputs a very hard to read
// snapshot. So we override it to one that shows new lines literally rather
// than via `\n`.
it.snapshot.setDefaultSnapshotSerializers([String]);

it('can parse a Uint8Array from Tracing.stop())', async () => {
const rawData = loadTraceAsBuffer('basic-trace.json.gz');
const result = await parseRawTraceBuffer(rawData);
assert.ok(result?.parsedTrace);
assert.ok(result?.insights);
});

it('can format results of a trace', async () => {
it('can format results of a trace', async t => {
const rawData = loadTraceAsBuffer('web-dev-with-commit.json.gz');
const result = await parseRawTraceBuffer(rawData);
assert.ok(result?.parsedTrace);
assert.ok(result?.insights);

const output = insightOutput(result);
assert.strictEqual(
output.includes(
'The Largest Contentful Paint (LCP) time for this navigation was 129.2 ms.',
),
true,
);
assert.strictEqual(
output.includes('- fetchpriority=high should be applied: FAILED'),
true,
);
t.assert.snapshot(output);
});
});
11 changes: 11 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,20 @@
"node_modules/chrome-devtools-frontend/front_end/models/logs",
"node_modules/chrome-devtools-frontend/front_end/models/text_utils",
"node_modules/chrome-devtools-frontend/front_end/models/network_time_calculator",
"node_modules/chrome-devtools-frontend/front_end/models/crux-manager",
"node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts",
"node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts",
"node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/NetworkRequestFormatter.ts",
"node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/UnitFormatters.ts",
"node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/performance",
"node_modules/chrome-devtools-frontend/front_end/models/trace_source_maps_resolver",
"node_modules/chrome-devtools-frontend/front_end/models/emulation",
"node_modules/chrome-devtools-frontend/front_end/models/stack_trace",
"node_modules/chrome-devtools-frontend/front_end/models/bindings",
"node_modules/chrome-devtools-frontend/front_end/models/formatter",
"node_modules/chrome-devtools-frontend/front_end/models/geometry",
"node_modules/chrome-devtools-frontend/front_end/models/source_map_scopes",
"node_modules/chrome-devtools-frontend/front_end/models/workspace",
"node_modules/chrome-devtools-frontend/front_end/core/common",
"node_modules/chrome-devtools-frontend/front_end/core/sdk",
"node_modules/chrome-devtools-frontend/front_end/core/protocol_client",
Expand Down
Loading