Skip to content

Commit

Permalink
RN: Enforce Manifest Constraints w/ ESLint
Browse files Browse the repository at this point in the history
Summary:
Creates a new lint rule that enforces the following constraints:

- `react-native/monorepo` cannot have `dependencies`.
- `react-native` cannot have `devDependencies`.

This also includes a lengthy comment in the rule definition explaining why these constraints exist, so that future contributors can make an informed decision when reconsidering these constraints.

Changelog:
[Internal]

Differential Revision: D48448633

fbshipit-source-id: 841f42df52c8d516a068e4acbbb611c3aa0369e9
  • Loading branch information
yungsters authored and facebook-github-bot committed Aug 18, 2023
1 parent 9f47b6a commit 3654c98
Show file tree
Hide file tree
Showing 6 changed files with 211 additions and 2 deletions.
10 changes: 10 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,16 @@ module.exports = {
'no-undef': 0,
},
},
{
files: ['package.json'],
parser: 'jsonc-eslint-parser',
},
{
files: ['package.json'],
rules: {
'lint/react-native-manifest': 2,
},
},
{
files: ['flow-typed/**/*.js'],
rules: {
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@
"update-lock": "npx yarn-deduplicate"
},
"workspaces": [
"packages/*"
"packages/*",
"tools/*"
],
"peerDependencies": {
"react": "18.2.0"
Expand Down
8 changes: 8 additions & 0 deletions tools/eslint/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "@react-native/eslint",
"private": true,
"version": "0.0.0",
"dependencies": {
"jsonc-eslint-parser": "^2.3.0"
}
}
91 changes: 91 additions & 0 deletions tools/eslint/rules/__tests__/react-native-manifest-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/**
* 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.
*
* @format
*/

'use strict';

const rule = require('../react-native-manifest.js');
const {RuleTester} = require('eslint');

const ruleTester = new RuleTester({
parser: require.resolve('jsonc-eslint-parser'),
});

ruleTester.run('react-native-manifest', rule, {
valid: [
{
code: JSON.stringify({
name: '@react-native/package-name',
}),
},
{
code: JSON.stringify({
name: '@react-native/package-name',
dependencies: {
dependencyA: '1.0.0',
},
devDependencies: {
dependencyB: '1.0.0',
},
}),
},
{
code: JSON.stringify({
name: '@react-native/monorepo',
devDependencies: {
dependencyB: '1.0.0',
},
}),
},
{
code: JSON.stringify({
name: 'react-native',
dependencies: {
dependencyA: '1.0.0',
},
}),
},
],
invalid: [
{
code: JSON.stringify({
name: '@react-native/monorepo',
dependencies: {
dependencyA: '1.0.0',
},
}),
errors: [
{
messageId: 'propertyDisallowed',
data: {
property: 'dependencies',
describe:
"Declare 'dependencies' in `packages/react-native/package.json`.",
},
},
],
},
{
code: JSON.stringify({
name: 'react-native',
devDependencies: {
dependencyA: '1.0.0',
},
}),
errors: [
{
messageId: 'propertyDisallowed',
data: {
property: 'devDependencies',
describe: "Declare 'devDependencies' in `<root>/package.json`.",
},
},
],
},
],
});
84 changes: 84 additions & 0 deletions tools/eslint/rules/react-native-manifest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/**
* 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.
*
* @format
*/

'use strict';

/**
* React Native's monorepo requires that "devDependencies" be declared at the
* root package.json and "dependencies" in the `react-native` package.json to
* permit the ability to segment dependent workspaces into 1) a development
* workspace root (which depends on the monorepo) and 2) a production workspace
* (which depends on the `react-native` package).
*/
const PACKAGE_CONSTRAINTS = {
'@react-native/monorepo': {
disallowed: [
{
property: 'dependencies',
describe:
"Declare 'dependencies' in `packages/react-native/package.json`.",
},
],
},
'react-native': {
disallowed: [
{
property: 'devDependencies',
describe: "Declare 'devDependencies' in `<root>/package.json`.",
},
],
},
};

module.exports = {
meta: {
type: 'problem',
docs: {
description: 'Enforce react-native manifest constraints',
},
messages: {
propertyDisallowed:
"'{{property}}' is disallowed in this file. {{describe}}",
},
schema: [],
},

create(context) {
// @see https://www.npmjs.com/package/jsonc-eslint-parser
if (!context.parserServices.isJSON) {
return {};
}
return {
'JSONExpressionStatement > JSONObjectExpression'(node) {
const propertyNodes = {};
for (const propertyNode of node.properties) {
propertyNodes[propertyNode.key.value] = propertyNode;
}

const name = propertyNodes.name?.value?.value;
const constraints = PACKAGE_CONSTRAINTS[name];
if (constraints == null) {
return;
}

for (const {property, describe} of constraints.disallowed) {
const propertyNode = propertyNodes[property];
if (propertyNode == null) {
continue;
}
context.report({
node: propertyNode,
messageId: 'propertyDisallowed',
data: {property, describe},
});
}
},
};
},
};
17 changes: 16 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5650,6 +5650,11 @@ eslint-visitor-keys@^2.0.0, eslint-visitor-keys@^2.1.0:
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303"
integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==

eslint-visitor-keys@^3.0.0:
version "3.4.3"
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800"
integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==

eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1:
version "3.4.1"
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz#c22c48f48942d08ca824cc526211ae400478a994"
Expand Down Expand Up @@ -5700,7 +5705,7 @@ eslint@^8.17.0, eslint@^8.19.0, eslint@^8.23.1:
strip-json-comments "^3.1.0"
text-table "^0.2.0"

espree@^9.4.0:
espree@^9.0.0, espree@^9.4.0:
version "9.6.1"
resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f"
integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==
Expand Down Expand Up @@ -7727,6 +7732,16 @@ json5@2.2.3, json5@^2.1.0, json5@^2.2.1:
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==

jsonc-eslint-parser@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/jsonc-eslint-parser/-/jsonc-eslint-parser-2.3.0.tgz#7c2de97d01bff7227cbef2f25d1025d42a36198b"
integrity sha512-9xZPKVYp9DxnM3sd1yAsh/d59iIaswDkai8oTxbursfKYbg/ibjX0IzFt35+VZ8iEW453TVTXztnRvYUQlAfUQ==
dependencies:
acorn "^8.5.0"
eslint-visitor-keys "^3.0.0"
espree "^9.0.0"
semver "^7.3.5"

jsonc-parser@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76"
Expand Down

0 comments on commit 3654c98

Please sign in to comment.