Add snapshot validation to build-types (#51893)

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

This diff adds `--validate` flag that runs snapshot validation to determine if the `ReactNativeApi.d.ts` rollup has been changed (if JS public API has been touched). There was also an issue with `sortProperties` that reordered some properties (ex. in ImagePropsBase) after removing one of them (ex. accessible) which had negative impact on the displayed result.

### Motivation
Compare previous snapshot with the one built on the current revision to determine the impact of made changes on the public API surface. Display differences in human readable format using `diff` method from the `jest-diff` library.

For now `--validate` flag is not useful on its own. It should be used with `--withSnapshot` flag (which will be removed shortly and generating snapshot will be a default mechanism).

Changelog:
[General][Added] - Add `--validate` flag to `build-types` script for JS API snapshot validation.

Reviewed By: huntie

Differential Revision: D76135158

fbshipit-source-id: 53f5b142c66e3e3931961f741c3f2fab8ccdc228
This commit is contained in:
Dawid Małecki
2025-06-11 09:06:39 -07:00
committed by Facebook GitHub Bot
parent 788c980c7c
commit f529fd6ba5
2 changed files with 48 additions and 8 deletions
+45 -6
View File
@@ -23,9 +23,11 @@ const {
} = require('@microsoft/api-extractor');
const {promises: fs} = require('fs');
const glob = require('glob');
const {diff} = require('jest-diff');
const path = require('path');
const prettier = require('prettier');
const osTempDir = require('temp-dir');
const {styleText} = require('util');
const inputFilesPostTransforms: $ReadOnlyArray<PluginObj<mixed>> = [
require('./transforms/renameDefaultExportedIdentifiers'),
@@ -37,7 +39,7 @@ const postTransforms: $ReadOnlyArray<PluginObj<mixed>> = [
require('./transforms/sortUnions'),
];
async function buildAPISnapshot() {
async function buildAPISnapshot(validate: boolean) {
const tempDirectory = await createTempDir('react-native-js-api-snapshot');
const packages = await findPackagesWithTypedef();
@@ -56,12 +58,23 @@ async function buildAPISnapshot() {
if (extractorResult.succeeded) {
const apiSnapshot = apiSnapshotTemplate(
await getCleanedUpRollup(tempDirectory),
);
) as string;
await fs.writeFile(
path.join(REACT_NATIVE_PACKAGE_DIR, 'ReactNativeApi.d.ts'),
apiSnapshot,
);
if (validate) {
const prevSnapshot = await fs.readFile(
path.join(REACT_NATIVE_PACKAGE_DIR, 'ReactNativeApi.d.ts'),
'utf-8',
);
const hasChanged = await validateSnapshots(prevSnapshot, apiSnapshot);
if (hasChanged) {
process.exitCode = 1;
}
} else {
await fs.writeFile(
path.join(REACT_NATIVE_PACKAGE_DIR, 'ReactNativeApi.d.ts'),
apiSnapshot,
);
}
} else {
process.exitCode = 1;
console.error(
@@ -73,6 +86,32 @@ async function buildAPISnapshot() {
await fs.rm(tempDirectory, {recursive: true});
}
async function validateSnapshots(
prevSnapshot: string,
newSnapshot: string,
): Promise<boolean> {
const hasChanged = newSnapshot !== prevSnapshot;
if (hasChanged) {
const options = {
aAnnotation: 'Previous Snapshot',
bAnnotation: 'New Snapshot',
expand: false,
emptyFirstOrLastLinePlaceholder: '↵',
includeChangeCounts: true,
aColor: (line: string) => styleText(['red'], line),
bColor: (line: string) => styleText(['green'], line),
};
const diffResult = diff(prevSnapshot, newSnapshot, options);
console.error(
`\n${styleText(['inverse'], ' VALIDATE ')} ReactNativeApi.d.ts has changed. Please re-run \`yarn build-types\` and commit the updated snapshot.\n\n`,
diffResult,
);
}
return hasChanged;
}
async function findPackagesWithTypedef() {
const packagesWithGeneratedTypes = glob
.sync(`${PACKAGES_DIR}/**/types_generated`, {nodir: false})
+3 -2
View File
@@ -20,12 +20,13 @@ const config = {
debug: {type: 'boolean'},
help: {type: 'boolean'},
withSnapshot: {type: 'boolean'},
validate: {type: 'boolean'},
},
};
async function main() {
const {
values: {debug: debugEnabled, help, withSnapshot},
values: {debug: debugEnabled, help, withSnapshot, validate},
/* $FlowFixMe[incompatible-call] Natural Inference rollout. See
* https://fburl.com/workplace/6291gfvu */
} = parseArgs(config);
@@ -68,7 +69,7 @@ async function main() {
'\n',
);
await buildApiSnapshot();
await buildApiSnapshot(validate);
}
}