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

feat: react-scripts lint command #1217 #3850

Closed

Conversation

maciej-ka
Copy link
Contributor

@maciej-ka maciej-ka commented Jan 18, 2018

Adds lint command to react-scripts.

Typical use will be to:

  1. add to package.json scripts:
  "scripts": {
    ...,
    "lint": "react-scripts lint"
  }

EDIT: PR provides a default configuration for ESlinter, so these are no longer needed:
2. yarn lint --init
3. yarn to install new dependencies
4. yarn lint to use

Script passes all options to eslint, so this will work yarn lint --fix. Files checked are: .js, .jsx and .mjs in src folder. This includes test files too.

Copy link
Contributor

@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 doesn’t work after eject.

@maciej-ka
Copy link
Contributor Author

That's on purpose to drive away from ejecting.
Thank you for checking. I'll try to solve this problem today.

@maciej-ka
Copy link
Contributor Author

Fixed and tested. Should work after ejecting now.

@@ -55,6 +55,10 @@ switch (script) {
process.exit(result.status);
break;
}
case 'lint': {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why does it need a separate case, and not using the same logic as above?

Copy link
Contributor Author

@maciej-ka maciej-ka Jan 19, 2018

Choose a reason for hiding this comment

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

I wasn't sure is node spawn needed in this case. But now I see. This way script feels no difference between being called with react-script command and after eject with node scripts/command.

{ stdio: 'inherit' }
);
if (args.indexOf('--init') >= 0) {
console.log(
Copy link
Contributor

Choose a reason for hiding this comment

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

What is this for? I don't understand.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

A part of eslint --init wizard is to install dependencies using npm i. Since we have yarn, this will make a small mess. This line of code is to inform, that after react-scripts lint project has to be rebuilt with rm node_modules && yarn and possibly rm package-lock.json.

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 added this info after losing some time figuring out why all commands don't work anymore after react-scripts lint.

@maciej-ka
Copy link
Contributor Author

Lint case unified with rest of commands in bin/react-scripts.js.
Small instruction after running lint --init wizard stayed. Maybe let's remove it after eslint --init gets new option --use-yarn?

@maciej-ka
Copy link
Contributor Author

@gaearon should project that was built by create-react-app come with default eslinter configuration? This is a suggested approach by ESLinter team.

At the moment we don't provide such a file and users will probably need to make a copy of rules from other project or create a new eslinter config with react-scripts lint --init wizard.

@gaearon
Copy link
Contributor

gaearon commented Jan 20, 2018

I don’t think we want to add any wizard here.

By default it should use the lint config that’s already used by default by commands like npm start. Specifically, eslint-config-react-app.

@maciej-ka
Copy link
Contributor Author

Thanks. I'll look for a way to integrate them into react-script lint.

@maciej-ka
Copy link
Contributor Author

Added a default ESlint configuration, removed warning about --init.

Copy link
Contributor

@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.

See below; we'll also need integration tests for this.

For example you could use some existing integration test in the tasks/ folder and verify that running ./node_modules/.bin/react-scripts lint before ejecting and node scripts/lint.js after ejecting doesn't break.

@@ -0,0 +1,3 @@
{
"extends": "react-app"
}
Copy link
Contributor

Choose a reason for hiding this comment

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

After ejecting we already put eslint config into the package.json (as far as I remember).
Why is this necessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Great to know ... now ... after few days of working on it (madman laugh: iiihahahaha).

Copy link
Contributor Author

@maciej-ka maciej-ka Jan 21, 2018

Choose a reason for hiding this comment

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

This file was added to run linter before ejecting.
Do you know how is it possible to have same effect but without such a file?

Side note: ESlinter can be run programmicaly:

const CLIEngine = require("eslint").CLIEngine;
const cli = new CLIEngine({
    configFile: require.resolve('../config/eslint.json'),
    fix: process.argv.slice(2).indexOf('--fix') >= 0
});
const report = cli.executeOnFiles(["src/**/*.{js,jsx,mjs}"]);
CLIEngine.outputFixes(report);
const formatter = cli.getFormatter();

console.log(formatter(report.results));

But it still seems it needs a config file. In supported options it is possible to pass rules, but not extends. And, sadly, running above snippet after ejecting doesn't work, because eslint cannot find jsx-a11y definitions, even with this line added: plugins: ['import', 'flowtype', 'jsx-a11y', 'react']

Copy link
Contributor

Choose a reason for hiding this comment

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

Can baseConfig argument help?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, it works
baseConfig: { extends: "react-app" },

however, after ejecting linter now reports

/private/tmp/demo-15/src/App.js
  1:1  warning  Definition for rule 'jsx-a11y/href-no-hash' was not found  jsx-a11y/href-no-hash

/private/tmp/demo-15/src/App.test.js
  1:1  warning  Definition for rule 'jsx-a11y/href-no-hash' was not found  jsx-a11y/href-no-hash

/private/tmp/demo-15/src/index.js
  1:1  warning  Definition for rule 'jsx-a11y/href-no-hash' was not found  jsx-a11y/href-no-hash

/private/tmp/demo-15/src/registerServiceWorker.js
  1:1  warning  Definition for rule 'jsx-a11y/href-no-hash' was not found  jsx-a11y/href-no-hash

✖ 4 problems (0 errors, 4 warnings)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Adding plugins: ['jxs-a11y'] throws `Error: Failed to load plugin jxs-a11y: Cannot find module 'eslint-plugin-jxs-a11y' (after yarn eject)

And trying these options didn't help

  cwd: '/tmp/demo-15',
  rulePaths: [
    path.dirname(require.resolve('eslint-plugin-jsx-a11y')) + '/rules'
  ],

I wonder, why having baseConfig: { extends: "react-app" } in scripts/lint.js would be better than an explicit config file in the config directory? Both of these duplicate entry in package.json.

.concat([
'src/**/*.{js,jsx,mjs}',
'--config',
require.resolve('../config/eslint.json'),
Copy link
Contributor

@viankakrisna viankakrisna Jan 23, 2018

Choose a reason for hiding this comment

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

i think this line can just be

    require.resolve('eslint-config-react-app')

Copy link
Contributor

Choose a reason for hiding this comment

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

after eject it needs to be

   require.resolve(paths.appPackageJson),

though

Copy link
Contributor

@viankakrisna viankakrisna Jan 23, 2018

Choose a reason for hiding this comment

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

i think we can do it by playing around with eject comments 😅

// @remove-on-eject-begin
/**
 * Copyright (c) 2015-present, Facebook, Inc.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
// @remove-on-eject-end
"use strict";

const spawn = require("react-dev-utils/crossSpawn");
const args = process.argv.slice(2);
// @remove-on-eject-begin
/**
// @remove-on-eject-end
const paths = require("../config/paths");
// @remove-on-eject-begin
*/
// @remove-on-eject-end

const result = spawn.sync(
  "node",
  [require.resolve("eslint/bin/eslint")]
    .concat([
      "src/**/*.{js,jsx,mjs}",
      "--config",
      // @remove-on-eject-begin
      /**
      // @remove-on-eject-end
       require.resolve(paths.appPackageJson),
       // @remove-on-eject-begin
       */
      // @remove-on-eject-end
      // @remove-on-eject-begin
      require.resolve("eslint-config-react-app")
      // @remove-on-eject-end
    ])
    .concat(args),
  { stdio: "inherit" }
);

process.exit(result.status);

Copy link
Contributor

Choose a reason for hiding this comment

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

or just

// @remove-on-eject-begin
/**
 * Copyright (c) 2015-present, Facebook, Inc.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
// @remove-on-eject-end
"use strict";

const spawn = require("react-dev-utils/crossSpawn");
const args = process.argv.slice(2);

const result = spawn.sync(
  "node",
  [require.resolve("eslint/bin/eslint")]
    .concat([
      "src/**/*.{js,jsx,mjs}",
      // @remove-on-eject-begin
      "--config",
      require.resolve("eslint-config-react-app")
      // @remove-on-eject-end
    ])
    .concat(args),
  { stdio: "inherit" }
);

process.exit(result.status);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you so much! Suggestion to userequire.resolve('eslint-config-react-app') as a config path is a gold.

@maciej-ka maciej-ka force-pushed the react-scripts-lint-command branch 2 times, most recently from 7dc0f55 to 9998ca2 Compare January 23, 2018 21:09
const CLIEngine = require('eslint').CLIEngine;

const cli = new CLIEngine({
configFile: require.resolve('eslint-config-react-app'),
Copy link
Contributor

Choose a reason for hiding this comment

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

hardcoding this won't respect the changes that user made in package.json or .eslintrc post eject. IMO we should wrap this line:

// @remove-on-eject-begin
    configFile: require.resolve('eslint-config-react-app'),
// @remove-on-eject-end

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes ... but then react-scripts lint after eject has few errors:

/private/tmp/demo20/src/App.js
  1:1   warning  Definition for rule 'jsx-a11y/href-no-hash' was not found  jsx-a11y/href-no-hash
 
/private/tmp/demo20/src/App.test.js
  1:1  warning  Definition for rule 'jsx-a11y/href-no-hash' was not found  jsx-a11y/href-no-hash

/private/tmp/demo20/src/index.js
  1:1  warning  Definition for rule 'jsx-a11y/href-no-hash' was not found  jsx-a11y/href-no-hash

/private/tmp/demo20/src/registerServiceWorker.js
  1:1  warning  Definition for rule 'jsx-a11y/href-no-hash' was not found  jsx-a11y/href-no-hash

✖ 4 problems (0 errors, 4 warnings)

Copy link
Contributor

Choose a reason for hiding this comment

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

hmm, do you have eslint installed globally / .eslintrc file somewhere in the parent folder? i think we need to add root: true to eslintConfig property in package.json post eject. https://eslint.org/docs/user-guide/configuring#using-configuration-files can you try adding it and see if it fix the problem?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sadly, no change after:

  "eslintConfig": {
    "root": true,
    "extends": "react-app"
  }

Copy link
Contributor Author

@maciej-ka maciej-ka Jan 23, 2018

Choose a reason for hiding this comment

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

Also adding plugins: ['jsx-a11y'] to CLIEngine constructor options doesn't remove errors.

Copy link
Contributor

Choose a reason for hiding this comment

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

maybe running it with DEBUG=eslint:* yarn lint would help you found the problem. I cannot reproduce it locally using the same code :(

Copy link
Contributor

Choose a reason for hiding this comment

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

or DEBUG=eslint:config* yarn lint if you just want the log related to config

@maciej-ka maciej-ka force-pushed the react-scripts-lint-command branch 5 times, most recently from a28a0f8 to 7a5ab30 Compare January 24, 2018 08:44
@maciej-ka
Copy link
Contributor Author

E2E tests added, removed eslint config file. Huge thanks and ❤️ to @viankakrisna

@viankakrisna
Copy link
Contributor

viankakrisna commented Jan 24, 2018

happy to help! so do you find what is the cause on the issue that you have on your machine? how do we avoid ejected users have the same issue as you?

@maciej-ka
Copy link
Contributor Author

I haven't found the reason why this version of react-scripts lint is breaking, after eject, on my local dev. I had ESLint installed globally but it didn't help when I removed it. I hope to find some time to debug ESLint internals.

@viankakrisna
Copy link
Contributor

what's the output of DEBUG=eslint:config* yarn lint ?

@maciej-ka
Copy link
Contributor Author

Ha, it works now. Even when I install ESLinter globally.
Maybe it was some silly mistake from my side yesterday (like using a wrong version of react-scripts).

@sbdchd
Copy link

sbdchd commented Jan 30, 2018

Might be a good idea to trigger process.exit(1); if there are errors, since the eslint CLIEngine won't do it itself.

@maciej-ka
Copy link
Contributor Author

That would allow chaining yarn lint && .... Let's add it. I will return process.exit(1) if there was any linting error (but not a warning). Thanks for the idea!

@maciej-ka maciej-ka force-pushed the react-scripts-lint-command branch 2 times, most recently from 2529a76 to 67a774d Compare January 30, 2018 21:21
CLIEngine.outputFixes(report);
console.log(formatter(report.results));

if (report.errorCount > 0) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Condition to trigger exit(1) could be different when a --fix flag was used.

Report object has a count of fixable errors and it is possible to count how many unfixed errors are left:

{
  '...': '...',
  errorCount: 1,
  fixableErrorCount: 1,
}

My concern is that in react-scripts lint --fix && git push files will be changed by lint but they still need git commit.

@uLan08
Copy link

uLan08 commented Apr 12, 2018

Any updates on this?

@Timer Timer closed this Sep 26, 2018
@Timer Timer reopened this Sep 26, 2018
@SirBif
Copy link

SirBif commented Oct 3, 2018

I'm trying to understand what's missing to close this PR. I was checking if I could help, but I have the impression this is just a rebase away from being ready.

  • the e2e tests requested by @gaearon are there
  • there is a merge conflict (which I believe is what's causing appveyor CI to fail)

Did I get that right @maciej-ka ?

@gaearon gaearon closed this Nov 1, 2018
@kfei
Copy link

kfei commented Jan 8, 2019

Is this PR rejected?

@lock lock bot locked and limited conversation to collaborators Jan 13, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants