Skip to content

add lint command to react-scripts #11556

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

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions packages/react-scripts/bin/react-scripts.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,17 @@ const spawn = require('react-dev-utils/crossSpawn');
const args = process.argv.slice(2);

const scriptIndex = args.findIndex(
x => x === 'build' || x === 'eject' || x === 'start' || x === 'test'
x =>
x === 'build' ||
x === 'eject' ||
x === 'start' ||
x === 'test' ||
x === 'lint'
);
const script = scriptIndex === -1 ? args[0] : args[scriptIndex];
const nodeArgs = scriptIndex > 0 ? args.slice(0, scriptIndex) : [];

if (['build', 'eject', 'start', 'test'].includes(script)) {
if (['build', 'eject', 'start', 'test', 'lint'].includes(script)) {
const result = spawn.sync(
process.execPath,
nodeArgs
Expand Down
31 changes: 31 additions & 0 deletions packages/react-scripts/config/eslintConfig.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// @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 path = require('path');
const paths = require('./paths');
const modules = require('./modules');

const baseConfig = {
extends: [require.resolve('eslint-config-react-app/base')],
rules: {
...(!modules.hasJsxRuntime && {
'react/react-in-jsx-scope': 'error',
}),
},
};

module.exports = {
extensions: ['js', 'mjs', 'jsx', 'ts', 'tsx'],
cache: true,
cacheLocation: path.resolve(paths.appNodeModules, '.cache/.eslintcache'),
cwd: paths.appPath,
resolvePluginsRelativeTo: __dirname,
baseConfig,
};
14 changes: 14 additions & 0 deletions packages/react-scripts/config/modules.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,11 +131,25 @@ function getModules() {

const additionalModulePaths = getAdditionalModulePaths(options);

const hasJsxRuntime = (() => {
if (process.env.DISABLE_NEW_JSX_TRANSFORM === 'true') {
return false;
}

try {
require.resolve('react/jsx-runtime');
return true;
} catch (e) {
return false;
}
})();

return {
additionalModulePaths: additionalModulePaths,
webpackAliases: getWebpackAliases(options),
jestAliases: getJestAliases(options),
hasTsConfig,
hasJsxRuntime,
};
}

Expand Down
34 changes: 3 additions & 31 deletions packages/react-scripts/config/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent');
const ESLintPlugin = require('eslint-webpack-plugin');
const eslintConfig = require('./eslintConfig');
const paths = require('./paths');
const modules = require('./modules');
const getClientEnvironment = require('./env');
Expand Down Expand Up @@ -82,19 +83,6 @@ const cssModuleRegex = /\.module\.css$/;
const sassRegex = /\.(scss|sass)$/;
const sassModuleRegex = /\.module\.(scss|sass)$/;

const hasJsxRuntime = (() => {
if (process.env.DISABLE_NEW_JSX_TRANSFORM === 'true') {
return false;
}

try {
require.resolve('react/jsx-runtime');
return true;
} catch (e) {
return false;
}
})();

// This is the production and development configuration.
// It is focused on developer experience, fast rebuilds, and a minimal bundle.
module.exports = function (webpackEnv) {
Expand Down Expand Up @@ -423,7 +411,7 @@ module.exports = function (webpackEnv) {
[
require.resolve('babel-preset-react-app'),
{
runtime: hasJsxRuntime ? 'automatic' : 'classic',
runtime: modules.hasJsxRuntime ? 'automatic' : 'classic',
},
],
],
Expand Down Expand Up @@ -764,27 +752,11 @@ module.exports = function (webpackEnv) {
!disableESLintPlugin &&
new ESLintPlugin({
// Plugin options
extensions: ['js', 'mjs', 'jsx', 'ts', 'tsx'],
formatter: require.resolve('react-dev-utils/eslintFormatter'),
eslintPath: require.resolve('eslint'),
failOnError: !(isEnvDevelopment && emitErrorsAsWarnings),
context: paths.appSrc,
cache: true,
cacheLocation: path.resolve(
paths.appNodeModules,
'.cache/.eslintcache'
),
// ESLint class options
cwd: paths.appPath,
resolvePluginsRelativeTo: __dirname,
baseConfig: {
extends: [require.resolve('eslint-config-react-app/base')],
rules: {
...(!hasJsxRuntime && {
'react/react-in-jsx-scope': 'error',
}),
},
},
...eslintConfig,
}),
].filter(Boolean),
// Turn off performance processing because we utilize
Expand Down
40 changes: 40 additions & 0 deletions packages/react-scripts/scripts/lint.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// @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';

// Do this as the first thing so that any code reading it knows the right env.
process.env.BABEL_ENV =
process.env.BABEL_ENV || process.env.NODE_ENV || 'development';
process.env.NODE_ENV = process.env.NODE_ENV || 'development';

// Makes the script crash on unhandled rejections instead of silently
// ignoring them. In the future, promise rejections that are not handled will
// terminate the Node.js process with a non-zero exit code.
process.on('unhandledRejection', err => {
throw err;
});

// Ensure environment variables are read.
require('../config/env');

const paths = require('../config/paths');
const eslintConfig = require('../config/eslintConfig');
const formatter = require('react-dev-utils/eslintFormatter');

const { ESLint } = require('eslint');

(async function main() {
const eslint = new ESLint(eslintConfig);
const results = await eslint.lintFiles([paths.appSrc]);
const resultText = formatter(results);
console.log(resultText);
})().catch(error => {
process.exitCode = 1;
console.error(error);
});
6 changes: 6 additions & 0 deletions tasks/e2e-simple.sh
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,9 @@ function verify_module_scope {
# Enter the app directory
cd test-app

# Test linter
npx react-scripts lint

# Test the build
npm run build
# Check for expected output
Expand Down Expand Up @@ -246,6 +249,9 @@ echo yes | npm run eject
# Test ejected files were staged
test -n "$(git diff --staged --name-only)"

# Test linter
node scripts/lint.js

# Test the build
npm run build
# Check for expected output
Expand Down