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

cli: convert to ES modules #13045

Merged
merged 31 commits into from
Sep 14, 2021
Merged

cli: convert to ES modules #13045

merged 31 commits into from
Sep 14, 2021

Conversation

connorjclark
Copy link
Collaborator

@connorjclark connorjclark commented Sep 10, 2021

ref #12689

  • Converts lighthouse-cli/ to esm.
  • Upgrades to newest jest, to use jest.unstable_mockModule (only cli test that uses mocks is bin-test.js).
  • Updates build-lr to also bundle up static-server.js, necessary since it now imports non-core modules (And bundling it all up is simplest way to run it in google3).

(split off into: #13046)

@connorjclark connorjclark requested a review from a team as a code owner September 10, 2021 22:22
@connorjclark connorjclark requested review from adamraine and removed request for a team September 10, 2021 22:22
@google-cla google-cla bot added the cla: yes label Sep 10, 2021
@@ -65,9 +68,18 @@ async function begin() {
if (cliFlags.configPath) {
// Resolve the config file path relative to where cli was called.
cliFlags.configPath = path.resolve(process.cwd(), cliFlags.configPath);
configJson = require(cliFlags.configPath);
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

of note.

if (rawTestDefns) {
// Support commonjs.
rawTestDefns = rawTestDefns.default || rawTestDefns;
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

of note

@connorjclark
Copy link
Collaborator Author

Error [ERR_UNSUPPORTED_ESM_URL_SCHEME]: Only file and data URLs are supported by the default ESM loader. On Windows, absolute paths must be valid file:// URLs. Received protocol 'd:'

wut. so... import('d:/some/file.js') should be import('file://d:/some/file.js') on windows????

@brendankenny
Copy link
Member

There's some pretty major stuff going on in here, can this be split up? Even if a lot of it is pretty straightforward, it would be nice to consider cli-flags changes separate from bundling static-sever in the lr bundle

const getFlags = require('../../cli-flags.js').getFlags;

import {strict as assert} from 'assert';
import fs from 'fs';
Copy link
Member

Choose a reason for hiding this comment

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

Do we need to be consistent with import fs from 'fs' or import * as fs from 'fs?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

fs is commonjs and so has no default export, so the entire module (aka * as fs) is treated as the default.

I have no idea which to go with, my first thought is leaning towards using the default import style.

Copy link
Member

Choose a reason for hiding this comment

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

import fs from 'fs' is my preferred version by far

Comment on lines -24 to -25
// Map plugin name to fixture since not actually installed in node_modules/.
jest.mock('lighthouse-plugin-simple', () => {
Copy link
Member

Choose a reason for hiding this comment

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

how does this work without this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

somehow require.resolve('lighthouse-plugin-simple') resolves to /Users/cjamcl/src/lighthouse/lighthouse-core/test/fixtures/config-plugins/lighthouse-plugin-simple/plugin-simple.js here. Not sure how the module is registered... the plugin-simple.js bit at least is explained by that folder's package.json main.

Copy link
Member

Choose a reason for hiding this comment

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

so weird, works here too. This completely goes against what I thought I knew about how resolve works :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

just checked from master too, same there. this mock isn't needed for some reason unrelated to this PR. weeeeird

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

this doesn't help but here:

{
        moduleIdentifier: 'lighthouse-plugin-simple',
        configDir: undefined,
        category: 'plugin',
        'require.resolve(moduleIdentifier)': '/Users/cjamcl/src/lighthouse/lighthouse-core/test/fixtures/config-plugins/lighthouse-plugin-simple/plugin-simple.js',
        'require.resolve.paths(moduleIdentifier)': [
          '/Users/cjamcl/src/lighthouse/lighthouse-core/config/node_modules',
          '/Users/cjamcl/src/lighthouse/lighthouse-core/node_modules',
          '/Users/cjamcl/src/lighthouse/node_modules',
          '/Users/cjamcl/src/node_modules',
          '/Users/cjamcl/node_modules',
          '/Users/node_modules',
          '/node_modules'
        ]
      }

Copy link
Collaborator Author

@connorjclark connorjclark Sep 14, 2021

Choose a reason for hiding this comment

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

OK so it 100% fails when using the CLI directly (node lighthouse-cli https://www.example.com --plugins=lighthouse-plugin-simple), so this must be a jest side effect. (--experimental-vm-modules made no difference)

Copy link
Member

Choose a reason for hiding this comment

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

didn't find much documentation on it, but looks like yeah, jest walks through your project and automatically finds all the package.json files. jest-resolve has a ModuleMap, which internally had a _raw.map, which looked like:

'gulp-lighthouse-recipe' => 'docs/recipes/gulp/package.json'
'custom-lighthouse-recipe' => 'docs/recipes/custom-audit/package.json'
'custom-lighthouse-pptr-recipe' => 'docs/recipes/custom-gatherer-puppeteer/package.json'
'lighthouse-plugin-example' => 'docs/recipes/lighthouse-plugin-example/package.json'
'lighthouse-logger' => 'lighthouse-logger/package.json'
'lighthouse' => 'package.json'
'lighthouse-plugin-config' => 'lighthouse-core/test/fixtures/config/lighthouse-plugin-config-helper/package.json'
'lighthouse-plugin-no-groups' => 'lighthouse-core/test/fixtures/config-plugins/lighthouse-plugin-no-groups/package.json'
'lighthouse-plugin-no-category' => 'lighthouse-core/test/fixtures/config-plugins/lighthouse-plugin-no-category/package.json'
'lighthouse-plugin-simple' => 'lighthouse-core/test/fixtures/config-plugins/lighthouse-plugin-simple/package.json'
'legacy-javascript' => 'lighthouse-core/scripts/legacy-javascript/package.json'

could be their haste modules, which apparently are "package.json files outside of node_modules folders anywhere in the file system". I didn't verify that's the path triggering this but close enough :)

Maybe worth leaving a comment that jest automatically resolves the '--plugins=lighthouse-plugin-simple' flag to the correct location?

Comment on lines +73 to +80
if (cliFlags.configPath.endsWith('.json')) {
configJson = JSON.parse(fs.readFileSync(cliFlags.configPath, 'utf-8'));
} else {
const configModuleUrl = url.pathToFileURL(cliFlags.configPath).href;
configJson = (await import(configModuleUrl)).default;
}
} else if (cliFlags.preset) {
configJson = require(`../lighthouse-core/config/${cliFlags.preset}-config.js`);
configJson = (await import(`../lighthouse-core/config/${cliFlags.preset}-config.js`)).default;
Copy link
Member

Choose a reason for hiding this comment

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

this seems like a place where a createRequire would be justified. Pretty awkward without it

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

wouldn't that fail for es modules?

Copy link
Member

Choose a reason for hiding this comment

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

oh yeah, good point :(

module.exports = {
begin,
export {
begin
Copy link
Member

Choose a reason for hiding this comment

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

not an emergency, but it feels like we probably want

'comma-dangle': [2, {
  imports: 'always-multiline',
  exports: 'always-multiline',
}]

seems weird not to have one

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

agreed, will put up a separate PR

lighthouse-cli/cli-flags.js Outdated Show resolved Hide resolved
const y = manualArgv ?
// @ts-expect-error - undocumented, but yargs() supports parsing a single `string`.
yargs(manualArgv) :
yargs(yargsHelpers.hideBin(process.argv));
Copy link
Member

Choose a reason for hiding this comment

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

Why the change? Seems way worse than having yargs handle it :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

oh. at one point I may have tried updating yargs and found this to be necessary. I can probably revert.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

hmm... not sure what changed, as there is no yargs upgrade, but the old code results in this error:

TypeError: y.help is not a function
    at getFlags (file:///Users/cjamcl/src/lighthouse/lighthouse-cli/cli-flags.js:28:18)
    at begin (file:///Users/cjamcl/src/lighthouse/lighthouse-cli/bin.js:53:20)
    at file:///Users/cjamcl/src/lighthouse/lighthouse-cli/index.js:11:1
    at ModuleJob.run (internal/modules/esm/module_job.js:152:23)
    at async Loader.import (internal/modules/esm/loader.js:177:24)
    at async Object.loadESM (internal/process/esm_loader.js:68:5)

Copy link
Member

Choose a reason for hiding this comment

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

hmm... not sure what changed, as there is no yargs upgrade, but the old code results in this error:

must be some change to the esmodules export. Here's a similar case: yargs/yargs#1774

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

shurg. this is exactly what their demo code does https://github.com/yargs/yargs#esm

tweaking the import to be * creates type errors.

listAudits,
listTraceCategories,
};
export {listAudits} from './list-audits.js';
Copy link
Member

Choose a reason for hiding this comment

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

complete nit, but not sure about this style. Maybe re-exporting is awkward no matter what, but I do like the explicitness of the other way

lighthouse-cli/commands/list-trace-categories.js Outdated Show resolved Hide resolved
lighthouse-cli/test/cli/index-test.js Outdated Show resolved Hide resolved
const y = manualArgv ?
// @ts-expect-error - undocumented, but yargs() supports parsing a single `string`.
yargs(manualArgv) :
yargs(yargsHelpers.hideBin(process.argv));
Copy link
Member

Choose a reason for hiding this comment

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

hmm... not sure what changed, as there is no yargs upgrade, but the old code results in this error:

must be some change to the esmodules export. Here's a similar case: yargs/yargs#1774

@@ -7,22 +7,22 @@

/* eslint-disable no-console */

const path = require('path');
const psList = require('ps-list');
/** @typedef {Error & {code: string, friendlyMessage?: string}} ExitError */
Copy link
Member

Choose a reason for hiding this comment

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

what did you think about my typedef after imports thing from #13046 (comment)?

Copy link
Collaborator Author

@connorjclark connorjclark Sep 14, 2021

Choose a reason for hiding this comment

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

I prefer local types at the top of the file, but not enough to discuss it much. I'm reverting the changes that were just moving things around.

Copy link
Member

Choose a reason for hiding this comment

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

ha, damn, I thought I had convinced you, but oh well :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

the short of it is that exactly what is being imported isn't too informing (and sometimes the import list is so long you glaze over all of it like it's a Java file); but the types are quite useful as to the data structures involved in the file. so for the same reason that fileoverview would be at top, I like local types to be too

Copy link
Member

Choose a reason for hiding this comment

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

For all the same reasons I think they should be closer to the code that uses them :) Definitions, just in the type domain instead of the value domain, so should be right next to the value definitions.

Copy link
Member

@brendankenny brendankenny left a comment

Choose a reason for hiding this comment

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

Just the last dangling comma and typedef style questions, but looks good! A very smooth transition

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.

4 participants