lighthouse 9.5.0-dev.20220330 → 9.5.0-dev.20220402

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -38,13 +38,15 @@ async function spawnAndLog(logs, command, args) {
38
38
  spawnHandle.on('error', (error) => {
39
39
  logs.push(`ERROR: ${error.toString()}`);
40
40
  });
41
+ spawnHandle.stdout.setEncoding('utf8');
41
42
  spawnHandle.stdout.on('data', data => {
42
43
  process.stdout.write(data);
43
- logs.push(`STDOUT: ${data.toString()}`);
44
+ logs.push(`STDOUT: ${data}`);
44
45
  });
46
+ spawnHandle.stderr.setEncoding('utf8');
45
47
  spawnHandle.stderr.on('data', data => {
46
48
  process.stderr.write(data);
47
- logs.push(`STDERR: ${data.toString()}`);
49
+ logs.push(`STDERR: ${data}`);
48
50
  });
49
51
  });
50
52
  await promise;
@@ -17,8 +17,8 @@ const UIStrings = {
17
17
  /** Title of a lighthouse audit that tells a user to preload an image in order to improve their LCP time. */
18
18
  title: 'Preload Largest Contentful Paint image',
19
19
  /** Description of a lighthouse audit that tells a user to preload an image in order to improve their LCP time. */
20
- description: 'Preload the image used by ' +
21
- 'the LCP element in order to improve your LCP time. [Learn more](https://web.dev/optimize-lcp/#preload-important-resources).',
20
+ description: 'If the LCP element is dynamically added to the page, you should preload the ' +
21
+ 'image in order to improve LCP. [Learn more](https://web.dev/optimize-lcp/#preload-important-resources).',
22
22
  };
23
23
 
24
24
  const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
@@ -148,10 +148,10 @@ class ScriptTreemapDataAudit extends Audit {
148
148
  * Returns nodes where the first level of nodes are URLs.
149
149
  * Every external script has a node.
150
150
  * All inline scripts are combined into a single node.
151
- * If a script has a source map, that node will be set by makeNodeFromSourceMapData.
151
+ * If a script has a source map, that node will be created by makeScriptNode.
152
152
  *
153
153
  * Example return result:
154
- - index.html (inlines scripts)
154
+ - index.html (inline scripts)
155
155
  - main.js
156
156
  - - webpack://
157
157
  - - - react.js
@@ -165,43 +165,18 @@ class ScriptTreemapDataAudit extends Audit {
165
165
  static async makeNodes(artifacts, context) {
166
166
  /** @type {LH.Treemap.Node[]} */
167
167
  const nodes = [];
168
-
169
- let inlineScriptLength = 0;
170
- for (const script of artifacts.Scripts) {
171
- // Combine so that inline scripts show up as a single root node.
172
- if (script.url === artifacts.URL.finalUrl) {
173
- inlineScriptLength += (script.content || '').length;
174
- }
175
- }
176
- if (inlineScriptLength) {
177
- const name = artifacts.URL.finalUrl;
178
- nodes.push({
179
- name,
180
- resourceBytes: inlineScriptLength,
181
- });
182
- }
183
-
168
+ /** @type {Map<string, LH.Treemap.Node>} */
169
+ const htmlNodesByFrameId = new Map();
184
170
  const bundles = await JsBundles.request(artifacts, context);
185
171
  const duplicationByPath = await ModuleDuplication.request(artifacts, context);
186
172
 
187
173
  for (const script of artifacts.Scripts) {
188
- if (script.url === artifacts.URL.finalUrl) continue; // Handled above.
174
+ if (script.scriptLanguage !== 'JavaScript') continue;
189
175
 
190
176
  const name = script.url;
191
177
  const bundle = bundles.find(bundle => script.scriptId === bundle.script.scriptId);
192
- const scriptCoverage = /** @type {Omit<LH.Crdp.Profiler.ScriptCoverage, "url"> | undefined} */
178
+ const scriptCoverage = /** @type {LH.Artifacts['JsUsage'][string] | undefined} */
193
179
  (artifacts.JsUsage[script.scriptId]);
194
- if (!bundle && !scriptCoverage) {
195
- // No bundle and no coverage information, so simply make a single node
196
- // detailing how big the script is.
197
-
198
- nodes.push({
199
- name,
200
- resourceBytes: script.length || 0,
201
- });
202
- continue;
203
- }
204
-
205
180
  const unusedJavascriptSummary = scriptCoverage ?
206
181
  await UnusedJavaScriptSummary.request(
207
182
  {scriptId: script.scriptId, scriptCoverage, bundle}, context) :
@@ -259,7 +234,31 @@ class ScriptTreemapDataAudit extends Audit {
259
234
  };
260
235
  }
261
236
 
262
- nodes.push(node);
237
+ // If this is an inline script, place the node inside a top-level (aka depth-one) node.
238
+ // Also separate each iframe / the main page's inline scripts into their own top-level nodes.
239
+ const isInlineHtmlScript = script.startColumn || script.startLine;
240
+ if (isInlineHtmlScript) {
241
+ let htmlNode = htmlNodesByFrameId.get(script.executionContextAuxData.frameId);
242
+ if (!htmlNode) {
243
+ htmlNode = {
244
+ name,
245
+ resourceBytes: 0,
246
+ unusedBytes: undefined,
247
+ children: [],
248
+ };
249
+ htmlNodesByFrameId.set(script.executionContextAuxData.frameId, htmlNode);
250
+ nodes.push(htmlNode);
251
+ }
252
+ htmlNode.resourceBytes += node.resourceBytes;
253
+ if (node.unusedBytes) htmlNode.unusedBytes = (htmlNode.unusedBytes || 0) + node.unusedBytes;
254
+ node.name = script.content ?
255
+ '(inline) ' + script.content.trimStart().substring(0, 15) + '…' :
256
+ '(inline)';
257
+ htmlNode.children?.push(node);
258
+ } else {
259
+ // Non-inline scripts each have their own top-level node.
260
+ nodes.push(node);
261
+ }
263
262
  }
264
263
 
265
264
  return nodes;
@@ -15,7 +15,6 @@
15
15
  * @property {LH.Gatherer.GatherMode} gatherMode
16
16
  * @property {Map<string, LH.ArbitraryEqualityMap>} computedCache
17
17
  * @property {LH.Config.Settings} settings
18
- * @property {string} url
19
18
  */
20
19
 
21
20
  /** @typedef {Record<string, Promise<any>>} IntermediateArtifacts */
@@ -75,7 +74,6 @@ async function collectPhaseArtifacts(options) {
75
74
  gatherMode,
76
75
  computedCache,
77
76
  settings,
78
- url,
79
77
  } = options;
80
78
  const priorPhase = phaseToPriorPhase[phase];
81
79
  const priorPhaseArtifacts = (priorPhase && artifactState[priorPhase]) || {};
@@ -92,7 +90,6 @@ async function collectPhaseArtifacts(options) {
92
90
  : /** @type {Dependencies} */ ({});
93
91
 
94
92
  return gatherer[phase]({
95
- url,
96
93
  gatherMode,
97
94
  driver,
98
95
  baseArtifacts,
@@ -46,7 +46,6 @@ async function snapshotGather(options) {
46
46
  const artifactDefinitions = config.artifacts || [];
47
47
  const artifactState = getEmptyArtifactState();
48
48
  await collectPhaseArtifacts({
49
- url,
50
49
  phase: 'getArtifact',
51
50
  gatherMode: 'snapshot',
52
51
  driver,
@@ -37,7 +37,6 @@ async function startTimespanGather(options) {
37
37
  const artifactState = getEmptyArtifactState();
38
38
  /** @type {Omit<import('./runner-helpers.js').CollectPhaseArtifactOptions, 'phase'>} */
39
39
  const phaseOptions = {
40
- url: initialUrl,
41
40
  driver,
42
41
  artifactDefinitions,
43
42
  artifactState,
@@ -54,7 +53,6 @@ async function startTimespanGather(options) {
54
53
  return {
55
54
  async endTimespanGather() {
56
55
  const finalUrl = await driver.url();
57
- phaseOptions.url = finalUrl;
58
56
 
59
57
  const runnerOptions = {config, computedCache};
60
58
  const artifacts = await Runner.gather(
@@ -88,6 +88,7 @@ class CriConnection extends Connection {
88
88
  path: '/json/' + command,
89
89
  }, response => {
90
90
  let data = '';
91
+ response.setEncoding('utf8');
91
92
  response.on('data', chunk => {
92
93
  data += chunk;
93
94
  });
@@ -46,7 +46,8 @@ class RobotsTxt extends FRGatherer {
46
46
  });
47
47
  }
48
48
 
49
- const robotsUrl = new URL('/robots.txt', passContext.url).href;
49
+ const {finalUrl} = passContext.baseArtifacts.URL;
50
+ const robotsUrl = new URL('/robots.txt', finalUrl).href;
50
51
  await passContext.driver.fetcher.enable();
51
52
  return passContext.driver.fetcher.fetchResource(robotsUrl)
52
53
  .catch(err => ({status: null, content: null, errorMessage: err.message}));
@@ -94,8 +94,8 @@ class WebAppManifest extends FRGatherer {
94
94
  */
95
95
  getArtifact(context) {
96
96
  const driver = context.driver;
97
-
98
- return WebAppManifest.getWebAppManifest(driver.defaultSession, context.url);
97
+ const {finalUrl} = context.baseArtifacts.URL;
98
+ return WebAppManifest.getWebAppManifest(driver.defaultSession, finalUrl);
99
99
  }
100
100
  }
101
101
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lighthouse",
3
- "version": "9.5.0-dev.20220330",
3
+ "version": "9.5.0-dev.20220402",
4
4
  "description": "Automated auditing, performance metrics, and best practices for the web.",
5
5
  "main": "./lighthouse-core/index.js",
6
6
  "bin": {
@@ -89,8 +89,8 @@
89
89
  "compile-proto": "protoc --python_out=./ ./proto/lighthouse-result.proto && mv ./proto/*_pb2.py ./proto/scripts || (echo \"❌ Install protobuf ≥ 3.7.1 to compile the proto file.\" && false)",
90
90
  "build-proto-roundtrip": "mkdir -p .tmp && cross-env PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION_VERSION=2 python proto/scripts/json_roundtrip_via_proto.py",
91
91
  "static-server": "node lighthouse-cli/test/fixtures/static-server.js",
92
- "serve-dist": "cd dist && python -m SimpleHTTPServer",
93
- "serve-gh-pages": "cd dist/gh-pages && python -m SimpleHTTPServer",
92
+ "serve-dist": "cd dist && python3 -m http.server",
93
+ "serve-gh-pages": "cd dist/gh-pages && python3 -m http.server",
94
94
  "serve-treemap": "yarn serve-gh-pages",
95
95
  "serve-viewer": "yarn serve-gh-pages",
96
96
  "flow-report": "yarn build-report --flow && node ./lighthouse-core/scripts/build-test-flow-report.js"
@@ -203,7 +203,7 @@
203
203
  "robots-parser": "^3.0.0",
204
204
  "semver": "^5.3.0",
205
205
  "speedline-core": "^1.4.3",
206
- "third-party-web": "^0.12.7",
206
+ "third-party-web": "^0.15.0",
207
207
  "ws": "^7.0.0",
208
208
  "yargs": "^17.3.1",
209
209
  "yargs-parser": "^21.0.0"
@@ -1272,7 +1272,7 @@
1272
1272
  "message": "Fonts with `font-display: optional` are preloaded"
1273
1273
  },
1274
1274
  "lighthouse-core/audits/preload-lcp-image.js | description": {
1275
- "message": "Preload the image used by the LCP element in order to improve your LCP time. [Learn more](https://web.dev/optimize-lcp/#preload-important-resources)."
1275
+ "message": "If the LCP element is dynamically added to the page, you should preload the image in order to improve LCP. [Learn more](https://web.dev/optimize-lcp/#preload-important-resources)."
1276
1276
  },
1277
1277
  "lighthouse-core/audits/preload-lcp-image.js | title": {
1278
1278
  "message": "Preload Largest Contentful Paint image"
@@ -1272,7 +1272,7 @@
1272
1272
  "message": "F̂ón̂t́ŝ ẃît́ĥ `font-display: optional` ár̂é p̂ŕêĺôád̂éd̂"
1273
1273
  },
1274
1274
  "lighthouse-core/audits/preload-lcp-image.js | description": {
1275
- "message": "P̂ŕêĺôád̂ t́ĥé îḿâǵê úŝéd̂ b́ŷ t́ĥé L̂ĆP̂ él̂ém̂én̂t́ îń ôŕd̂ér̂ t́ô ím̂ṕr̂óv̂é ŷóûŕ L̂ĆP̂ t́îḿê. [Ĺêár̂ń m̂ór̂é](https://web.dev/optimize-lcp/#preload-important-resources)."
1275
+ "message": "Îf́ t̂h́ ĹĈṔ êĺêêń íŝ d́ŷńâîćĺl̂ âd́d̂éd̂ t́ô t́ĥé p̂áĝé, ŷóû śĥóûĺd̂ ṕr̂él̂óâd́ t̂h́ê ím̂áĝé îń ôŕd̂ér̂ t́ô ím̂ṕr̂óv̂é L̂ĆP̂. [Ĺêár̂ń m̂ór̂é](https://web.dev/optimize-lcp/#preload-important-resources)."
1276
1276
  },
1277
1277
  "lighthouse-core/audits/preload-lcp-image.js | title": {
1278
1278
  "message": "P̂ŕêĺôád̂ Ĺâŕĝéŝt́ Ĉón̂t́êńt̂f́ûĺ P̂áîńt̂ ím̂áĝé"
@@ -47,8 +47,6 @@ declare module Gatherer {
47
47
 
48
48
  /** The limited context interface shared between pre and post Fraggle Rock Lighthouse. */
49
49
  interface FRTransitionalContext<TDependencies extends DependencyKey = DefaultDependenciesKey> {
50
- /** The URL of the page that is currently active. Might be `about:blank` in the before phases */
51
- url: string;
52
50
  /** The gather mode Lighthouse is currently in. */
53
51
  gatherMode: GatherMode;
54
52
  /** The connection to the page being analyzed. */