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
Update: Resolve npm installed formatters (#5900) #9464
Conversation
LGTM |
LGTM |
Not sure what happened to |
Closing/reopening PR to restart CI. |
@platinumazure Thanks! Is there anything particular I need to do to initiate a review of this PR? |
@testower Nah, the team will get to it soon. @eslint/eslint-team Anyone want to review this? |
lib/cli-engine.js
Outdated
@@ -670,15 +673,21 @@ class CLIEngine { | |||
// replace \ with / for Windows compatibility | |||
format = format.replace(/\\/g, "/"); | |||
|
|||
const cwd = this.options ? this.options.cwd : process.cwd(); | |||
|
|||
let formatterPath; | |||
|
|||
// if there's a slash, then it's a 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.
This assumption doesn’t hold if I want to use a formatter from a scoped package on npm:
npm install @btmills/eslint-formatter-turtles
eslint -f @btmills/turtles ...
This raises two questions:
- Do we want to allow formatters from scoped packages?
- If I use a formatted in a scoped package, can I omit the
eslint-formatter-
prefix?
If the answer to both questions is yes, then we might be able to borrow the code that resolves plugins and shared configs, which handles those already. I’d find a link if I weren’t on my phone.
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. Seems like a reasonable requirement to me. Let me know if you find that link.
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.
Lines 40 to 77 in 1a962e8
/** | |
* Removes the prefix `eslint-plugin-` from a plugin name. | |
* @param {string} pluginName The name of the plugin which may have the prefix. | |
* @returns {string} The name of the plugin without prefix. | |
*/ | |
static removePrefix(pluginName) { | |
return pluginName.startsWith(PLUGIN_NAME_PREFIX) ? pluginName.slice(PLUGIN_NAME_PREFIX.length) : pluginName; | |
} | |
/** | |
* Gets the scope (namespace) of a plugin. | |
* @param {string} pluginName The name of the plugin which may have the prefix. | |
* @returns {string} The name of the plugins namepace if it has one. | |
*/ | |
static getNamespace(pluginName) { | |
return pluginName.match(NAMESPACE_REGEX) ? pluginName.match(NAMESPACE_REGEX)[0] : ""; | |
} | |
/** | |
* Removes the namespace from a plugin name. | |
* @param {string} pluginName The name of the plugin which may have the prefix. | |
* @returns {string} The name of the plugin without the namespace. | |
*/ | |
static removeNamespace(pluginName) { | |
return pluginName.replace(NAMESPACE_REGEX, ""); | |
} | |
/** | |
* Defines a plugin with a given name rather than loading from disk. | |
* @param {string} pluginName The name of the plugin to load. | |
* @param {Object} plugin The plugin object. | |
* @returns {void} | |
*/ | |
define(pluginName, plugin) { | |
const pluginNamespace = Plugins.getNamespace(pluginName), | |
pluginNameWithoutNamespace = Plugins.removeNamespace(pluginName), | |
pluginNameWithoutPrefix = Plugins.removePrefix(pluginNameWithoutNamespace), | |
shortName = pluginNamespace + pluginNameWithoutPrefix; |
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 have added test cases for the points raised by btmills and made them pass. I have also moved some helpers into a new "naming" utility (perhaps it should be called "prefix(-ing)"? The getFormatter is getting a bit complex, so I would prefer some refactoring, and maybe moving some logic to helper(s). What do you think.
LGTM |
LGTM |
LGTM |
LGTM |
LGTM |
lib/cli-engine.js
Outdated
const namespace = naming.getNamespaceFromTerm(format); | ||
|
||
if (namespace) { | ||
cwd = `${cwd}/${namespace}`; |
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 is this line doing, can you please explain the with a use case?
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.
Please see comment I left above regarding the same.
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.
Overall, great job! Left a couple of small comments. I think there's an issue with namespaced packages though.
lib/cli-engine.js
Outdated
const namespace = naming.getNamespaceFromTerm(format); | ||
|
||
if (namespace) { | ||
cwd = `${cwd}/${namespace}`; |
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'm not sure this line is correct. Namespace shouldn't affect path (as far as I know), it's just a way to point to a specific package in NPM repository.
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.
It should change the working directory to the "scope" of the scoped package. E.g. the package @foo/bar
will be in node_modules/@foo/bar
, so cwd should be node_modules/@foo
so that bar
can be resolved.
It's possible that I'm confused about something here, but when I install scoped packages this does indeed affect the path.
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.
Oh I see where I might have got confused. The module resolver should already be able to handle scoped packages, right? I only need to remove the namespace in order to enforce the prefix. The namespace should be added back when passing the format to the module resolver? That should simplify quite a bit I think.
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 have now updated the PR in an attempt to untangle this confusion. Hope that helps?
lib/cli-engine.js
Outdated
|
||
try { | ||
formatterPath = resolver.resolve(npmFormat, cwd); | ||
} catch (_) { |
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: Could you change _
to e
? We use e
everywhere in the codebase, just want to be consistent
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.
Done
@@ -0,0 +1,59 @@ | |||
/** |
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.
Nice! Good refactoring. However, I think config/config-file.js
can also be updated to use this.
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.
- Did you intend for this to happen within this current pull request?
- Just to clarify the scope of your request, this is related to
normalizePackageName
?
It seems after looking more closely that normalizePackageName
can probably be reused in some way to make the logic in getFormatter a little nicer. So moving normalizePackageName
in some way to the naming util looks like a good option.
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.
Ok I moved the normalizePackageName to the naming util altogether. It simplified the logic in getFormatter quite a bit.
aaa484f
to
db7073c
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.
Love the direction this is going in! Just left a couple of minor comments.
lib/util/naming.js
Outdated
* @returns {string} The namepace of the term if it has one. | ||
*/ | ||
function getNamespaceFromTerm(term) { | ||
return term.match(NAMESPACE_REGEX) ? term.match(NAMESPACE_REGEX)[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.
Would it be possible to avoid executing the regular expression twice by storing the match result in a local variable?
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 comments @platinumazure I have updated as per your suggestions and tweaked some of the jsdoc / comments further, since the normalizaPackageName now applies not only to plugins and configs, but also to formatters.
lib/util/naming.js
Outdated
@@ -0,0 +1,110 @@ | |||
/** | |||
* @fileoverview Common helpers naming of plugins and formatters |
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.
Nitpick: Seems to be missing a word here, maybe something like "Common helpers for naming of plugins and formatters"?
All issues have been fixed
Thanks for PR @testower ! |
What is the purpose of this pull request? (put an "X" next to item)
[ ] Documentation update
[ ] Bug fix (template)
[ ] New rule (template)
[ ] Changes an existing rule (template)
[ ] Add autofixing to a rule
[ ] Add a CLI option
[x] Add something to the core
[ ] Other, please explain:
What changes did you make? (Give an overview)
Attempt to solve issue:5900.
CLIEngine#getFormatter will check if format is resolvable as
eslint-formatter-{format}
. Providing theeslint-formatter-
prefix in the format argument is optional. I have added unit test coverage for cli engine and documentation for the --format / -f has been updated.Is there anything you'd like reviewers to focus on?
This is my first pull request in eslint. Be gentle :)