RN: Private Package for Monorepo ESLint Rules (#51858)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/51858

Creates a new private package for the ESLint rules that are defined in the `facebook/react-native` repository for linting the monorepo itself: `react-native/eslint-plugin-monorepo`

It resides in a new `private/` directory that will be used for all private packages. I plan to move private packages over from `packages/` and `tools/`.

This also eliminates the need for the `eslint-plugin-lint` dependency in open source.

Changelog:
[Internal]

Reviewed By: cortinico

Differential Revision: D76088698

fbshipit-source-id: 697d5d91421cda4ef3f96d0497a96954cd047296
This commit is contained in:
Tim Yung
2025-06-09 20:30:32 -07:00
committed by Facebook GitHub Bot
parent fb7a45a934
commit d17f7d0a29
37 changed files with 57 additions and 44 deletions
+12 -16
View File
@@ -10,16 +10,12 @@
'use strict';
const path = require('node:path');
require('eslint-plugin-lint').load(path.join(__dirname, 'tools/eslint/rules'));
module.exports = {
root: true,
extends: ['@react-native'],
plugins: ['@react-native/eslint-plugin-specs', 'lint'],
plugins: ['@react-native/monorepo', '@react-native/specs'],
overrides: [
// overriding the JS config from @react-native/eslint-config to ensure
@@ -28,6 +24,7 @@ module.exports = {
files: ['*.js', '*.js.flow', '*.jsx'],
parser: 'hermes-eslint',
rules: {
'@react-native/monorepo/sort-imports': 1,
'eslint-comments/no-unlimited-disable': 0,
'ft-flow/require-valid-file-annotation': [2, 'always'],
'no-extra-boolean-cast': 0,
@@ -35,7 +32,6 @@ module.exports = {
// These rules are not required with hermes-eslint
'ft-flow/define-flow-type': 0,
'ft-flow/use-flow-type': 0,
'lint/sort-imports': 1,
// Flow handles these checks for us, so they aren't required
'no-undef': 0,
'no-unreachable': 0,
@@ -55,7 +51,7 @@ module.exports = {
],
parser: 'hermes-eslint',
rules: {
'lint/no-commonjs-exports': 1,
'@react-native/monorepo/no-commonjs-exports': 1,
},
},
{
@@ -65,14 +61,14 @@ module.exports = {
{
files: ['package.json'],
rules: {
'lint/react-native-manifest': 2,
'@react-native/monorepo/react-native-manifest': 2,
},
},
{
files: ['flow-typed/**/*.js', 'packages/react-native/flow/**/*'],
rules: {
'@react-native/monorepo/valid-flow-typed-signature': 2,
'ft-flow/require-valid-file-annotation': 0,
'lint/valid-flow-typed-signature': 2,
'no-shadow': 0,
'no-unused-vars': 0,
quotes: 0,
@@ -84,14 +80,14 @@ module.exports = {
'packages/react-native/src/**/*.js',
],
rules: {
'@react-native/monorepo/no-haste-imports': 2,
'@react-native/monorepo/no-react-default-imports': 2,
'@react-native/monorepo/no-react-named-type-imports': 2,
'@react-native/monorepo/no-react-native-imports': 2,
'@react-native/monorepo/no-react-node-imports': 2,
'@react-native/monorepo/require-extends-error': 2,
'@react-native/platform-colors': 2,
'@react-native/specs/react-native-modules': 2,
'lint/no-haste-imports': 2,
'lint/no-react-native-imports': 2,
'lint/require-extends-error': 2,
'lint/no-react-node-imports': 2,
'lint/no-react-default-imports': 2,
'lint/no-react-named-type-imports': 2,
},
},
{
@@ -145,7 +141,7 @@ module.exports = {
{
files: ['**/__tests__/**'],
rules: {
'lint/no-react-native-imports': 'off',
'@react-native/monorepo/no-react-native-imports': 'off',
},
},
],
+1 -1
View File
@@ -12,7 +12,7 @@
'use strict';
// eslint-disable-next-line lint/sort-imports
// eslint-disable-next-line @react-native/monorepo/sort-imports
const {
transformFromAstSync: babelTransformFromAstSync,
transformSync: babelTransformSync,
+1 -1
View File
@@ -39,6 +39,7 @@
},
"workspaces": [
"packages/*",
"private/*",
"tools/*",
"!packages/helloworld"
],
@@ -73,7 +74,6 @@
"eslint-plugin-ft-flow": "^2.0.1",
"eslint-plugin-jest": "^27.9.0",
"eslint-plugin-jsx-a11y": "^6.6.0",
"eslint-plugin-lint": "^1.0.0",
"eslint-plugin-react": "^7.30.1",
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-native": "^4.0.0",
+1 -1
View File
@@ -40,5 +40,5 @@ function getAssetByID(assetId /*: number */) /*: PackagerAsset */ {
return assets[assetId - 1];
}
// eslint-disable-next-line lint/no-commonjs-exports
// eslint-disable-next-line @react-native/monorepo/no-commonjs-exports
module.exports = {registerAsset, getAssetByID};
+1 -1
View File
@@ -12,7 +12,7 @@
import {Command} from 'commander';
*/
// eslint-disable-next-line lint/sort-imports
// eslint-disable-next-line @react-native/monorepo/sort-imports
const {patchCoreCLIUtilsPackageJSON} = require('./scripts/monorepo');
function injectCoreCLIUtilsRuntimePatch() {
@@ -8,8 +8,6 @@
* @format
*/
// These don't actually exist anywhere in the code.
'use strict';
import type {ModuleConfig} from '../NativeModules';
@@ -35,5 +33,5 @@ const MessageQueueTestConfig = {
remoteModuleConfig: remoteModulesConfig,
};
// eslint-disable-next-line lint/no-commonjs-exports
// eslint-disable-next-line @react-native/monorepo/no-commonjs-exports
module.exports = MessageQueueTestConfig;
@@ -20,5 +20,5 @@ const MessageQueueTestModule = {
testHook2: function () {},
};
// eslint-disable-next-line lint/no-commonjs-exports
// eslint-disable-next-line @react-native/monorepo/no-commonjs-exports
module.exports = MessageQueueTestModule;
+1 -1
View File
@@ -27,5 +27,5 @@ const RelativeImageStub = (AssetRegistry.registerAsset({
type: 'png',
}): number);
// eslint-disable-next-line lint/no-commonjs-exports
// eslint-disable-next-line @react-native/monorepo/no-commonjs-exports
module.exports = RelativeImageStub;
@@ -51,7 +51,7 @@ export type {PublicRootInstance} from '../ReactNative/ReactFabricPublicInstance/
export type PublicTextInstance = ReturnType<createPublicTextInstance>;
// flowlint unsafe-getters-setters:off
// eslint-disable-next-line lint/no-commonjs-exports
// eslint-disable-next-line @react-native/monorepo/no-commonjs-exports
module.exports = {
get BatchedBridge(): BatchedBridge {
return require('../BatchedBridge/BatchedBridge').default;
@@ -70,7 +70,7 @@ const ERROR_CODES: {[string]: number} = {
DATA_CLONE_ERR: 25,
};
/* eslint-disable lint/require-extends-error */
/* eslint-disable @react-native/monorepo/require-extends-error */
// $FlowExpectedError[incompatible-variance] name is writable in Error but not in DOMException, but this is how it works on Web.
export default class DOMException extends Error {
static +INDEX_SIZE_ERR: 1;
@@ -8,8 +8,9 @@
* @format
*/
// eslint-disable-next-line lint/sort-imports
// eslint-disable-next-line @react-native/monorepo/sort-imports
import type Performance from '../Performance';
import {performanceEntryTypeToRaw} from '../internals/RawPerformanceEntry';
import {reportEntry} from '../specs/__mocks__/NativePerformanceMock';
+1 -1
View File
@@ -12,7 +12,7 @@
import {Command} from 'commander';
*/
// eslint-disable-next-line lint/sort-imports
// eslint-disable-next-line @react-native/monorepo/sort-imports
const {patchCoreCLIUtilsPackageJSON} = require('./scripts/monorepo');
function injectCoreCLIUtilsRuntimePatch() {
+7
View File
@@ -0,0 +1,7 @@
# @react-native/eslint-plugin-monorepo
This is a private package that contains ESLint rules for `react-native` itself.
They are configured in the `.eslintrc.js` at the repository root.
For ESLint rules intended for projects that consume React Native, add them to
the `@react-native-community/eslint-plugin` package instead.
@@ -15,7 +15,7 @@ import path from 'path';
const REPO_DIR = path.resolve(__dirname, '..', '..', '..');
describe('react-native/.eslintrc.js', () => {
describe('lint/sort-imports', () => {
describe('@react-native/monorepo/sort-imports', () => {
const testDirectories = [
'.',
'packages/react-native',
@@ -35,7 +35,7 @@ describe('react-native/.eslintrc.js', () => {
expect(config).toHaveProperty(
'rules',
expect.objectContaining({
'lint/sort-imports': expect.arrayContaining([1]),
'@react-native/monorepo/sort-imports': expect.arrayContaining([1]),
}),
);
});
+22
View File
@@ -0,0 +1,22 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
exports.rules = {
'no-commonjs-exports': require('./rules/no-commonjs-exports'),
'no-haste-imports': require('./rules/no-haste-imports'),
'no-react-default-imports': require('./rules/no-react-default-imports'),
'no-react-named-type-imports': require('./rules/no-react-named-type-imports'),
'no-react-native-imports': require('./rules/no-react-native-imports'),
'no-react-node-imports': require('./rules/no-react-node-imports'),
'react-native-manifest': require('./rules/react-native-manifest'),
'require-extends-error': require('./rules/require-extends-error'),
'sort-imports': require('./rules/sort-imports'),
'valid-flow-typed-signature': require('./rules/valid-flow-typed-signature'),
};
@@ -1,7 +1,8 @@
{
"name": "@react-native/eslint",
"name": "@react-native/eslint-plugin-monorepo",
"private": true,
"version": "0.0.0",
"main": "index.js",
"dependencies": {
"jsonc-eslint-parser": "^2.3.0"
}
-7
View File
@@ -1,7 +0,0 @@
# react-native/tools/eslint
This directory contains ESLint rules for the `react-native` repository itself.
They are configured in `.eslintrc.js` using the `eslint-plugin-lint` package.
For ESLint rules intended for projects that consume React Native, add them to
the `@react-native-community/eslint-plugin` package instead.
-5
View File
@@ -4027,11 +4027,6 @@ eslint-plugin-jsx-a11y@^6.6.0:
safe-regex-test "^1.0.3"
string.prototype.includes "^2.0.0"
eslint-plugin-lint@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-lint/-/eslint-plugin-lint-1.0.0.tgz#bfc98ad0d1b5ea437b0072ec735c459df4d084b5"
integrity sha512-hYl6F/lYLjycZmHYnpTk3dlliNxjy9breG/9URhdQmPZibmENjM378EPKvSdIDBOV+Zw/Z0d3EaJhLTjcWTovA==
eslint-plugin-react-hooks@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz#1be0080901e6ac31ce7971beed3d3ec0a423d9e3"