-
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
Add --view flag which will start a server and host report.html #1130
Conversation
Thanks for your pull request. It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). 📝 Please visit https://cla.developers.google.com/ to sign. Once you've signed, please reply here (e.g.
|
9ac64ca
to
aff780a
Compare
CLAs look good, thanks! |
}); | ||
|
||
function listen(port) { | ||
return app.listen(port) |
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.
nit: ;
} | ||
|
||
module.exports = { | ||
listen: listen |
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.
listen: app.listen
?
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 changed it to listen: app.listen.bind(app)
const fileAbsPath = `${__dirname}/server/reports/${filename}`; | ||
Printer.write(results, 'html', fileAbsPath); | ||
opn(`http://localhost:${_REPORT_SERVER_PORT}/reports/${filename}`); | ||
|
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.
So --view
overrides output pretty?
const filename = `./${assetSaver.getFilenamePrefix({url: address})}.report.html`; | ||
Printer.write(results, 'html', filename); | ||
|
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.
nit: remove blank line
Need to fix travis errors. |
is it possible to post the plan for this to a public issue to give context to everyone who hasn't seen it? |
@@ -8,6 +8,8 @@ | |||
"typescript": "^2.0.3" | |||
}, | |||
"dependencies": { | |||
"@types/node": "^6.0.45" | |||
"@types/node": "^6.0.45", | |||
"express": "^4.14.0", |
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.
until we need more advanced server features, could we limit this to a more simple server a la static-server to avoid the extensive dependencies express
brings in until we need them?
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.
IMO express is very minimalist already. I think the trade off is writing less code or less dependencies. Express lets us do simple routing and deals with basic security issues in single lines. (current code can be cut down further).
const express = require('express'); | ||
const app = express(); | ||
|
||
app.get('/reports/:name', (request, response) => { |
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.
What about using express.static()
here if its all in one directory?
@brendankenny: Yup, in this particular case #616 is just about viewing trace results. @WeiweiAtGit: can you make sure you summaries the experiments ideas in a separate issue? |
@samuelli Sure. I will summarize the project and put a link to the document later. |
@@ -234,6 +240,14 @@ function lighthouseRun(addresses: Array<string>, config: Object, flags: Object) | |||
Printer.write(results, 'html', filename); | |||
} | |||
|
|||
// Generate report.html, host it and open it in the default browser | |||
if (flags.view) { | |||
const filename = `${assetSaver.getFilenamePrefix({url: address})}.report.html`; |
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.
maybe dry this up with 239-240.
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.
Thanks for the suggestion
*/ | ||
'use strict'; | ||
|
||
const express = require('express'); |
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.
can we try to do the server without express?
it's so simple I think we can get away with not using it and instead reusing https://github.com/GoogleChrome/lighthouse/blob/master/lighthouse-cli/test/fixtures/static-server.js
It might need a feature or two, but augmenting that seems better.
if (flags.view) { | ||
server.listen().then((port: number) => { | ||
const fileAbsPath = `${__dirname}/server/reports/${filename}`; | ||
Printer.write(results, 'html', fileAbsPath); |
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.
We are already saving the file to disk in the default pretty
case. Shall we just reuse that file and host that?
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.
Currently, the report page is a static page. But the new report page should allow developers to interact with that. The server will then need to host a different report page (eventually). So I think we cannot use the same html file unless we want to update the old report page to the new one as well.
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.
ah gotcha. now i see what you were thinking here.
one way we could handle this is append a <script>...</script>
to the end of the response HTML as its being served. (that's also how livereload servers work, just injecting scripts into the end of the file)
generally this has some advantages, in that we can keep the reporting stuff really consistent between our static and interactive reports.
wdyt?
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.
Wow I didn't know we could do it in that approach. Thanks for the suggestion!
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.
Already changed. Also, server.js now uses 'http' instead of 'express'.
Edit: It's not working properly. Will update it soon
Edit2: Updated
…ico to avoid error when loading report.html in a browser
Deleted unnecessary code in server.js.\nUpdated filename variable when file is not found.\nRearranged code to avoid using variable 'server' before defining
…generated in the past Now use symlink to access report files which are not in the server folder.\nAlso deny access to files outside server folder.\n
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.
some more comments :)
@@ -0,0 +1,94 @@ | |||
/** |
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 there a more descriptive directory name we can use here since this is going to become specifically the performance experiments server?
|
||
function getPort(port) { | ||
if (!portPromise) { | ||
portPromise = new Promise((reslove, reject) => { |
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.
s/reslove/resolve
const server = http.createServer(requestHandler); | ||
server.on('error', e => console.error(e.code, e)); | ||
|
||
function getPort(port) { |
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.
should this be called something more like createServer
?
(after the listen()
callback the port query is just a sync operation)
if (flags.view) { | ||
server.getPort(0).then((port: number) => { | ||
const filePath = `${server.FOLDERS.REPORTS}/${filename}`; | ||
const htmlExist = outputMode === Printer.OutputMode[Printer.OutputMode.pretty] |
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.
Would it be better to just check if the file exists to avoid having two places to change any logic on when a file will be printed to file?
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.
Thanks for your advice. I tried to follow your approach but I found that we still need to check the output mode because we only want to host HTML file (may change to only JSON file later to ease performance comparison). Besides, there may exist a random file named 'stdout' in CWD, considering that 'stdout' is a common name. Thus I think it's still better to check whether outputPath is 'stdout'.
// If the HTML file is already generated, create a symlink to it | ||
if (htmlExist) { | ||
const targetPath = outputMode === Printer.OutputMode[Printer.OutputMode.pretty] ? filename : outputPath; | ||
fs.symlinkSync(path.resolve(targetPath), filePath); |
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.
should we clear this directory on server close
? On the one hand, it may be worth having older versions, but if you're deleting the original the symlink becomes useless and you probably won't know to delete the ones in the server directory.
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.
Instead of deleting the entire folder, now the server will delete all broken symlinks on start. So we can keep the old reports and we don't need to worry about those broken symlinks.
f760d64
to
59a178e
Compare
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.
Changed server folder name from 'server' to 'performance-experiment'.
Changed function name from 'listen' to 'startServer'.
Server now deletes broken symlinks on start.
if (flags.view) { | ||
server.getPort(0).then((port: number) => { | ||
const filePath = `${server.FOLDERS.REPORTS}/${filename}`; | ||
const htmlExist = outputMode === Printer.OutputMode[Printer.OutputMode.pretty] |
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.
Thanks for your advice. I tried to follow your approach but I found that we still need to check the output mode because we only want to host HTML file (may change to only JSON file later to ease performance comparison). Besides, there may exist a random file named 'stdout' in CWD, considering that 'stdout' is a common name. Thus I think it's still better to check whether outputPath is 'stdout'.
// If the HTML file is already generated, create a symlink to it | ||
if (htmlExist) { | ||
const targetPath = outputMode === Printer.OutputMode[Printer.OutputMode.pretty] ? filename : outputPath; | ||
fs.symlinkSync(path.resolve(targetPath), filePath); |
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.
Instead of deleting the entire folder, now the server will delete all broken symlinks on start. So we can keep the old reports and we don't need to worry about those broken symlinks.
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.
a few more things
performanceXServer.startServer(0).then((port: number) => { | ||
const filePath = `${performanceXServer.FOLDERS.REPORTS}/${filename}`; | ||
const htmlExist = outputMode === Printer.OutputMode[Printer.OutputMode.pretty] | ||
|| (outputPath !== 'stdout' && outputMode === Printer.OutputMode[Printer.OutputMode.html]); |
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.
for some conflicting advice here: I'm still not a fan of the fragility of this check. It may not be worth reusing the pretty
report...I'd personally prefer just re-printing (~100ms for a decent report) and longer term we could do some kind of refactor like separating generation of the report html from saving to file
@paulirish for his opinion
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.
Sure. I'm happy to regenerate the report rather than read it from where we just printed it on disk.
if --view becomes a default, then the automatic disk-save won't really make sense.
}; | ||
|
||
const server = http.createServer(requestHandler); | ||
server.on('error', e => console.error(e.code, e)); |
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.
should use log.error
here for logging
sendResponse(404, `404 - File not found. ${pathname}`); | ||
return; | ||
} | ||
console.error(`Unable to read local file ${filePath}:`, err); |
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.
log.error
here as well
Added a file overview and some comments for performance-experiment/server.js. No longer check if report page is already created. Now always create a new copy for report page. Removed symlink cleanup process on server start since it’s no longer needed.
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.
LGTM
over to @paulirish |
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.
Yup. This is looking ace. Thanks for kicking this awesome work off, @WeiweiAtGit :D
Thanks for all the advice! |
This server will allow to host trace file in the future. #616