Extend check-packages-test to validate additional required fields (#57510)

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

Extend `check-packages-test.js` to catch two more classes of manifest drift: published packages missing required fields, and packages under `private/` missing the `private` flag.

**Motivation**

Inspired by https://github.com/react-native-community/template/pull/241 — avoid a missing field blocking a future RN package publish.

**Other changes**

- To satisfy the new `files` requirement, add an explicit `files` allowlist to the four config packages that lacked one. As a side effect, this saves some `__tests__`  files from being distributed.

Changelog: [Internal]

Reviewed By: cortinico

Differential Revision: D111471314

fbshipit-source-id: e13082d2282128936e22e3a7580b5e434c3b41b2
This commit is contained in:
Alex Hunt
2026-07-10 12:27:14 -07:00
committed by meta-codesync[bot]
parent 0ed6c560da
commit 042833e699
6 changed files with 70 additions and 4 deletions
@@ -23,6 +23,7 @@
".": "./index.js",
"./flat": "./flat.js"
},
"files": ["README.md", "flat.js", "index.js", "shared.js"],
"dependencies": {
"@babel/core": "^7.25.2",
"@babel/eslint-parser": "^7.25.1",
@@ -17,6 +17,13 @@
],
"bugs": "https://github.com/react/react-native/issues",
"main": "index.js",
"files": [
"README.md",
"index.js",
"no-deep-imports.js",
"platform-colors.js",
"utils.js"
],
"devDependencies": {
"babel-plugin-syntax-hermes-parser": "0.36.1",
"hermes-eslint": "0.36.1"
@@ -19,6 +19,12 @@
],
"bugs": "https://github.com/react/react-native/issues",
"main": "index.js",
"files": [
"README.md",
"index.js",
"react-native-modules.js",
"with-babel-register"
],
"scripts": {
"prepack": "node prepack.js",
"postpack": "node postpack.js"
+2 -1
View File
@@ -17,5 +17,6 @@
"bugs": "https://github.com/react/react-native/issues",
"exports": {
".": "./tsconfig.json"
}
},
"files": ["README.md", "tsconfig.json", "tsconfig.strict.json"]
}
@@ -8,11 +8,13 @@
* @format
*/
import {REPO_ROOT} from '../../shared/consts';
import {PRIVATE_DIR, REPO_ROOT} from '../../shared/consts';
import {
getPackages,
getReactNativePackage,
getWorkspaceRoot,
} from '../../shared/monorepoUtils';
import path from 'path';
import {globSync} from 'tinyglobby';
describe('package manifests', () => {
@@ -25,6 +27,52 @@ describe('package manifests', () => {
const {packageJson} = await getReactNativePackage();
expect(packageJson).not.toHaveProperty('devDependencies');
});
test('published packages must declare required fields', async () => {
const packages = await getPackages({includeReactNative: true});
const violations: Array<string> = [];
for (const name of Object.keys(packages)) {
const {packageJson} = packages[name];
if (!packageJson.version) {
violations.push(`${name}: missing "version"`);
}
if (packageJson.license == null || packageJson.license === '') {
violations.push(`${name}: missing "license"`);
}
// "repository" is required for npm's trusted publishing / provenance (OIDC)
if (
packageJson.repository?.url == null ||
packageJson.repository.url === ''
) {
violations.push(`${name}: missing "repository.url"`);
}
if (
packageJson.repository?.directory == null ||
packageJson.repository.directory === ''
) {
violations.push(`${name}: missing "repository.directory"`);
}
if (packageJson.files == null || packageJson.files.length === 0) {
violations.push(`${name}: missing "files"`);
}
}
expect(violations).toEqual([]);
});
test('packages under private/ must set "private": true', async () => {
const packages = await getPackages({
includeReactNative: true,
includePrivate: true,
});
const notPrivate = Object.keys(packages)
.filter(name => packages[name].path.startsWith(PRIVATE_DIR + path.sep))
.filter(name => packages[name].packageJson.private !== true);
expect(notPrivate).toEqual([]);
});
});
describe('package file structure', () => {
+5 -2
View File
@@ -19,11 +19,14 @@ const WORKSPACES_CONFIG = '{packages,private}/*';
export type PackageJson = {
name: string,
version: string,
private?: boolean,
dependencies?: Record<string, string>,
devDependencies?: Record<string, string>,
peerDependencies?: Record<string, string>,
files?: ReadonlyArray<string>,
license?: string,
main?: string,
peerDependencies?: Record<string, string>,
private?: boolean,
repository?: {type?: string, url?: string, directory?: string, ...},
...
};