-
Notifications
You must be signed in to change notification settings - Fork 9.5k
/
Copy pathjs-bundles.js
120 lines (99 loc) · 3.44 KB
/
js-bundles.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
/**
* @license
* Copyright 2020 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import log from 'lighthouse-logger';
import {makeComputedArtifact} from './computed-artifact.js';
import SDK from '../lib/cdt/SDK.js';
/**
* Calculate the number of bytes contributed by each source file
* @param {LH.Artifacts.Bundle['map']} map
* @param {number} contentLength
* @param {string} content
* @return {LH.Artifacts.Bundle['sizes']}
*/
function computeGeneratedFileSizes(map, contentLength, content) {
const lines = content.split('\n');
/** @type {Record<string, number>} */
const files = {};
const totalBytes = contentLength;
let unmappedBytes = totalBytes;
// @ts-expect-error: This function is added in SDK.js. This will eventually be added to CDT.
map.computeLastGeneratedColumns();
for (const mapping of map.mappings()) {
const source = mapping.sourceURL;
const lineNum = mapping.lineNumber;
const colNum = mapping.columnNumber;
const lastColNum = mapping.lastColumnNumber;
// Webpack sometimes emits null mappings.
// https://github.com/mozilla/source-map/pull/303
if (!source) continue;
// Lines and columns are zero-based indices. Visually, lines are shown as a 1-based index.
const line = lines[lineNum];
if (line === null || line === undefined) {
const errorMessage = `${map.url()} mapping for line out of bounds: ${lineNum + 1}`;
log.error('JSBundles', errorMessage);
return {errorMessage};
}
if (colNum > line.length) {
const errorMessage =
`${map.url()} mapping for column out of bounds: ${lineNum + 1}:${colNum}`;
log.error('JSBundles', errorMessage);
return {errorMessage};
}
let mappingLength = 0;
if (lastColNum !== undefined) {
if (lastColNum > line.length) {
// eslint-disable-next-line max-len
const errorMessage =
`${map.url()} mapping for last column out of bounds: ${lineNum + 1}:${lastColNum}`;
log.error('JSBundles', errorMessage);
return {errorMessage};
}
mappingLength = lastColNum - colNum;
} else {
// Add +1 to account for the newline.
mappingLength = line.length - colNum + 1;
}
files[source] = (files[source] || 0) + mappingLength;
unmappedBytes -= mappingLength;
}
return {
files,
unmappedBytes,
totalBytes,
};
}
class JSBundles {
/**
* @param {Pick<LH.Artifacts, 'SourceMaps'|'Scripts'>} artifacts
*/
static async compute_(artifacts) {
const {SourceMaps, Scripts} = artifacts;
/** @type {LH.Artifacts.Bundle[]} */
const bundles = [];
// Collate map and script, compute file sizes.
for (const SourceMap of SourceMaps) {
if (!SourceMap.map) continue;
const {scriptId, map: rawMap} = SourceMap;
if (!rawMap.mappings) continue;
const script = Scripts.find(s => s.scriptId === scriptId);
if (!script) continue;
const compiledUrl = SourceMap.scriptUrl || 'compiled.js';
const mapUrl = SourceMap.sourceMapUrl || 'compiled.js.map';
const map = new SDK.SourceMap(compiledUrl, mapUrl, rawMap);
const sizes = computeGeneratedFileSizes(map, script.length || 0, script.content || '');
const bundle = {
rawMap,
script,
map,
sizes,
};
bundles.push(bundle);
}
return bundles;
}
}
const JSBundlesComputed = makeComputedArtifact(JSBundles, ['Scripts', 'SourceMaps']);
export {JSBundlesComputed as JSBundles};