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

Add Lighthouse audit command #7990

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft

Add Lighthouse audit command #7990

wants to merge 1 commit into from

Conversation

mrmckeb
Copy link
Contributor

@mrmckeb mrmckeb commented Nov 16, 2019

This PR adds a lighthouse script, name TBD.

This script should:

  • Build the app (not yet implemented)
  • Run the app
  • Run Lighthouse
  • Save the audit
  • Open the audit

Known issues:

  • Ideally, we want to build for the user - there are a few ways we could do this.
  • Lighthouse requires Node 10.
  • The score is lowered due to the lack of HTTPS and HTTP2.

Copy link
Contributor

@ianschmitz ianschmitz left a comment

Choose a reason for hiding this comment

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

Nice work so far! Left a few comments even though you're still in draft 😛.

@@ -114,6 +114,7 @@ module.exports = function(
build: 'react-scripts build',
test: 'react-scripts test',
eject: 'react-scripts eject',
lighthouse: 'react-scripts audit',
Copy link
Contributor

Choose a reason for hiding this comment

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

What do you think of audit: 'react-scripts audit'?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is what I had... but it turns out it conflicts with yarn audit @ianschmitz. I didn't think of that initially, and didn't rename the other files yet, as I think the name would need discussion.

Maybe audit-app?

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah doh! Of course

.launch({ chromeFlags: opts.chromeFlags })
.then(chrome => {
opts.port = chrome.port;
return lighthouse(url, opts).then(results => {
Copy link
Contributor

@ianschmitz ianschmitz Nov 17, 2019

Choose a reason for hiding this comment

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

It would be nice to forward arguments to lighthouse. They have some cool customizations through their CLI.

For example --emulated-form-factor, the various --throttling flags or --chrome-flags

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed, this would make sense. Or better yet - or perhaps also - we could load a config file?

const launchChromeAndRunLighthouse = (url, opts) => {
return chromeLauncher
.launch({ chromeFlags: opts.chromeFlags })
.then(chrome => {
Copy link
Contributor

Choose a reason for hiding this comment

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

We should be able to use async/await if that helps clean it up.

Copy link
Contributor Author

@mrmckeb mrmckeb Nov 17, 2019

Choose a reason for hiding this comment

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

We don't support Node 6 anymore, right @ianschmitz? If so, I 1000% agree.

We have Node ">8.10" as our requirement for react-scripts, so I'll make this change.

.then(() => choosePort(HOST, DEFAULT_PORT))
.then(port => {
if (port == null) {
console.log('Unable to find a free port');
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we do something like this in error cases for consistency elsewhere?

console.error(
  chalk.bold.red(...)
)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed, this should be polished - it also needs some line spacing.

packages/react-scripts/scripts/audit.js Show resolved Hide resolved
const { choosePort } = require('react-dev-utils/WebpackDevServerUtils');
const open = require('open');
const handler = require('serve-handler');
const lighthouse = require('lighthouse');
Copy link
Contributor

@ianschmitz ianschmitz Nov 17, 2019

Choose a reason for hiding this comment

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

Running chrome in headless would be amazing! I could see some cool CI use cases to fail when below a threshold. Potentially using puppeteer would make that trivial for us, removes the need to have chrome installed to use this functionality, and also provides the option to run headed if needed for debugging etc.

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, so there's an option for headless. I left it "headfull" as the default intentionally - I feel that for some people, it's nice to see how it works - I guess we can rely on a flag or config for opting out of that?

What do you think @ianschmitz?

Copy link
Contributor

Choose a reason for hiding this comment

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

On second thought, including puppeteer as a direct dependency would greatly increase the node_modules size which isn't great:

When you install Puppeteer, it downloads a recent version of Chromium (~170MB Mac, ~282MB Linux, ~280MB Win).

Documentation could likely handle this:

If you want to run this in CI, make sure Chrome/Chromium is installed... (or use puppeteer etc.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, and I think we should also handle this in the script... I'm sure we can.

.launch({ chromeFlags: opts.chromeFlags })
.then(chrome => {
opts.port = chrome.port;
return lighthouse(url, opts).then(results => {
Copy link
Contributor

@ianschmitz ianschmitz Nov 17, 2019

Choose a reason for hiding this comment

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

async/await may make it a bit simpler to cover failure cases such as any of this process throwing in which case we should probably clean up the browser and server instances.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed. I just left this as per the docs for now - and forgot that we don't support Node 6.

Copy link
Contributor

@ianschmitz ianschmitz left a comment

Choose a reason for hiding this comment

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

Just wondering about the comment about This script should: Build the app. Just thinking for the CI use case this would result in a double build which isn't great.

@mrmckeb
Copy link
Contributor Author

mrmckeb commented Nov 18, 2019

Good point @ianschmitz, what if it just checked for a build folder, and then threw if there wasn't one? Explaining that the project should be built first?

Or it could build in the case that a build doesn't exist...

@ianschmitz
Copy link
Contributor

Good point @ianschmitz, what if it just checked for a build folder, and then threw if there wasn't one? Explaining that the project should be built first?

Or it could build in the case that a build doesn't exist...

Yeah something like that would probably be nice

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants