Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

core(gather): new computed artifact, js-bundles #10078

Merged
merged 60 commits into from
Jan 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
6d27caa
initial
connorjclark Dec 7, 2019
f9408f3
count lines
connorjclark Dec 7, 2019
fa07a80
mapping test
connorjclark Dec 7, 2019
7ad9389
do not pollute global array
connorjclark Dec 7, 2019
2f80d5a
remove hacky globalThis.cdt
connorjclark Dec 7, 2019
122f2a2
ts
connorjclark Dec 7, 2019
808736d
Common.console
connorjclark Dec 7, 2019
dff8358
errors
connorjclark Dec 7, 2019
4a9127d
delete some stuff not needed
connorjclark Dec 7, 2019
0d73a8b
reduce code
connorjclark Dec 7, 2019
43da869
tmp
connorjclark Dec 7, 2019
ed4bb21
remove ParsedURL
connorjclark Dec 7, 2019
7d50227
nul
connorjclark Dec 7, 2019
9f47880
remove unused js
connorjclark Dec 9, 2019
47e8a41
comment
connorjclark Dec 11, 2019
73eca7b
move SDK up b/c global pollution is gone
connorjclark Dec 11, 2019
3333c4f
add sections to source map type
connorjclark Dec 11, 2019
0b655b6
better cdt lib gen
connorjclark Dec 11, 2019
7129ef9
trim unneeded code
connorjclark Dec 11, 2019
5d17268
rm some ts ignores
connorjclark Dec 11, 2019
6480198
update frontend
connorjclark Dec 11, 2019
e6fde71
semver
connorjclark Dec 11, 2019
caa269b
comments
connorjclark Dec 11, 2019
943e19c
deletionBraceCount
connorjclark Dec 11, 2019
52796db
no unshift
connorjclark Dec 11, 2019
ecb6260
no index
connorjclark Dec 11, 2019
e3b8eee
fix log.error, count newlines
connorjclark Dec 12, 2019
370c9bc
remove load fn
connorjclark Dec 13, 2019
b9f8a90
test sourceLineMapping
connorjclark Dec 13, 2019
2ba1d27
ts
connorjclark Dec 13, 2019
2cd5a72
no lazy. LH.Artifacts.Bundle
connorjclark Dec 17, 2019
141099b
Merge remote-tracking branch 'origin/master' into computed-artifact-b…
connorjclark Dec 17, 2019
2d1bc1b
indices comment
connorjclark Dec 17, 2019
5b02f21
use tsc
connorjclark Dec 23, 2019
7e44451
names
connorjclark Dec 23, 2019
f470521
comment
connorjclark Dec 23, 2019
dc5d601
sanity checks
connorjclark Dec 23, 2019
83adf25
JSBundles
connorjclark Dec 23, 2019
1457eae
fix ts
connorjclark Dec 23, 2019
70b0388
latest cdt
connorjclark Dec 23, 2019
24a9538
test
connorjclark Jan 6, 2020
795393f
ts
connorjclark Jan 6, 2020
8c12500
rawcode
connorjclark Jan 6, 2020
354f364
ts undefined
connorjclark Jan 6, 2020
36d4b48
comment
connorjclark Jan 6, 2020
8a5f792
rm todo
connorjclark Jan 6, 2020
c5a6faa
ts
connorjclark Jan 6, 2020
c947635
comment
connorjclark Jan 7, 2020
d3be59a
Merge remote-tracking branch 'origin/master' into computed-artifact-b…
connorjclark Jan 7, 2020
68441bb
-networkRecord
connorjclark Jan 7, 2020
e25d82a
Update lighthouse-core/computed/js-bundles.js
connorjclark Jan 13, 2020
e73b8c8
test dirty build
connorjclark Jan 13, 2020
d9aa735
update
connorjclark Jan 13, 2020
418ed9b
fix
connorjclark Jan 13, 2020
d8be656
debug
connorjclark Jan 13, 2020
ce15205
blah
connorjclark Jan 13, 2020
d75df5e
ok
connorjclark Jan 13, 2020
f6ffd75
check just 1
connorjclark Jan 13, 2020
bae54d4
2020
connorjclark Jan 14, 2020
f1bc372
Merge remote-tracking branch 'origin/master' into computed-artifact-b…
connorjclark Jan 27, 2020
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
2 changes: 2 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

**/node_modules/**
**/third_party/**
**/generated/**
**/source-maps/**

/dist/**

Expand Down
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ before_script:
- google-chrome-stable --version
- yarn build-all
script:
# Fail if any changes were written to source files (ex, from: build/build-cdt-lib.js).
- git diff --exit-code
- yarn bundlesize
- yarn diff:sample-json
- yarn lint
Expand Down
137 changes: 137 additions & 0 deletions build/build-cdt-lib.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
/**
* @license Copyright 2020 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
'use strict';

/* eslint-disable no-console */

const fs = require('fs');
const ts = require('typescript');

const outDir = `${__dirname}/../lighthouse-core/lib/cdt/generated`;
const files = {
'node_modules/chrome-devtools-frontend/front_end/sdk/SourceMap.js': 'SourceMap.js',
};

console.log('making modifications ...');

for (const [inFilename, outFilename] of Object.entries(files)) {
const code = fs.readFileSync(inFilename, 'utf-8');
const codeWithoutLegacyExports = code.substr(0, code.indexOf('/* Legacy exported object'));
const codeTranspiledToCommonJS = ts.transpileModule(codeWithoutLegacyExports, {
compilerOptions: {module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2019},
}).outputText;

const sourceFile = ts.createSourceFile('', codeTranspiledToCommonJS,
ts.ScriptTarget.ES2019, true, ts.ScriptKind.JS);
const simplePrinter = ts.createPrinter({newLine: ts.NewLineKind.LineFeed});

const classesToRemove = [
// Not needed.
'EditResult',
// Not needed.
'WasmSourceMap',
];
const methodsToRemove = [
// Not needed.
'load',
// Not needed.
'sourceContentProvider',
];
const expressionsToRemove = [
/* Original:

let url = Common.ParsedURL.completeURL(this._baseURL, href) || href;
const source = sourceMap.sourcesContent && sourceMap.sourcesContent[i];
if (url === this._compiledURL && source) {
url += Common.UIString('? [sm]')
}
this._sourceInfos.set(url, new TextSourceMap.SourceInfo(source, null));
sourcesList.push(url);
----
If a source file is the same as the compiled url and there is a sourcesContent,
then `entry.sourceURL` (what is returned from .mappings) will have `? [sm]` appended.
This is useful in DevTools - to show that a sources panel tab not a real network resource -
but for us it is not wanted. The sizing function uses `entry.sourceURL` to index the byte
counts, and is further used in the details to specify a file within a source map.
*/
`url += Common.UIString('? [sm]')`,
// Delete all the export statements.
'exports.',
];
// Complicated expressions are hard detect with the TS lib, so instead work with the raw code.
const rawCodeToReplace = {
// Similar to the reason for removing `url += Common.UIString('? [sm]')`.
// The entries in `.mappings` should not have their url property modified.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it'll be a little weird to have devtools' POV slightly different here, but looking at the spec and a few examples... I don't see a great reason why the sources' URLs should be relative to the sourcemap file's URL.

so maybe we end up also removing this in devtools.

'Common.ParsedURL.completeURL(this._baseURL, href)': `''`,
};

// Verify that all the above code is present.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

const codeFragments = [
...classesToRemove,
...methodsToRemove,
...expressionsToRemove,
...Object.values(rawCodeToReplace),
];
for (const codeFragment of codeFragments) {
if (!codeTranspiledToCommonJS.includes(codeFragment)) {
throw new Error(`did not find expected code fragment: ${codeFragment}`);
}
}

const printer = ts.createPrinter({newLine: ts.NewLineKind.LineFeed}, {
substituteNode(hint, node) {
let removeNode = false;

if (ts.isMethodDeclaration(node) &&
// Make typescript happy.
!ts.isComputedPropertyName(node.name) &&
methodsToRemove.includes(node.name.text)) {
removeNode = true;
}

if (ts.isClassDeclaration(node) && node.name && classesToRemove.includes(node.name.text)) {
removeNode = true;
}

if (ts.isExpressionStatement(node)) {
const asString = simplePrinter.printNode(ts.EmitHint.Unspecified, node, sourceFile);
if (classesToRemove.some(className => asString.includes(className))) {
removeNode = true;
}
if (expressionsToRemove.some(className => asString.includes(className))) {
removeNode = true;
}
}

if (removeNode) {
return ts.createNode(ts.SyntaxKind.Unknown);
}

return node;
},
});

let sourceFilePrinted = '';
sourceFile.forEachChild(node => {
sourceFilePrinted += printer.printNode(ts.EmitHint.Unspecified, node, sourceFile) + '\n';
});

for (const [code, replacement] of Object.entries(rawCodeToReplace)) {
connorjclark marked this conversation as resolved.
Show resolved Hide resolved
sourceFilePrinted = sourceFilePrinted.replace(code, replacement);
if (sourceFilePrinted.includes(code)) {
throw new Error(`expected only one occurrence for: ${code}`);
}
}

const modifiedFile = [
'// @ts-nocheck\n',
'// generated by yarn build-cdt-lib\n',
'const Common = require(\'../Common.js\')\n',
sourceFilePrinted,
'module.exports = TextSourceMap',
].join('');
fs.writeFileSync(`${outDir}/${outFilename}`, modifiedFile);
}
122 changes: 122 additions & 0 deletions lighthouse-core/computed/js-bundles.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/**
* @license Copyright 2020 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
'use strict';

const log = require('lighthouse-logger');
const makeComputedArtifact = require('./computed-artifact.js');
const SDK = require('../lib/cdt/SDK.js');

/**
* Calculate the number of bytes contributed by each source file
* @param {LH.Artifacts.Bundle['map']} map
* @param {string} content
* @return {LH.Artifacts.Bundle['sizes']}
*/
function computeGeneratedFileSizes(map, content) {
const lines = content.split('\n');
/** @type {Record<string, number>} */
const files = {};
const totalBytes = content.length;
let unmappedBytes = totalBytes;

// If the map + contents don't line up, return a result that
// denotes nothing is mapped.
const failureResult = {files: {}, unmappedBytes, totalBytes};

// @ts-ignore: 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;
// @ts-ignore: `lastColumnNumber` is not on types yet. This will eventually be added to CDT.
const lastColNum = /** @type {number=} */ (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) {
log.error('JSBundles', `${map.url()} mapping for line out of bounds: ${lineNum + 1}`);
return failureResult;
}

if (colNum > line.length) {
// eslint-disable-next-line max-len
log.error('JSBundles', `${map.url()} mapping for column out of bounds: ${lineNum + 1}:${colNum}`);
return failureResult;
}

let mappingLength = 0;
if (lastColNum !== undefined) {
if (lastColNum > line.length) {
// eslint-disable-next-line max-len
log.error('JSBundles', `${map.url()} mapping for last column out of bounds: ${lineNum + 1}:${lastColNum}`);
return failureResult;
}
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'|'ScriptElements'>} artifacts
*/
static async compute_(artifacts) {
const {SourceMaps, ScriptElements} = artifacts;

/** @type {LH.Artifacts.Bundle[]} */
const bundles = [];

// Collate map and script, compute file sizes.
for (const SourceMap of SourceMaps) {
if (!SourceMap.map) continue;
const {scriptUrl, map: rawMap} = SourceMap;

if (!rawMap.mappings) continue;

const scriptElement = ScriptElements.find(s => s.src === scriptUrl);
if (!scriptElement) continue;

const compiledUrl = SourceMap.scriptUrl || 'compiled.js';
const mapUrl = SourceMap.sourceMapUrl || 'compiled.js.map';
// @ts-ignore: CDT expects undefined properties to be explicit.
const rawMapForCdt = /** @type {any} */ (rawMap);
const map = new SDK.TextSourceMap(compiledUrl, mapUrl, rawMapForCdt);

const content = scriptElement && scriptElement.content ? scriptElement.content : '';
const sizes = computeGeneratedFileSizes(map, content);

const bundle = {
rawMap,
script: scriptElement,
map,
sizes,
};
bundles.push(bundle);
}

return bundles;
}
}

module.exports = makeComputedArtifact(JSBundles);
3 changes: 3 additions & 0 deletions lighthouse-core/gather/gatherers/source-maps.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,9 @@ class SourceMaps extends Gatherer {
const map = isSourceMapADataUri ?
this.parseSourceMapFromDataUrl(rawSourceMapUrl) :
await this.fetchSourceMapInPage(driver, rawSourceMapUrl);
if (map.sections) {
map.sections = map.sections.filter(section => section.map);
}
return {
scriptUrl,
sourceMapUrl,
Expand Down
14 changes: 14 additions & 0 deletions lighthouse-core/lib/cdt/Common.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* @license Copyright 2020 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
'use strict';

/** @fileoverview Needed for the generated/SourceMap.js file */

const Common = {
connorjclark marked this conversation as resolved.
Show resolved Hide resolved
console,
};

module.exports = Common;
Loading