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

misc(build): bundle pub ads plugin for devtools #9924

Merged
merged 10 commits into from
Jan 28, 2020
Merged

misc(build): bundle pub ads plugin for devtools #9924

merged 10 commits into from
Jan 28, 2020

Conversation

brendankenny
Copy link
Member

@brendankenny brendankenny commented Nov 5, 2019

This is basically all that's required to get a hard-coded plugin into one of our bundles. Not quite working yet...browserify is having an issue with walking and parsing transitive dependencies that I haven't resolved yet.

(+ #9936 )

// HACK: manually include the lighthouse-plugin-publisher-ads audits.
/** @type {Array<string>} */
// @ts-ignore
const pubAdsAudits = require('lighthouse-plugin-publisher-ads/plugin.js').audits.map(a => a.path);
Copy link
Member Author

Choose a reason for hiding this comment

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

or whatever is a good way to get this list...readdir doesn't work cause there's more than just the audits in that directory

@@ -83,6 +88,7 @@ async function browserifyFile(entryPath, distPath) {
}

// Expose the audits, gatherers, and computed artifacts so they can be dynamically loaded.
// Exposed path relative to lighthouse-core/config/config-helpers.js (where loading occurs).
Copy link
Member Author

Choose a reason for hiding this comment

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

important information, passed down over generations of build revisions, but only implicitly specified :)

Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// Exposed path relative to lighthouse-core/config/config-helpers.js (where loading occurs).
// Exposed path MUST be a relative path from lighthouse-core/config/config-helpers.js (where loading occurs).

doesn't have to be spec style like this but I didn't totally understand this until rereading all the code to see what it did, any complete sentence might've helped :)

requirePath = resolveModule(audit.path, configDir, 'audit');
const absolutePath = resolveModule(audit.path, configDir, 'audit');
// Use a relative path so bundler can easily expose it.
requirePath = path.relative(__dirname, absolutePath);
Copy link
Member Author

Choose a reason for hiding this comment

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

needs to be relative, otherwise we'd need to be browserify exposing an absolute path

Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't totally understand the comment and this line. Isn't the bundler exposing the pub audits by manually requiring in its config file?

It seems like this path of requireAudits is only hit when we're requiring in the audits as we execute the bundle where we're in brfs land and don't have real dirnames anymore. Is that right?

Copy link
Member Author

Choose a reason for hiding this comment

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

It seems like this path of requireAudits is only hit when we're requiring in the audits as we execute the bundle where we're in brfs land and don't have real dirnames anymore. Is that right?

right. Internal to the bundle they have some module name that can be required. Since they're required dynamically, the name they're exposed as has to be figured out at runtime, and the easiest way to do that is to use the same name that is passed to require() in non-browserified execution. The existing code was doing that with an absolute path here, and even though we could expose the modules with the same absolute path in build-bundle, those are going to be different based on what machine it's run on, etc. Hence using a relative path here, which also works non-browserified.

It's basically the dynamic require.resolve() equivalent of the ../audits/${audit.path} for the core audits a few lines above this.

package.json Outdated
@@ -119,6 +119,8 @@
"isomorphic-fetch": "^2.2.1",
"jest": "^24.3.0",
"jsdom": "^12.2.0",
"lighthouse-plugin-publisher-ads": "./node_modules/lighthouse-plugin-publisher-ads-wrapper/lighthouse-plugin-publisher-ads/",
"lighthouse-plugin-publisher-ads-wrapper": "https://github.com/googleads/publisher-ads-lighthouse-plugin",
Copy link
Member Author

Choose a reason for hiding this comment

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

it would be really helpful if lighthouse-plugin-publisher-ads was published on npm so we didn't have to add the wrapper from github and then add the plugin with a relative path into the wrapper module :)

Copy link
Collaborator

Choose a reason for hiding this comment

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

yarn errors for me:

error Package "lighthouse-plugin-publisher-ads" refers to a non-existing file '"/usr/local/google/home/cjamcl/code/lighthouse/node_modules/lighthouse-plugin-publisher-ads-wrapper/lighthouse-plugin-publisher-ads"'.

I assume this only worked for you because you initially had lighthouse-plugin-publisher-ads-wrapper installed before adding the other one.

Copy link
Collaborator

Choose a reason for hiding this comment

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

image

Copy link
Member Author

Choose a reason for hiding this comment

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

yarn errors for me

Yeah, we'll get it published before landing. Too bad the hack isn't reproducible from package.json though.

@@ -14,13 +14,15 @@ const {registerLocaleData, lookupLocale} = require('../lighthouse-core/lib/i18n/

/**
* Return a version of the default config, filtered to only run the specified
* categories.
* categories. Exclude `lighthouse-plugin-publisher-ads` to not include the
Copy link
Collaborator

Choose a reason for hiding this comment

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

so pub ads runs even if categoryIDs doesn't include the plugin category?

Copy link
Member Author

Choose a reason for hiding this comment

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

I'll reword that to be less confusing :) it's trying to say it's the same as the others... It'll run if it's in categoryIDs, otherwise no

Copy link
Collaborator

Choose a reason for hiding this comment

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

+1 to a reword :)

we should also add a test for the onlyCategories/plugins interaction, it seems non-obvious to me what the behavior should be I never really thought about it.

@connorjclark
Copy link
Collaborator

Let me know when this is quite working and I can verify it works from the Audits panel (assuming you're not setup to verify that?)

@brendankenny
Copy link
Member Author

Let me know when this is quite working and I can verify it works from the Audits panel (assuming you're not setup to verify that?)

SG. Feel free to poke at the browserify error, otherwise I probably won't be getting to this until tomorrow.

Copy link
Collaborator

@connorjclark connorjclark left a comment

Choose a reason for hiding this comment

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

Error: unsupported type for static module: LogicalExpression
at expression:

  opts.fs || fs
 while parsing file: /usr/local/google/home/cjamcl/code/lighthouse/node_modules/mkdirp/index.js while parsing file: /usr/local/google/home/cjamcl/code/lighthouse/node_modules/mkdirp/index.js

This means that there is some code that defines a module using a logical expression. (not determinable until runtime). See mkdirp.

A dirty workaround is to ignore this module.

.ignore('mkdirp')

Doing so reveals a couple transitive modules that are pulled in by ws. Obviously, that shouldn't be browserified so let's ignore that too.

.ignore('ws')

Rather than ignore these modules explicitly, we should understand why they get used now. It's clearly due to one of the pub ad audit modules. I narrowed it down to:

const {Audit} = require('lighthouse');

This breaks everything.

const Audit = require('lighthouse/lighthouse-core/audits/audit.js');

That seems to work.

why: The wrapper module has lighthouse as a direct dependency. our bundle script ignore the cri.js but via a very specific path that only matches our actual (non node_modules) cri.js. The fix above works because lighthouse/lighthouse-core/audits/audit.js doesn't include cri.js, but lighthouse does.

@paulirish link works because it allows our cri.js ignoring to work (doesn't use vendored lighthouse).

package.json Outdated
@@ -119,6 +119,8 @@
"isomorphic-fetch": "^2.2.1",
"jest": "^24.3.0",
"jsdom": "^12.2.0",
"lighthouse-plugin-publisher-ads": "./node_modules/lighthouse-plugin-publisher-ads-wrapper/lighthouse-plugin-publisher-ads/",
"lighthouse-plugin-publisher-ads-wrapper": "https://github.com/googleads/publisher-ads-lighthouse-plugin",
Copy link
Collaborator

Choose a reason for hiding this comment

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

yarn errors for me:

error Package "lighthouse-plugin-publisher-ads" refers to a non-existing file '"/usr/local/google/home/cjamcl/code/lighthouse/node_modules/lighthouse-plugin-publisher-ads-wrapper/lighthouse-plugin-publisher-ads"'.

I assume this only worked for you because you initially had lighthouse-plugin-publisher-ads-wrapper installed before adding the other one.

@brendankenny
Copy link
Member Author

Rather than ignore these modules explicitly, we should understand why they get used now

Yes, that was the implicit request :P

I narrowed it down to: const {Audit} = require('lighthouse');

Ah, nice find! Yeah, that would do it. I guess we'll need the plugin to reach in more directly.

@@ -83,6 +88,7 @@ async function browserifyFile(entryPath, distPath) {
}

// Expose the audits, gatherers, and computed artifacts so they can be dynamically loaded.
// Exposed path relative to lighthouse-core/config/config-helpers.js (where loading occurs).
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// Exposed path relative to lighthouse-core/config/config-helpers.js (where loading occurs).
// Exposed path MUST be a relative path from lighthouse-core/config/config-helpers.js (where loading occurs).

doesn't have to be spec style like this but I didn't totally understand this until rereading all the code to see what it did, any complete sentence might've helped :)

@@ -92,6 +98,13 @@ async function browserifyFile(entryPath, distPath) {
bundle = bundle.require(gatherer, {expose: gatherer.replace(driverPath, '../gather/')});
});

// HACK: manually include the lighthouse-plugin-publisher-ads audits.
if (isDevtools(entryPath)) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

kinda feels like we should resolve those functions to booleans called isDevtools and call the functions isDevtoolsEntry or something

Copy link
Member Author

Choose a reason for hiding this comment

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

kinda feels like we should resolve those functions to booleans called isDevtools and call the functions isDevtoolsEntry or something

yeah, I had a similar thought. Not sure why they're like that...I assume it made sense as a one-off and we've just c/p it multiple times now

// HACK: manually include the lighthouse-plugin-publisher-ads audits.
if (isDevtools(entryPath)) {
pubAdsAudits.forEach(pubAdAudit => {
bundle = bundle.require(pubAdAudit, {expose: '../../node_modules/' + pubAdAudit});
Copy link
Collaborator

Choose a reason for hiding this comment

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

add a note that all the audits are already prefixed with the module/plugin name?

requirePath = resolveModule(audit.path, configDir, 'audit');
const absolutePath = resolveModule(audit.path, configDir, 'audit');
// Use a relative path so bundler can easily expose it.
requirePath = path.relative(__dirname, absolutePath);
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't totally understand the comment and this line. Isn't the bundler exposing the pub audits by manually requiring in its config file?

It seems like this path of requireAudits is only hit when we're requiring in the audits as we execute the bundle where we're in brfs land and don't have real dirnames anymore. Is that right?

@paulirish
Copy link
Member

const {Audit} = require('lighthouse');

This breaks everything.

# i tried this in the repo root
yarn link 
yarn link lighthouse

after that yarn build-devtools seemed to work!

@connorjclark
Copy link
Collaborator

connorjclark commented Nov 5, 2019

My review was edited with the root cause of the bundling issue and a workaround.

@brendankenny
Copy link
Member Author

@paulirish link works because it allows our cri.js ignoring to work (doesn't use vendored lighthouse).

I haven't done the bundle analysis yet, but that sounds like what we might want then (we don't want duplicate lighthouse files in our bundle).

But there's a way more evil way of doing this :)

"resolutions": {
   "**/lighthouse": "file:."
}

🔥 😈 😈 😈 🔥

@paulirish
Copy link
Member

seems good.

@brendankenny
Copy link
Member Author

This appears to be working and the bundle looks good. It does currently require a

yarn link 
yarn link lighthouse

in the base directory to get a symlink (resolutions copies which we don't want), but we can figure out the workflow.

@connorjclark should be good to try out and we can work on build ergonomics.

@brendankenny
Copy link
Member Author

@connorjclark should be good to try out and we can work on build ergonomics.

It might actually need a few more dynamically included files... Maybe plugin.js for the plugin config? I can add that

@connorjclark connorjclark changed the title WIP: include plugin in dt build misc(build): bundle pub ads plugin for devtools Nov 9, 2019
lighthouse-core/config/config-helpers.js Outdated Show resolved Hide resolved
@paulirish paulirish marked this pull request as ready for review November 9, 2019 03:55
@vercel vercel bot temporarily deployed to staging November 9, 2019 03:59 Inactive
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants