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

Update: Resolve npm installed formatters (#5900) #9464

Merged
merged 1 commit into from Nov 1, 2017

Conversation

testower
Copy link
Contributor

@testower testower commented Oct 16, 2017

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 the eslint-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 :)

@eslintbot
Copy link

LGTM

@aladdin-add aladdin-add added accepted There is consensus among the team that this change meets the criteria for inclusion core Relates to ESLint's core APIs and features enhancement This change enhances an existing feature of ESLint labels Oct 16, 2017
@eslintbot
Copy link

LGTM

@testower testower changed the title Update: Resolve npm installed formatters Update: Resolve npm installed formatters (#5900) Oct 17, 2017
@testower
Copy link
Contributor Author

Not sure what happened to continuous-integration/appveyor/pr, it passed on the previous run, and the only change is the commit message (added issue number).

@platinumazure
Copy link
Member

Closing/reopening PR to restart CI.

@testower
Copy link
Contributor Author

@platinumazure Thanks! Is there anything particular I need to do to initiate a review of this PR?

@platinumazure
Copy link
Member

@testower Nah, the team will get to it soon.

@eslint/eslint-team Anyone want to review this?

@@ -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
Copy link
Member

@btmills btmills Oct 19, 2017

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:

  1. Do we want to allow formatters from scoped packages?
  2. 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.

Copy link
Contributor Author

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.

Copy link
Member

Choose a reason for hiding this comment

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

/**
* 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;

Copy link
Contributor Author

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.

@eslintbot
Copy link

LGTM

@eslintbot
Copy link

LGTM

@eslintbot
Copy link

LGTM

@eslintbot
Copy link

LGTM

@eslintbot
Copy link

LGTM

@testower testower closed this Oct 22, 2017
@testower testower reopened this Oct 22, 2017
const namespace = naming.getNamespaceFromTerm(format);

if (namespace) {
cwd = `${cwd}/${namespace}`;
Copy link
Member

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?

Copy link
Contributor Author

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.

Copy link
Member

@ilyavolodin ilyavolodin left a 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.

const namespace = naming.getNamespaceFromTerm(format);

if (namespace) {
cwd = `${cwd}/${namespace}`;
Copy link
Member

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.

Copy link
Contributor Author

@testower testower Oct 27, 2017

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.

Copy link
Contributor Author

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.

Copy link
Contributor Author

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?


try {
formatterPath = resolver.resolve(npmFormat, cwd);
} catch (_) {
Copy link
Member

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

Copy link
Contributor Author

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 @@
/**
Copy link
Member

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.

Copy link
Contributor Author

@testower testower Oct 28, 2017

Choose a reason for hiding this comment

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

  1. Did you intend for this to happen within this current pull request?
  2. 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.

Copy link
Contributor Author

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.

@testower testower force-pushed the issue5900 branch 2 times, most recently from aaa484f to db7073c Compare October 28, 2017 17:26
Copy link
Member

@platinumazure platinumazure left a 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.

* @returns {string} The namepace of the term if it has one.
*/
function getNamespaceFromTerm(term) {
return term.match(NAMESPACE_REGEX) ? term.match(NAMESPACE_REGEX)[0] : "";
Copy link
Member

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?

Copy link
Contributor Author

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.

@@ -0,0 +1,110 @@
/**
* @fileoverview Common helpers naming of plugins and formatters
Copy link
Member

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"?

@ilyavolodin ilyavolodin dismissed platinumazure’s stale review November 1, 2017 21:43

All issues have been fixed

@ilyavolodin ilyavolodin merged commit 4d7d7ab into eslint:master Nov 1, 2017
@ilyavolodin
Copy link
Member

Thanks for PR @testower !

@eslint-deprecated eslint-deprecated bot locked and limited conversation to collaborators May 1, 2018
@eslint-deprecated eslint-deprecated bot added the archived due to age This issue has been archived; please open a new issue for any further discussion label May 1, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
accepted There is consensus among the team that this change meets the criteria for inclusion archived due to age This issue has been archived; please open a new issue for any further discussion core Relates to ESLint's core APIs and features enhancement This change enhances an existing feature of ESLint
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants