-
Notifications
You must be signed in to change notification settings - Fork 9.3k
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(lifecycle): allow gathering & auditing to run separately #3743
Changes from 16 commits
2645c46
5dd3d50
e0c0013
f72379b
309401e
7bdd889
d429332
a43b016
80b6b96
f072088
245d9e3
a7a4902
9332fd6
93aeddd
ee704bd
f14f2b0
bda2045
1ac586c
04b7a09
9a4b36d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -46,7 +46,7 @@ function lighthouse(url, flags = {}, configJSON) { | |
|
||
// kick off a lighthouse run | ||
return Runner.run(connection, {url, flags, config}) | ||
.then(lighthouseResults => { | ||
.then((lighthouseResults = {}) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what execution path leads to this being undefined? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. -G does. no lighthouse results because we saved artifacts and are resolving early i could pass an object back there, but it seems slightly more awkward. open to ideas. |
||
// Annotate with time to run lighthouse. | ||
const endTime = Date.now(); | ||
lighthouseResults.timing = lighthouseResults.timing || {}; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,10 +7,13 @@ | |
'use strict'; | ||
|
||
const fs = require('fs'); | ||
const path = require('path'); | ||
const log = require('lighthouse-logger'); | ||
const stream = require('stream'); | ||
const stringifySafe = require('json-stringify-safe'); | ||
const Metrics = require('./traces/pwmetrics-events'); | ||
const TraceParser = require('./traces/trace-parser'); | ||
const rimraf = require('rimraf'); | ||
const mkdirp = require('mkdirp'); | ||
|
||
/** | ||
* Generate basic HTML page of screenshot filmstrip | ||
|
@@ -55,19 +58,94 @@ img { | |
`; | ||
} | ||
|
||
const artifactsFilename = 'artifacts.json'; | ||
const traceSuffix = '.trace.json'; | ||
const devtoolsLogSuffix = '.devtoolslog.json'; | ||
|
||
/** | ||
* Load artifacts object from files located within basePath | ||
* Also save the traces to their own files | ||
* @param {string} basePath | ||
* @return {!Promise<!Artifacts>} | ||
*/ | ||
// Set to ignore because testing it would imply testing fs, which isn't strictly necessary. | ||
/* istanbul ignore next */ | ||
function loadArtifacts(basePath) { | ||
log.log('Reading artifacts from disk:', basePath); | ||
|
||
if (!fs.existsSync(basePath)) return Promise.reject(new Error('No saved artifacts found')); | ||
|
||
// load artifacts.json | ||
const filenames = fs.readdirSync(basePath); | ||
const artifacts = JSON.parse(fs.readFileSync(path.join(basePath, artifactsFilename), 'utf8')); | ||
|
||
// load devtoolsLogs | ||
artifacts.devtoolsLogs = {}; | ||
filenames.filter(f => f.endsWith(devtoolsLogSuffix)).map(filename => { | ||
const passName = filename.replace(devtoolsLogSuffix, ''); | ||
const devtoolsLog = JSON.parse(fs.readFileSync(path.join(basePath, filename), 'utf8')); | ||
artifacts.devtoolsLogs[passName] = devtoolsLog; | ||
}); | ||
|
||
// load traces | ||
artifacts.traces = {}; | ||
const promises = filenames.filter(f => f.endsWith(traceSuffix)).map(filename => { | ||
return new Promise(resolve => { | ||
const passName = filename.replace(traceSuffix, ''); | ||
const readStream = fs.createReadStream(path.join(basePath, filename), { | ||
encoding: 'utf-8', | ||
highWaterMark: 4 * 1024 * 1024, // TODO benchmark to find the best buffer size here | ||
}); | ||
const parser = new TraceParser(); | ||
readStream.on('data', chunk => parser.parseChunk(chunk)); | ||
readStream.on('end', _ => { | ||
artifacts.traces[passName] = parser.getTrace(); | ||
resolve(); | ||
}); | ||
}); | ||
}); | ||
return Promise.all(promises).then(_ => artifacts); | ||
} | ||
|
||
/** | ||
* Save entire artifacts object to a single stringified file located at | ||
* pathWithBasename + .artifacts.log | ||
* Save artifacts object mostly to single file located at basePath/artifacts.log. | ||
* Also save the traces & devtoolsLogs to their own files | ||
* @param {!Artifacts} artifacts | ||
* @param {string} pathWithBasename | ||
* @param {string} basePath | ||
*/ | ||
// Set to ignore because testing it would imply testing fs, which isn't strictly necessary. | ||
/* istanbul ignore next */ | ||
function saveArtifacts(artifacts, pathWithBasename) { | ||
const fullPath = `${pathWithBasename}.artifacts.log`; | ||
// The networkRecords artifacts have circular references | ||
fs.writeFileSync(fullPath, stringifySafe(artifacts)); | ||
log.log('artifacts file saved to disk', fullPath); | ||
function saveArtifacts(artifacts, basePath) { | ||
mkdirp.sync(basePath); | ||
rimraf.sync(`${basePath}/*${traceSuffix}`); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe chrome launcher had a lot of issues on windows when using sync rimraf. Should we move to async? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. rimraf's bumps since then were around this issue so i am hoping we're good now. https://github.com/isaacs/rimraf/commits/master |
||
rimraf.sync(`${basePath}/${artifactsFilename}`); | ||
|
||
// We don't want to mutate the artifacts as provided | ||
artifacts = Object.assign({}, artifacts); | ||
|
||
// save traces | ||
const traces = artifacts.traces; | ||
let promise = Promise.all(Object.keys(traces).map(passName => { | ||
return saveTrace(traces[passName], `${basePath}/${passName}${traceSuffix}`); | ||
})); | ||
|
||
// save devtools log | ||
const devtoolsLogs = artifacts.devtoolsLogs; | ||
promise = promise.then(_ => { | ||
Object.keys(devtoolsLogs).map(passName => { | ||
const log = JSON.stringify(devtoolsLogs[passName]); | ||
fs.writeFileSync(`${basePath}/${passName}${devtoolsLogSuffix}`, log, 'utf8'); | ||
}); | ||
delete artifacts.traces; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can't delete |
||
delete artifacts.devtoolsLogs; | ||
}); | ||
|
||
// save everything else | ||
promise = promise.then(_ => { | ||
fs.writeFileSync(`${basePath}/${artifactsFilename}`, JSON.stringify(artifacts), 'utf8'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. care about prettifying? a consequence of this I just ran into is that some artifacts are objects like |
||
log.log('Artifacts saved to disk in folder:', basePath); | ||
}); | ||
return promise; | ||
} | ||
|
||
/** | ||
|
@@ -178,7 +256,7 @@ function saveTrace(traceData, traceFilename) { | |
function saveAssets(artifacts, audits, pathWithBasename) { | ||
return prepareAssets(artifacts, audits).then(assets => { | ||
return Promise.all(assets.map((data, index) => { | ||
const devtoolsLogFilename = `${pathWithBasename}-${index}.devtoolslog.json`; | ||
const devtoolsLogFilename = `${pathWithBasename}-${index}${devtoolsLogSuffix}`; | ||
fs.writeFileSync(devtoolsLogFilename, JSON.stringify(data.devtoolsLog, null, 2)); | ||
log.log('saveAssets', 'devtools log saved to disk: ' + devtoolsLogFilename); | ||
|
||
|
@@ -190,7 +268,7 @@ function saveAssets(artifacts, audits, pathWithBasename) { | |
fs.writeFileSync(screenshotsJSONFilename, JSON.stringify(data.screenshots, null, 2)); | ||
log.log('saveAssets', 'screenshots saved to disk: ' + screenshotsJSONFilename); | ||
|
||
const streamTraceFilename = `${pathWithBasename}-${index}.trace.json`; | ||
const streamTraceFilename = `${pathWithBasename}-${index}${traceSuffix}`; | ||
log.log('saveAssets', 'streaming trace file to disk: ' + streamTraceFilename); | ||
return saveTrace(data.traceData, streamTraceFilename).then(_ => { | ||
log.log('saveAssets', 'trace file streamed to disk: ' + streamTraceFilename); | ||
|
@@ -221,6 +299,7 @@ function logAssets(artifacts, audits) { | |
|
||
module.exports = { | ||
saveArtifacts, | ||
loadArtifacts, | ||
saveAssets, | ||
prepareAssets, | ||
saveTrace, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is anyone using this should we wait for a major version? I'm fine nuking, but seems like it could still be supported
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ideally it would stick around, at least until 3.0.
auditResults
can skip everything and go right to the scoring (basically-R
)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(it's also helpful for tests where you just want to test the -R part)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer to just kill auditResults. Turns out we dont really have tests using it (except one).
auditResults is this weird inbetween value that's only different from the LHR because of these few lines: https://github.com/GoogleChrome/lighthouse/blob/gar/lighthouse-core/runner.js#L128-L155
if we REALLY want to support -R then we'd definitely not put auditResults on the config instance because it's bizarre. (obv that would break backcompat (for those mystery users)). if we want to do this i'd prefer to do it in a followup