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

use different eslint config for es6 and es5 #11794

Merged
merged 11 commits into from
Dec 11, 2017

Conversation

ctxhou
Copy link
Contributor

@ctxhou ctxhou commented Dec 7, 2017

This pr uses different eslint config to solve issues #11783.

The following is the updated part:

So now, when we run yarn lint or yarn linc, it will lint es5 and es6 separately.


module.exports = {
config: eslintrc,
OFF,
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this is a bit confusing. Let's just copy and paste these constants, and completely remove this file :-) ESLint also actually accepts strings so we can later remove them altogether.

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

@@ -0,0 +1,3 @@
const baseConfig = require('./baseConfig');

module.exports = baseConfig.config;
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think there's more to it. I think we want to have ecmaVersion set to 6 for the ES6 config and to 5 for the ES5 config. It's not just about var, but about all the other features too.

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 add it on new commit.

const file = jsFiles[index];
for (let pathIndex in es5Path) {
const pattern = es5Path[pathIndex];
const isES5 = minimatch(file, pattern);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this efficient? What's the full yarn lint time before and after the change?

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 also want to refactor it. But currently I don't think of other refactor way.
But it doesn't affect too much efficiency, because this script will only be used on yarn linc to distinguish es5 and es6 files: here.

For yarn lint, because the es5, and es6 pattern is known, we don't need to judge files' ecma version.

Here is the yarn lint run time after the change:

Before: 22.50s
After: 17.86s.

The reason for runtime decrease is because, before the change, the filePattern was [.] (ref), after the change we use specific es path.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe there’s something clever we can do to have ESLint do the filtering instead of us? I’m not sure but since Prettier can do this, maybe you can find a way with ESLint API too.

'packages/*/src/**/*.js',
'packages/shared/**/*.js',
],
patterns: es6Path,
ignore: ['**/node_modules/**'],
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should ignore patterns also be a part of the respective configs? That would make sense to me. The fact that they’re currently duplicated in eslintignore is a bit confusing.

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'll move it to respective config.
Does it mean we should put all the eslintignore setting to the shared config file?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I guess that could mess with the editor integrations people on the team might be using. So maybe we shouldn’t do it. What do you 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 think we should keep the .eslintrc to make editor work as usual.
If we want to avoid ignore pattern duplicate, maybe we can write a script to read .eslintrc to use in prettier ignore part.

Copy link
Collaborator

Choose a reason for hiding this comment

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

That sounds a bit convoluted.

How about:

  • We use top level eslintignore/prettierignore for things that should never be touched (like build products and third party code)
  • For ES5/ES6, we always negate the other pattern to get a list of extra ignores. For example ES5 = everything in ES5 patterns except anything that is ES6. And vice versa.

Copy link
Collaborator

Choose a reason for hiding this comment

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

It’s even simpler.

ES6 doesn’t need a pattern. It should just be “everything”. The global ignores (in ESLint and Prettier top lebel ignore files) should filter out build artefacts and dependencies.

ES5 needs a pattern that just says “these files are ES5”. It’s a subset of all files.

You run ES5 passing it as “include” set.

Then you run ES6 by passing ES5 as “exclude” set. Because everything else is ES6 (or already excluded by global config).

In either case ES5 is the only pattern that needs to be explicitly specified.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Cool! It's more clear. I will correct it in this way.

BTW, for the lint change, eslint provide a ignorePattern params.
So when linting es5 files, we can pass es6 path as ignore pattern, vice versa.

However, it will cause this warning when the file is ignored: (related issue: eslint/eslint#5623)

0:0  warning  File ignored because of a matching ignore pattern. Use "--no-ignore" to override

To turn off this warning is to set quiet as true, but it will quiet "all warning". I think it's inappropriate.
Therefore, I think write the filter to distinguish es5 files and es6 files is needed. I'll try a cleaner method to do it.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we can just filter this particular warning from the output (and maybe raise an issue to ask for an option to completely disable this warning).

const minimatch = require('minimatch');
const {es5Path} = require('./esPath');

module.exports = function(jsFiles) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

If this is only used in linc task let’s just move it and inline it there. I wouldn’t worry too much about having a test for this file.

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

const file = jsFiles[index];
for (let pathIndex in es5Path) {
const pattern = es5Path[pathIndex];
const isES5 = minimatch(file, pattern);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe there’s something clever we can do to have ESLint do the filtering instead of us? I’m not sure but since Prettier can do this, maybe you can find a way with ESLint API too.

@ctxhou
Copy link
Contributor Author

ctxhou commented Dec 8, 2017

@gaearon, this new commit I use eslint params ignorePattern to filter out another es version's file.

In the earlier discussion, we talk about use .prettierignore file to list ignore files. But currently, prettier's node api doesn't suppport read .prettierignore, it only support pass source content.
It means that we still need to keep current method passing the needed content to prettier. So that's why I didn't add .prettierignore file.

@@ -8,12 +8,30 @@
'use strict';

const CLIEngine = require('eslint').CLIEngine;
const cli = new CLIEngine();
const formatter = cli.getFormatter();
const eslintPath = './scripts/eslint';
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is called __dirname in Node :-)

var ERROR = 2;

module.exports = Object.assign({}, eslintrc, {
parseOptions: {
Copy link
Collaborator

Choose a reason for hiding this comment

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

parserOptions, not parseOptions.

@@ -0,0 +1,16 @@
const eslintrc = require('../../.eslintrc');
Copy link
Collaborator

@gaearon gaearon Dec 8, 2017

Choose a reason for hiding this comment

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

I don't see how this could possibly work. By the pattern, this file should be treated as ES5. But then const should not parse. However it lints just fine.

This brings up a separate point: we're actually okay with treating our Node /scripts/ as ES6. It's just that we don't want trailing commas in them. So maybe we need three different file types (ESnext+JSX, ES6, and ES5).

Copy link
Contributor Author

@ctxhou ctxhou Dec 8, 2017

Choose a reason for hiding this comment

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

So the main different lint setting of ES6 and ESnext is we turn off comma-dangle rule at es6?
Currently, I can't find other different of ESnext and ES6 config.

Copy link
Collaborator

Choose a reason for hiding this comment

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

JSX, Flow should also be disabled in ES6. And it should use the espree parser.

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, so there are some files in scripts should be ignored.

Like:
/scripts/flow/environment.js
/scripts/rollup/shims/react-native/NativeMethodsMixin.js

Copy link
Collaborator

Choose a reason for hiding this comment

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

All shims should be treated as source (ESNext). Good catch.

@gaearon
Copy link
Collaborator

gaearon commented Dec 8, 2017

My impression is that ESLint CLIEngine ignores the explicit parserOptions configuration in favor of what the base config (fbjs) says. I don't know why this happens. It might be a bug.

@gaearon
Copy link
Collaborator

gaearon commented Dec 8, 2017

Seems like these are the options we need to use to force ESLint to treat file as ES5:

    parser: 'espree',
    parserOptions: {
      ecmaVersion: 5,
      sourceType: 'script'
    },

This won't work for our Node scripts, so as I mentioned above, we need to divide files into three different sets:

  • ESNext + JSX (source)
  • ES6-only files (scripts)
  • ES5 files (npm entry points)

And then supply different options above depending on the types.

@ctxhou
Copy link
Contributor Author

ctxhou commented Dec 8, 2017

@gaearon, I've pushed new commits to solve it.
Really sorry for some wrong commits because I didn't notice the prettier config.

As what we discussed, I separate the es path to ESNext, ES6, ES5, and those have independent eslint setting.
For prettier, default is ESNextPath, and scripts is ES5Path and ES6Path.

const config = {
  default: {
    patterns: esNextPath,
    ignore: ['**/node_modules/**'],
  },
  scripts: {
    patterns: [...es5Path, ...es6Path],
    ignore: [
      '**/node_modules/**',
      // Built files and React repo clone
      'scripts/bench/benchmarks/**',
      // shims & flow treat as ESNext
      'scripts/flow/*.js',
      'scripts/rollup/shims/**/*.js',
    ],
    options: {
      trailingComma: 'es5',
    },
  },
};

@@ -21,7 +21,7 @@ function createHTTP2Server(benchmark) {
__dirname,
'benchmarks',
benchmark,
request.url
request.url,
Copy link
Collaborator

Choose a reason for hiding this comment

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

This won't work. Everything inside scripts/bench (and scripts in general, except scripts/rollup/shims) should be normal ES6 without trailing commas in function calls. Otherwise it won't run on Node.

const stories = 50;

async function getStory(id) {
const storyRes = await fetch(`https://hacker-news.firebaseio.com/v0/item/${id}.json`);
const storyRes = await fetch(
`https://hacker-news.firebaseio.com/v0/item/${id}.json`,
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 see how this could work. It should be ES6 (which doesn't support trailing commas).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's my fault. On previous commit, I was uncareful to ignore benchmark, so it ran prettier on those files.
I've reverted it on latest commit.

sourceType: 'module',
ecmaFeatures: {
jsx: true,
experimentalObjectRestSpread: true,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why do we need these features for ES6? I’d expect both to be false.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Cause this files scripts/release/build-commands/parse-build-parameters.js use this syntax.
So we don't allow this syntax?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Why does it work? Did it get enabled by default in some Node version?

In this case let’s leave it on. But JSX shouldn’t be.

Copy link
Contributor Author

@ctxhou ctxhou Dec 10, 2017

Choose a reason for hiding this comment

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

I think that's because our devEngines is required node v8.x, so this syntax works.

Copy link
Collaborator

@gaearon gaearon left a comment

Choose a reason for hiding this comment

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

This is going in the right direction. Let's make a few changes.

  • Rename ESLint configs to eslintrc.source.js (ESNext), eslintrc.node.js (ES6) and eslintrc.npm.js (ES5).
  • The eslintrc.node.js config should not enable JSX or object spread. It also should have 'script' rather than 'module' type. Additionally, it should include a lint rule that requires 'use strict' to be specified in every file.
  • The eslintrc.npm.js config should disallow JSX.
  • fixtures/**/*.js should be ES6 (aka "node" in new naming), rather than ES5.
  • After these changes are done, please revert any changes to files that were automatic. Then rebase this PR on top of master. Then reapply the automatic changes. Otherwise this isn't up to date.

@ctxhou
Copy link
Contributor Author

ctxhou commented Dec 11, 2017

@gaearon, I've updated the config naming, revert automatic changed files and rebase to master 😄

yarn.lock Outdated
@@ -3358,7 +3358,7 @@ minimatch@^2.0.3:

minimatch@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
Copy link
Collaborator

Choose a reason for hiding this comment

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

Unintentional?

Copy link
Contributor

Choose a reason for hiding this comment

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

yarn seems to have a bug where it occasionally alternates between registries on some packages; not sure what causes it, but I've seen it once in a while

Copy link
Collaborator

Choose a reason for hiding this comment

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

I just mean we can safely omit this change from a PR.

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'll revert it.

ignore: [
'**/node_modules/**',
// Built files and React repo clone
'scripts/bench/benchmarks/**',
// shims & flow treat as ESNext
'scripts/flow/*.js',
'scripts/rollup/shims/**/*.js',
Copy link
Collaborator

Choose a reason for hiding this comment

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

It feels confusing that we still have to explicitly ignore some paths here but ESLint is fully covered by just “patterns” config.

@@ -8,12 +8,54 @@
'use strict';

const CLIEngine = require('eslint').CLIEngine;
const cli = new CLIEngine();
const formatter = cli.getFormatter();
const {npmPath, nodePath, sourcePath} = require('../shared/esPath');
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let’s rename that file to filesByLanguageVersion?

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

'scripts/flow/*.js',
'scripts/rollup/shims/**/*.js',
],
ignore: ['**/node_modules/**'],
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 think remove the scripts in ignore will cause other issues. Some patterns are covered (like 'scripts/flow/*.js') will run on both prettier process.

Because we run prettier twice (default -> scripts), the results seem work as our expect, but actually some changes on default prettier is covered by scripts prettier.

Maybe we can expand the scripts pattern to make it more clear?

const nodePath = [
  'scripts/babel/*.js',
  'scripts/circleci/*.js',
  'scripts/error-codes/*.js',
  ...
];

Copy link
Collaborator

Choose a reason for hiding this comment

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

I see. Let me look at that.

I think I can take this PR from here, thanks a lot!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're welcome. Thanks for your patience.

@jquense
Copy link
Contributor

jquense commented Dec 11, 2017

sorry to jump in late and snipe the PR! Eslint now supports overriding and configuration by file glob in the eslintrc. (example here) that may help simplify this a bit and reduce the extra code needed for the different paths

eslint docs: https://eslint.org/docs/user-guide/configuring#configuration-based-on-glob-patterns

@gaearon
Copy link
Collaborator

gaearon commented Dec 11, 2017

Oh nice. I think we'll get a version of this in first, but then file another issue to refactor it :-)

@gaearon gaearon merged commit e8e62eb into facebook:master Dec 11, 2017
@gaearon
Copy link
Collaborator

gaearon commented Dec 11, 2017

This is great. Thank you!

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.

5 participants