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

tests(lint): require file extension in require() #9017

Merged
merged 3 commits into from May 23, 2019
Merged

Conversation

brendankenny
Copy link
Member

enforce change made in #9006

creates a local eslint rule that requires a local path in a require() statement includes the file extension. eslint has support for local rules on the command line via --rulesdir, but that means it won't work in text editors with an eslint plugin installed.

eslint-plugin-local-rules is an eslint plugin that gets around that by just loading a local file as its own payload. The downside is that it has to be a root-level file, but on the plus side it works for our inherited .eslintrc.js files for the clients and viewer.

(there's another similar plugin eslint-plugin-rulesdir that allows specifying the location of the file, but I'm not sure if it inherits and it appears like it might be broken in vscode (not-an-aardvark/eslint-plugin-rulesdir#2), something that's been tested and is working in eslint-plugin-local-rules (cletusw/eslint-plugin-local-rules#1))

happy to bikeshed on how we do this

(also picks up a few places that #9006 missed and deletes dot-js.js as hopefully we don't need any more mass fixes, but I can restore if we want to keep it)

@brendankenny
Copy link
Member Author

deletes dot-js.js as hopefully we don't need any more mass fixes

Hmm, we could use this for a returned fix() method. Does anyone ever actually use --fix with eslint?

@connorjclark
Copy link
Collaborator

deletes dot-js.js as hopefully we don't need any more mass fixes

Hmm, we could use this for a returned fix() method. Does anyone ever actually use --fix with eslint?

all the time. how else do you format?

Copy link
Collaborator

@patrickhulce patrickhulce left a comment

Choose a reason for hiding this comment

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

eslint is my preferred path too, how hard is it to write a fixer for a rule?

@@ -9,7 +9,7 @@ const BaseNode = require('../base-node.js');
const TcpConnection = require('./tcp-connection.js');
const ConnectionPool = require('./connection-pool.js');
const DNSCache = require('./dns-cache.js');
const mobileSlow4G = require('../../../config/constants').throttling.mobileSlow4G;
Copy link
Collaborator

Choose a reason for hiding this comment

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

I am so curious how these slipped through haha

Copy link
Collaborator

Choose a reason for hiding this comment

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

the answer here is I was conservative and only matched require(...); and since these do require(...).someVariable they didn't match :)

@@ -70,7 +70,7 @@ function generateConfig(configJson, flags) {

lighthouse.generateConfig = generateConfig;
lighthouse.getAuditList = Runner.getAuditList;
lighthouse.traceCategories = require('./gather/driver').traceCategories;
lighthouse.traceCategories = require('./gather/driver.js').traceCategories;
lighthouse.Audit = require('./audits/audit.js');
Copy link
Collaborator

Choose a reason for hiding this comment

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

yikes 😬 those probably need to be fixed and tests added that these don't throw?

Copy link
Member Author

Choose a reason for hiding this comment

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

yikes 😬 those probably need to be fixed and tests added that these don't throw?

I think this is a valid path? I'm not getting the context of the 😬here :)

Copy link
Collaborator

Choose a reason for hiding this comment

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

hm, this is super weird in the email and on disk I see

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

but in GitHub UI I see

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

image

seems like a GitHub bug that needs fixing not our bug! 😆

if (file.endsWith('.json')) continue;
const fullPathOfDep = path.resolve(dir, file);
if (!fs.existsSync(fullPathOfDep)) {
console.warn(`Uh-oh! Cannot find dependency "${fullPathOfDep}" in ${name}`); // eslint-disable-line no-console
Copy link
Collaborator

Choose a reason for hiding this comment

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

maybe we should run this line over the codebase without the '.js' and .json checks to see if it broke anything else?

Copy link
Member Author

Choose a reason for hiding this comment

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

maybe we should run this line over the codebase without the '.js' and .json checks to see if it broke anything else?

I was hoping tsc would catch any of those since it usually complains about "Cannot find module", but I can definitely run that over the codebase

Copy link
Collaborator

Choose a reason for hiding this comment

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

seems like this was all false alarms from the GH UI bug lol, so you can feel free to ignore that comment

@brendankenny
Copy link
Member Author

all the time. how else do you format?

haha, I guess the red underlines in my editor leave me unable to continue until I fix things, so it never gets too bad

how hard is it to write a fixer for a rule?

I don't think it'll be hard, it can mostly rip off what's done in dot-js.js

@brendankenny
Copy link
Member Author

updated with --fix support.

To test, I reverted the changes in #9006 and this PR and verified that the required paths were restored to the same fixed state (only had to add back the eslint-disable max-len comments to get back to exactly the same state)

const traceSaver = require('../../../lighthouse-core/lib/lantern-trace-saver.js');
const PredictivePerf = require('../../audits/predictive-perf.js');
const Simulator = require('../../lib/dependency-graph/simulator/simulator.js');
const traceSaver = require('../../lib/lantern-trace-saver.js');
Copy link
Member Author

Choose a reason for hiding this comment

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

the only path changes that aren't adding a suffix because these files are already under lighthouse-core/ :)

The lint rule actually won't flag or fix this case normally (since the paths do have a file extension and they resolve to a file), but these were changed when I was testing on the reverted #9006 (which added .js to them) and it seems worth keeping the simplified form

@@ -6,6 +6,7 @@
'use strict';

// NOTE: this require path does not resolve correctly.
// eslint-disable-next-line local-rules/require-file-extension
Copy link
Member Author

Choose a reason for hiding this comment

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

these are broken on purpose

@@ -5,7 +5,6 @@
*/
'use strict';

// eslint-disable-next-line
Copy link
Member Author

Choose a reason for hiding this comment

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

here and in network-analyzer-test.js were fixed in #9006 but the new --fix missed these because of these disable comments. I assume these were originally disabled for max-len but maybe moving files reduced the path length over time. It's fine without the disable now

tsconfig.json Outdated
@@ -18,6 +18,7 @@
"clients/**/*.js",
"build/**/*.js",
"./types/**/*.d.ts",
"*.js",
Copy link
Collaborator

Choose a reason for hiding this comment

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

what prompted this?

Copy link
Member Author

Choose a reason for hiding this comment

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

type checking ./eslint-local-rules.js. I can narrow it if it messes with people's local, untracked scripts :)

Copy link
Collaborator

Choose a reason for hiding this comment

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

yes please if we can :)

Copy link
Collaborator

@patrickhulce patrickhulce left a comment

Choose a reason for hiding this comment

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

LGTM! yay for cleaner PR reviews :D


context.report({
node: node,
message: 'Required path must have a file extension.',
Copy link
Collaborator

Choose a reason for hiding this comment

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

super nit but this message felt a bit weird to me since Required is so typically used for Necessary/Mandatory

brainstorm of alternatives

Path of module must have a file extension
Path of required module must have a file extension
Require path must have a file extension
Local require path must have a file extension
Path to require must have a file extension
Local path to require must have a file extension

if (!resolvedRequiredPath) {
return context.report({
node: node,
message: `Cannot resolve module '${requiredPath}'.`,
Copy link
Collaborator

Choose a reason for hiding this comment

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

FWIW, the eslint messages I see don't end in periods

Copy link
Member Author

Choose a reason for hiding this comment

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

FWIW, the eslint messages I see don't end in periods

interestingly eslint seems to take care of this itself. e.g.

let asdf = 'asdf';

when hovering in vscode give

'asdf' is assigned a value but never used. Allowed unused vars must match /(^_$)/.
'asdf' is never reassigned. Use 'const' instead.

(with periods) and they do have periods in the source.

But then in the CLI output, you get

error 'asdf' is assigned a value but never used. Allowed unused vars must match /(^_$)/
error 'asdf' is never reassigned. Use 'const' instead

(no periods).

The same thing happens to this message, so I guess they auto-trim periods when printing? Pretty funny.

Copy link
Collaborator

Choose a reason for hiding this comment

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

woah that's awesome haha

const requireFileExtension = {
meta: {
docs: {
description: 'disallow require() without a file extension',
Copy link
Collaborator

Choose a reason for hiding this comment

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

or wait, is this the message that shows up in eslint CLI?

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 think this is actually if you generate docs for the rule?

On the command line you just get error Local require path must have a file extension

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ah gotcha!

// a suffix to the existing path, but sometimes humans write confusing
// paths, e.g. './lighthouse-core/lib/../lib/lh-error.js'. To cover both
// cases, double check that the paths resolve to the same file.
const resolvedFixedPath = requireResolveOrNull(fixedPath, contextDirname);
Copy link
Collaborator

Choose a reason for hiding this comment

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

is there a reason all this is outside fix() if we only use it for the fix case?

Copy link
Member Author

Choose a reason for hiding this comment

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

is there a reason all this is outside fix() if we only use it for the fix case?

Good point, it'll save time in the non --fix case if it's inside fix(), so I'll move it.

Copy link
Member Author

Choose a reason for hiding this comment

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

Good point, it'll save time in the non --fix case if it's inside fix(), so I'll move it.

well, only if there are a bunch of resolvable requires that don't have a file extension, and I'd imagine most of them would be hitting the require cache, so in practice it won't really save any time, but it looks less overwhelming inside fix() than I thought it would and it makes sense to keep it together

Copy link
Collaborator

Choose a reason for hiding this comment

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

yeah I wasn't really thinking about speed, just relatedness 👍

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

Successfully merging this pull request may close these issues.

None yet

3 participants