Add `@react-native/codegen-typescript-test` to verify .d.ts files in `@react-native/codegen` (1) (#36562)

Summary:
- Add a typescript project to test `CodegenSchema.d.ts`. More tests for other .d.ts files will be added in future pull requests.
- The build script scans all snapshots from `react-native/codegen`'s typescript frontend and generates .ts files for each snapshot, but they are .gitignore-ed.
- `npm run build` will build these .ts files against `CodegenSchema.d.ts` after generating them.
- A failed jest case is included to ensure CI catch it, it will be removed before merged.

bypass-github-export-checks

## Changelog:

[General] [Added] - Add react-native/codegen-typescript-test to verify .d.ts files in react-native/codegen (1)

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

Test Plan:
`npm run build` in `packages/react-native-codegen-typescript-test` and see all test files appear in `__generated__`.

## Screenshot

![Untitled](https://user-images.githubusercontent.com/53799235/226757755-cab4cb29-7d22-46a1-9ecb-d6732122ed38.png)

Reviewed By: rshest

Differential Revision: D44292277

Pulled By: cipolleschi

fbshipit-source-id: 8d79fe913f9563d64c92aae7c4f4e97a24ae9a21
This commit is contained in:
Zihan Chen (MSFT) 2023-04-05 07:38:25 -07:00 коммит произвёл Facebook GitHub Bot
Родитель a248456d18
Коммит 9bb71650c9
11 изменённых файлов: 2477 добавлений и 30 удалений

2
.gitignore поставляемый
Просмотреть файл

@ -127,6 +127,8 @@ package-lock.json
/packages/rn-tester/NativeModuleExample/ScreenshotManagerSpec*
/**/RCTThirdPartyFabricComponentsProvider.*
# @react-native/codegen-typescript-test
/packages/react-native-codegen-typescript-test/lib
# Additional SDKs
/packages/react-native/sdks/download

1
packages/react-native-codegen-typescript-test/__generated__/.gitignore сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1 @@
*.ts

Просмотреть файл

@ -0,0 +1,38 @@
{
"name": "@react-native/codegen-typescript-test",
"version": "0.0.1",
"description": "⚛️ TypeScript related unit test for @react-native/codegen",
"homepage": "https://github.com/facebook/react-native/tree/HEAD/packages/react-native-typescript-test",
"repository": {
"type": "git",
"url": "git@github.com:facebook/react-native.git",
"directory": "packages/react-native-codegen-typescript-test"
},
"scripts": {
"build": "yarn clean && node scripts/build.js --verbose && tsc",
"clean": "rimraf lib && rimraf __generated__/*.ts",
"prepare": "yarn run build"
},
"license": "MIT",
"dependencies": {
"@react-native/codegen": "*"
},
"devDependencies": {
"@babel/core": "^7.20.0",
"@babel/plugin-proposal-class-properties": "^7.0.0",
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.0.0",
"@babel/plugin-proposal-object-rest-spread": "^7.0.0",
"@babel/plugin-proposal-optional-chaining": "^7.0.0",
"@babel/plugin-syntax-dynamic-import": "^7.0.0",
"@babel/plugin-transform-async-to-generator": "^7.0.0",
"@babel/plugin-transform-destructuring": "^7.0.0",
"@babel/plugin-transform-flow-strip-types": "^7.0.0",
"@babel/preset-env": "^7.14.0",
"@types/jest": "^24.0.17",
"jest": "^24.0.17",
"rimraf": "^3.0.2"
},
"peerDependencies": {
"@babel/preset-env": "^7.1.6"
}
}

42
packages/react-native-codegen-typescript-test/scripts/build.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,42 @@
/**
* 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.
*/
const fs = require('fs');
const path = require('path');
const tsComponentFixturePath = path.join(__dirname, '../../react-native-codegen/src/parsers/typescript/components/__test_fixtures__/fixtures.js');
const tsComponentSnapshotPath = path.join(__dirname, '../../react-native-codegen/src/parsers/typescript/components/__tests__/__snapshots__/typescript-component-parser-test.js.snap');
const tsModuleFixturePath = path.join(__dirname, '../../react-native-codegen/src/parsers/typescript/modules/__test_fixtures__/fixtures.js');
const tsModuleSnapshotPath = path.join(__dirname, '../../react-native-codegen/src/parsers/typescript/modules/__tests__/__snapshots__/typescript-module-parser-snapshot-test.js.snap');
const snapshotOutputPath = path.join(__dirname, '../__generated__');
function genereateSnapshotTestCases(name, fixturePath, snapshotPath, outputPath) {
const fixtures = require(fixturePath);
const snapshots = require(snapshotPath);
for (const key of Object.keys(fixtures)) {
const snapshotName = `RN Codegen TypeScript Parser can generate fixture ${key} 1`;
const snapshotString = snapshots[snapshotName];
const snapshot = snapshotString.substring(2, snapshotString.length - 2);
const tsSourceCode = `
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* DO NOT MODIFY
* Generated by src/build.js
* From snapshot: ${name} - ${key}
*/
import type { SchemaType } from '@react-native/codegen/lib/CodegenSchema';
const snapshot : SchemaType = ${snapshot};
export default snapshot;
`;
fs.writeFileSync(path.join(outputPath, `${name}_${key}.ts`), tsSourceCode, { encoding: 'utf-8' });
}
}
genereateSnapshotTestCases('component', tsComponentFixturePath, tsComponentSnapshotPath, snapshotOutputPath);
genereateSnapshotTestCases('module', tsModuleFixturePath, tsModuleSnapshotPath, snapshotOutputPath);

Просмотреть файл

@ -0,0 +1,79 @@
/**
* 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
*/
import * as assert from 'assert';
import type {SchemaType} from '@react-native/codegen/lib/CodegenSchema';
import {TypeScriptParser} from '@react-native/codegen/lib/parsers/typescript/parser';
test(`@rn/codegen should parse an empty TypeScript module`, () => {
const tsInput = `
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const parser = new TypeScriptParser();
const actual = parser.parseString(tsInput, 'SampleTurboModule.ts');
const expected: SchemaType = {
modules: {
SampleTurboModule: {
aliasMap: {},
enumMap: {},
excludedPlatforms: undefined,
moduleName: 'SampleTurboModule',
spec: {
properties: [],
},
type: 'NativeModule',
},
},
};
assert.deepStrictEqual(actual, expected);
});
test(`@rn/codegen should parse an empty TypeScript component`, () => {
const tsInput = `
import type {ViewProps} from 'ViewPropTypes';
import type {HostComponent} from 'react-native';
const codegenNativeComponent = require('codegenNativeComponent');
export interface ModuleProps extends ViewProps {}
export default codegenNativeComponent<ModuleProps>('Module', {
interfaceOnly: true,
paperComponentName: 'RCTModule',
}) as HostComponent<ModuleProps>;
`;
const parser = new TypeScriptParser();
const actual = parser.parseString(tsInput, 'SampleNativeComponent.ts');
const expected: SchemaType = {
modules: {
Module: {
components: {
Module: {
commands: [],
events: [],
extendsProps: [
{
knownTypeName: 'ReactNativeCoreViewProps',
type: 'ReactNativeBuiltInType',
},
],
interfaceOnly: true,
paperComponentName: 'RCTModule',
props: [],
},
},
type: 'Component',
},
},
};
assert.deepStrictEqual(actual, expected);
});

Просмотреть файл

@ -0,0 +1,20 @@
{
"compilerOptions": {
"lib": [
"es6"
],
"module": "commonjs",
"moduleResolution": "node",
"target": "es5",
"declaration": false,
"sourceMap": false,
"outDir": "./lib",
"noEmitOnError": true,
"noUnusedLocals": true,
"strictNullChecks": true,
},
"exclude": [
"node_modules",
"lib"
]
}

Просмотреть файл

@ -50,6 +50,10 @@ export interface ObjectTypeAnnotation<T> {
readonly baseTypes?: readonly string[] | undefined;
}
export interface MixedTypeAnnotation {
readonly type: 'MixedTypeAnnotation';
}
export interface FunctionTypeAnnotation<P, R> {
readonly type: 'FunctionTypeAnnotation';
readonly params: readonly NamedShape<P>[];
@ -74,6 +78,7 @@ export interface ComponentShape extends OptionsShape {
readonly events: readonly EventTypeShape[];
readonly props: readonly NamedShape<PropTypeAnnotation>[];
readonly commands: readonly NamedShape<CommandTypeAnnotation>[];
readonly deprecatedViewConfigName?: string | undefined;
}
export interface OptionsShape {
@ -168,7 +173,8 @@ export type PropTypeAnnotation =
readonly type: 'ArrayTypeAnnotation';
readonly elementType: ObjectTypeAnnotation<PropTypeAnnotation>;
};
};
}
| MixedTypeAnnotation;
export interface ReservedPropTypeAnnotation {
readonly type: 'ReservedPropTypeAnnotation';
@ -263,7 +269,7 @@ export interface NativeModuleBooleanTypeAnnotation {
export type NativeModuleEnumMembers = readonly {
readonly name: string;
readonly value: string;
readonly value: string | number;
}[];
export type NativeModuleEnumMemberType =
@ -343,6 +349,7 @@ export type NativeModuleTypeAnnotation =
export type NativeModuleParamOnlyTypeAnnotation = NativeModuleFunctionTypeAnnotation;
export type NativeModuleReturnOnlyTypeAnnotation =
| NativeModuleFunctionTypeAnnotation
| NativeModulePromiseTypeAnnotation
| VoidTypeAnnotation;

Просмотреть файл

@ -5,6 +5,13 @@
* LICENSE file in the root directory of this source tree.
*/
import type {Parser} from '../parser';
import type { Parser } from '../parser';
import type { SchemaType } from '../../CodegenSchema';
import type { ParserType } from '../errors';
export declare class FlowParser implements Parser{}
export declare class FlowParser implements Parser {
language(): ParserType;
parseFile(filename: string): SchemaType;
parseString(contents: string, filename?: string): SchemaType;
parseModuleFixture(filename: string): SchemaType;
}

Просмотреть файл

@ -5,6 +5,13 @@
* LICENSE file in the root directory of this source tree.
*/
import type {Parser} from '../parser';
import type { Parser } from '../parser';
import type { SchemaType } from '../../CodegenSchema';
import type { ParserType } from '../errors';
export declare class TypeScriptParser implements Parser{}
export declare class TypeScriptParser implements Parser {
language(): ParserType;
parseFile(filename: string): SchemaType;
parseString(contents: string, filename?: string): SchemaType;
parseModuleFixture(filename: string): SchemaType;
}

Просмотреть файл

@ -53,6 +53,33 @@ try {
throw Error(exitCode);
}
/*
* Build @react-native/codegen and @react-native/codegen-typescript-test
*
* The typescript-test project use TypeScript to write test cases
* In order to make these tests discoverable to jest
* *-test.ts must be compiled to *-test.js before running jest
*/
describe('Test: Build @react-native/codegen');
if (
exec(`${YARN_BINARY} --cwd ./packages/react-native-codegen run build`).code
) {
echo('Failed to build @react-native/codegen.');
exitCode = 1;
throw Error(exitCode);
}
describe('Test: Build @react-native/codegen-typescript-test');
if (
exec(
`${YARN_BINARY} --cwd ./packages/react-native-codegen-typescript-test run build`,
).code
) {
echo('Failed to build @react-native/codegen-typescript-test.');
exitCode = 1;
throw Error(exitCode);
}
describe('Test: Jest');
if (
exec(

2265
yarn.lock

Разница между файлами не показана из-за своего большого размера Загрузить разницу