diff --git a/packages/eslint-plugin-specs/react-native-modules.js b/packages/eslint-plugin-specs/react-native-modules.js index 19584626f95c..fa0b58440d2f 100644 --- a/packages/eslint-plugin-specs/react-native-modules.js +++ b/packages/eslint-plugin-specs/react-native-modules.js @@ -23,9 +23,10 @@ const ERRORS = { let RNModuleParser; let RNParserUtils; +let RNFlowParser; function requireModuleParser() { - if (RNModuleParser == null || RNParserUtils == null) { + if (RNModuleParser == null || RNParserUtils == null || RNFlowParser == null) { // If using this externally, we leverage @react-native/codegen as published form if (!PACKAGE_USAGE) { const config = { @@ -36,6 +37,7 @@ function requireModuleParser() { withBabelRegister(config, () => { RNModuleParser = require('@react-native/codegen/src/parsers/flow/modules'); RNParserUtils = require('@react-native/codegen/src/parsers/utils'); + RNFlowParser = require('@react-native/codegen/src/parsers/flow/parser'); }); } else { const config = { @@ -46,6 +48,7 @@ function requireModuleParser() { withBabelRegister(config, () => { RNModuleParser = require('@react-native/codegen/lib/parsers/flow/modules'); RNParserUtils = require('@react-native/codegen/lib/parsers/flow/utils'); + RNFlowParser = require('@react-native/codegen/lib/parsers/flow/parser'); }); } } @@ -53,6 +56,7 @@ function requireModuleParser() { return { buildModuleSchema: RNModuleParser.buildModuleSchema, createParserErrorCapturer: RNParserUtils.createParserErrorCapturer, + parser: new RNFlowParser.FlowParser(), }; } @@ -127,7 +131,7 @@ function rule(context) { }); } - const {buildModuleSchema, createParserErrorCapturer} = + const {buildModuleSchema, createParserErrorCapturer, parser} = requireModuleParser(); const flowParser = require('flow-parser'); @@ -137,7 +141,7 @@ function rule(context) { const ast = flowParser.parse(sourceCode, {enums: true}); tryParse(() => { - buildModuleSchema(hasteModuleName, ast, tryParse); + buildModuleSchema(hasteModuleName, ast, tryParse, parser); }); parsingErrors.forEach(error => { diff --git a/packages/react-native-codegen/e2e/__tests__/components/GenerateComponentDescriptorH-test.js b/packages/react-native-codegen/e2e/__tests__/components/GenerateComponentDescriptorH-test.js index 8d35cca3843e..3e12f2afa956 100644 --- a/packages/react-native-codegen/e2e/__tests__/components/GenerateComponentDescriptorH-test.js +++ b/packages/react-native-codegen/e2e/__tests__/components/GenerateComponentDescriptorH-test.js @@ -11,8 +11,7 @@ 'use strict'; -const {parseFile} = require('../../../src/parsers/utils'); -const FlowParser = require('../../../src/parsers/flow'); +const {FlowParser} = require('../../../src/parsers/flow/parser'); const generator = require('../../../src/generators/components/GenerateComponentDescriptorH'); const fs = require('fs'); @@ -20,13 +19,12 @@ const FIXTURE_DIR = `${__dirname}/../../__test_fixtures__/components`; const fixtures = fs.readdirSync(FIXTURE_DIR); +const parser = new FlowParser(); + fixtures.forEach(fixture => { it(`GenerateComponentDescriptorH can generate for '${fixture}'`, () => { const libName = 'RNCodegenModuleFixtures'; - const schema = parseFile( - `${FIXTURE_DIR}/${fixture}`, - FlowParser.buildSchema, - ); + const schema = parser.parseFile(`${FIXTURE_DIR}/${fixture}`); const output = generator.generate(libName, schema); expect(Object.fromEntries(output)).toMatchSnapshot(); }); diff --git a/packages/react-native-codegen/e2e/__tests__/components/GenerateComponentHObjCpp-test.js b/packages/react-native-codegen/e2e/__tests__/components/GenerateComponentHObjCpp-test.js index 89d14fc74a63..50959ea33a80 100644 --- a/packages/react-native-codegen/e2e/__tests__/components/GenerateComponentHObjCpp-test.js +++ b/packages/react-native-codegen/e2e/__tests__/components/GenerateComponentHObjCpp-test.js @@ -11,8 +11,7 @@ 'use strict'; -const {parseFile} = require('../../../src/parsers/utils'); -const FlowParser = require('../../../src/parsers/flow'); +const {FlowParser} = require('../../../src/parsers/flow/parser'); const generator = require('../../../src/generators/components/GenerateComponentHObjCpp'); const fs = require('fs'); @@ -20,13 +19,12 @@ const FIXTURE_DIR = `${__dirname}/../../__test_fixtures__/components`; const fixtures = fs.readdirSync(FIXTURE_DIR); +const parser = new FlowParser(); + fixtures.forEach(fixture => { it(`GenerateComponentHObjCpp can generate for '${fixture}'`, () => { const libName = 'RNCodegenModuleFixtures'; - const schema = parseFile( - `${FIXTURE_DIR}/${fixture}`, - FlowParser.buildSchema, - ); + const schema = parser.parseFile(`${FIXTURE_DIR}/${fixture}`); const output = generator.generate(libName, schema); expect(Object.fromEntries(output)).toMatchSnapshot(); }); diff --git a/packages/react-native-codegen/e2e/__tests__/components/GenerateEventEmitterCpp-test.js b/packages/react-native-codegen/e2e/__tests__/components/GenerateEventEmitterCpp-test.js index 603c77b56607..177ec1dccb9e 100644 --- a/packages/react-native-codegen/e2e/__tests__/components/GenerateEventEmitterCpp-test.js +++ b/packages/react-native-codegen/e2e/__tests__/components/GenerateEventEmitterCpp-test.js @@ -11,8 +11,7 @@ 'use strict'; -const {parseFile} = require('../../../src/parsers/utils'); -const FlowParser = require('../../../src/parsers/flow'); +const {FlowParser} = require('../../../src/parsers/flow/parser'); const generator = require('../../../src/generators/components/GenerateEventEmitterCpp'); const fs = require('fs'); @@ -20,13 +19,12 @@ const FIXTURE_DIR = `${__dirname}/../../__test_fixtures__/components`; const fixtures = fs.readdirSync(FIXTURE_DIR); +const parser = new FlowParser(); + fixtures.forEach(fixture => { it(`GenerateEventEmitterCpp can generate for '${fixture}'`, () => { const libName = 'RNCodegenModuleFixtures'; - const schema = parseFile( - `${FIXTURE_DIR}/${fixture}`, - FlowParser.buildSchema, - ); + const schema = parser.parseFile(`${FIXTURE_DIR}/${fixture}`); const output = generator.generate(libName, schema); expect(Object.fromEntries(output)).toMatchSnapshot(); }); diff --git a/packages/react-native-codegen/e2e/__tests__/components/GenerateEventEmitterH-test.js b/packages/react-native-codegen/e2e/__tests__/components/GenerateEventEmitterH-test.js index 05b1082297cc..962c57ef4041 100644 --- a/packages/react-native-codegen/e2e/__tests__/components/GenerateEventEmitterH-test.js +++ b/packages/react-native-codegen/e2e/__tests__/components/GenerateEventEmitterH-test.js @@ -11,8 +11,7 @@ 'use strict'; -const {parseFile} = require('../../../src/parsers/utils'); -const FlowParser = require('../../../src/parsers/flow'); +const {FlowParser} = require('../../../src/parsers/flow/parser'); const generator = require('../../../src/generators/components/GenerateEventEmitterH'); const fs = require('fs'); @@ -20,13 +19,12 @@ const FIXTURE_DIR = `${__dirname}/../../__test_fixtures__/components`; const fixtures = fs.readdirSync(FIXTURE_DIR); +const parser = new FlowParser(); + fixtures.forEach(fixture => { it(`GenerateEventEmitterH can generate for '${fixture}'`, () => { const libName = 'RNCodegenModuleFixtures'; - const schema = parseFile( - `${FIXTURE_DIR}/${fixture}`, - FlowParser.buildSchema, - ); + const schema = parser.parseFile(`${FIXTURE_DIR}/${fixture}`); const output = generator.generate(libName, schema); expect(Object.fromEntries(output)).toMatchSnapshot(); }); diff --git a/packages/react-native-codegen/e2e/__tests__/components/GeneratePropsCpp-test.js b/packages/react-native-codegen/e2e/__tests__/components/GeneratePropsCpp-test.js index 131d4f4fd668..b6d0f5e9792b 100644 --- a/packages/react-native-codegen/e2e/__tests__/components/GeneratePropsCpp-test.js +++ b/packages/react-native-codegen/e2e/__tests__/components/GeneratePropsCpp-test.js @@ -11,8 +11,7 @@ 'use strict'; -const {parseFile} = require('../../../src/parsers/utils'); -const FlowParser = require('../../../src/parsers/flow'); +const {FlowParser} = require('../../../src/parsers/flow/parser'); const generator = require('../../../src/generators/components/GeneratePropsCpp'); const fs = require('fs'); @@ -20,13 +19,12 @@ const FIXTURE_DIR = `${__dirname}/../../__test_fixtures__/components`; const fixtures = fs.readdirSync(FIXTURE_DIR); +const parser = new FlowParser(); + fixtures.forEach(fixture => { it(`GeneratePropsCpp can generate for '${fixture}'`, () => { const libName = 'RNCodegenModuleFixtures'; - const schema = parseFile( - `${FIXTURE_DIR}/${fixture}`, - FlowParser.buildSchema, - ); + const schema = parser.parseFile(`${FIXTURE_DIR}/${fixture}`); const output = generator.generate(libName, schema); expect(Object.fromEntries(output)).toMatchSnapshot(); }); diff --git a/packages/react-native-codegen/e2e/__tests__/components/GeneratePropsH-test.js b/packages/react-native-codegen/e2e/__tests__/components/GeneratePropsH-test.js index 0600f6bb70d1..6c60a966c345 100644 --- a/packages/react-native-codegen/e2e/__tests__/components/GeneratePropsH-test.js +++ b/packages/react-native-codegen/e2e/__tests__/components/GeneratePropsH-test.js @@ -11,8 +11,7 @@ 'use strict'; -const {parseFile} = require('../../../src/parsers/utils'); -const FlowParser = require('../../../src/parsers/flow'); +const {FlowParser} = require('../../../src/parsers/flow/parser'); const generator = require('../../../src/generators/components/GeneratePropsH'); const fs = require('fs'); @@ -20,13 +19,12 @@ const FIXTURE_DIR = `${__dirname}/../../__test_fixtures__/components`; const fixtures = fs.readdirSync(FIXTURE_DIR); +const parser = new FlowParser(); + fixtures.forEach(fixture => { it(`GeneratePropsH can generate for '${fixture}'`, () => { const libName = 'RNCodegenModuleFixtures'; - const schema = parseFile( - `${FIXTURE_DIR}/${fixture}`, - FlowParser.buildSchema, - ); + const schema = parser.parseFile(`${FIXTURE_DIR}/${fixture}`); const output = generator.generate(libName, schema); expect(Object.fromEntries(output)).toMatchSnapshot(); }); diff --git a/packages/react-native-codegen/e2e/__tests__/components/GeneratePropsJavaDelegate-test.js b/packages/react-native-codegen/e2e/__tests__/components/GeneratePropsJavaDelegate-test.js index 3f87386c4a0e..3a3a2135cb22 100644 --- a/packages/react-native-codegen/e2e/__tests__/components/GeneratePropsJavaDelegate-test.js +++ b/packages/react-native-codegen/e2e/__tests__/components/GeneratePropsJavaDelegate-test.js @@ -11,8 +11,7 @@ 'use strict'; -const {parseFile} = require('../../../src/parsers/utils'); -const FlowParser = require('../../../src/parsers/flow'); +const {FlowParser} = require('../../../src/parsers/flow/parser'); const generator = require('../../../src/generators/components/GeneratePropsJavaDelegate'); const fs = require('fs'); @@ -20,13 +19,12 @@ const FIXTURE_DIR = `${__dirname}/../../__test_fixtures__/components`; const fixtures = fs.readdirSync(FIXTURE_DIR); +const parser = new FlowParser(); + fixtures.forEach(fixture => { it(`GeneratePropsJavaDelegate can generate for '${fixture}'`, () => { const libName = 'RNCodegenModuleFixtures'; - const schema = parseFile( - `${FIXTURE_DIR}/${fixture}`, - FlowParser.buildSchema, - ); + const schema = parser.parseFile(`${FIXTURE_DIR}/${fixture}`); const output = generator.generate(libName, schema); expect(Object.fromEntries(output)).toMatchSnapshot(); }); diff --git a/packages/react-native-codegen/e2e/__tests__/components/GeneratePropsJavaInterface-test.js b/packages/react-native-codegen/e2e/__tests__/components/GeneratePropsJavaInterface-test.js index 40fcfe3d9d02..14a807fd02b4 100644 --- a/packages/react-native-codegen/e2e/__tests__/components/GeneratePropsJavaInterface-test.js +++ b/packages/react-native-codegen/e2e/__tests__/components/GeneratePropsJavaInterface-test.js @@ -11,21 +11,19 @@ 'use strict'; -const {parseFile} = require('../../../src/parsers/utils'); -const FlowParser = require('../../../src/parsers/flow'); +const {FlowParser} = require('../../../src/parsers/flow/parser'); const generator = require('../../../src/generators/components/GeneratePropsJavaInterface'); const fs = require('fs'); const FIXTURE_DIR = `${__dirname}/../../__test_fixtures__/components`; const fixtures = fs.readdirSync(FIXTURE_DIR); +const parser = new FlowParser(); + fixtures.forEach(fixture => { it(`GeneratePropsJavaInterface can generate for '${fixture}'`, () => { const libName = 'RNCodegenModuleFixtures'; - const schema = parseFile( - `${FIXTURE_DIR}/${fixture}`, - FlowParser.buildSchema, - ); + const schema = parser.parseFile(`${FIXTURE_DIR}/${fixture}`); const output = generator.generate(libName, schema, undefined, false); expect(Object.fromEntries(output)).toMatchSnapshot(); }); diff --git a/packages/react-native-codegen/e2e/__tests__/components/GenerateShadowNodeCpp-test.js b/packages/react-native-codegen/e2e/__tests__/components/GenerateShadowNodeCpp-test.js index a4fa38bddab7..a7a766b1a56b 100644 --- a/packages/react-native-codegen/e2e/__tests__/components/GenerateShadowNodeCpp-test.js +++ b/packages/react-native-codegen/e2e/__tests__/components/GenerateShadowNodeCpp-test.js @@ -11,21 +11,19 @@ 'use strict'; -const {parseFile} = require('../../../src/parsers/utils'); -const FlowParser = require('../../../src/parsers/flow'); +const {FlowParser} = require('../../../src/parsers/flow/parser'); const generator = require('../../../src/generators/components/GenerateShadowNodeCpp'); const fs = require('fs'); const FIXTURE_DIR = `${__dirname}/../../__test_fixtures__/components`; const fixtures = fs.readdirSync(FIXTURE_DIR); +const parser = new FlowParser(); + fixtures.forEach(fixture => { it(`GenerateShadowNodeCpp can generate for '${fixture}'`, () => { const libName = 'RNCodegenModuleFixtures'; - const schema = parseFile( - `${FIXTURE_DIR}/${fixture}`, - FlowParser.buildSchema, - ); + const schema = parser.parseFile(`${FIXTURE_DIR}/${fixture}`); const output = generator.generate(libName, schema, undefined, false); expect(Object.fromEntries(output)).toMatchSnapshot(); }); diff --git a/packages/react-native-codegen/e2e/__tests__/components/GenerateShadowNodeH-test.js b/packages/react-native-codegen/e2e/__tests__/components/GenerateShadowNodeH-test.js index 0af0ba44f4b2..87cc703a17aa 100644 --- a/packages/react-native-codegen/e2e/__tests__/components/GenerateShadowNodeH-test.js +++ b/packages/react-native-codegen/e2e/__tests__/components/GenerateShadowNodeH-test.js @@ -11,21 +11,19 @@ 'use strict'; -const {parseFile} = require('../../../src/parsers/utils'); -const FlowParser = require('../../../src/parsers/flow'); +const {FlowParser} = require('../../../src/parsers/flow/parser'); const generator = require('../../../src/generators/components/GenerateShadowNodeH'); const fs = require('fs'); const FIXTURE_DIR = `${__dirname}/../../__test_fixtures__/components`; const fixtures = fs.readdirSync(FIXTURE_DIR); +const parser = new FlowParser(); + fixtures.forEach(fixture => { it(`GenerateShadowNodeH can generate for '${fixture}'`, () => { const libName = 'RNCodegenModuleFixtures'; - const schema = parseFile( - `${FIXTURE_DIR}/${fixture}`, - FlowParser.buildSchema, - ); + const schema = parser.parseFile(`${FIXTURE_DIR}/${fixture}`); const output = generator.generate(libName, schema, undefined, false); expect(Object.fromEntries(output)).toMatchSnapshot(); }); diff --git a/packages/react-native-codegen/e2e/__tests__/components/GenerateViewConfigJs-test.js b/packages/react-native-codegen/e2e/__tests__/components/GenerateViewConfigJs-test.js index fa4d90797ba4..833866e473df 100644 --- a/packages/react-native-codegen/e2e/__tests__/components/GenerateViewConfigJs-test.js +++ b/packages/react-native-codegen/e2e/__tests__/components/GenerateViewConfigJs-test.js @@ -11,8 +11,7 @@ 'use strict'; -const {parseFile} = require('../../../src/parsers/utils'); -const FlowParser = require('../../../src/parsers/flow'); +const {FlowParser} = require('../../../src/parsers/flow/parser'); const generator = require('../../../src/generators/components/GenerateViewConfigJs'); const fs = require('fs'); @@ -20,13 +19,12 @@ const FIXTURE_DIR = `${__dirname}/../../__test_fixtures__/components`; const fixtures = fs.readdirSync(FIXTURE_DIR); +const parser = new FlowParser(); + fixtures.forEach(fixture => { it(`GenerateViewConfigJs can generate for '${fixture}'`, () => { const libName = 'RNCodegenModuleFixtures'; - const schema = parseFile( - `${FIXTURE_DIR}/${fixture}`, - FlowParser.buildSchema, - ); + const schema = parser.parseFile(`${FIXTURE_DIR}/${fixture}`); const output = generator.generate(libName, schema); expect(output).toMatchSnapshot(); }); diff --git a/packages/react-native-codegen/e2e/__tests__/modules/GenerateModuleObjCpp-test.js b/packages/react-native-codegen/e2e/__tests__/modules/GenerateModuleObjCpp-test.js index a6bb549d056a..9e0434286073 100644 --- a/packages/react-native-codegen/e2e/__tests__/modules/GenerateModuleObjCpp-test.js +++ b/packages/react-native-codegen/e2e/__tests__/modules/GenerateModuleObjCpp-test.js @@ -11,8 +11,7 @@ 'use strict'; -const {parseFile} = require('../../../src/parsers/utils'); -const FlowParser = require('../../../src/parsers/flow'); +const {FlowParser} = require('../../../src/parsers/flow/parser'); const generator = require('../../../src/generators/modules/GenerateModuleObjCpp'); const fs = require('fs'); @@ -20,14 +19,13 @@ import type {SchemaType} from '../../../src/CodegenSchema'; const FIXTURE_DIR = `${__dirname}/../../__test_fixtures__/modules`; +const parser = new FlowParser(); + function getModules(): SchemaType { const filenames: Array = fs.readdirSync(FIXTURE_DIR); return filenames.reduce( (accumulator, file) => { - const schema = parseFile( - `${FIXTURE_DIR}/${file}`, - FlowParser.buildSchema, - ); + const schema = parser.parseFile(`${FIXTURE_DIR}/${file}`); return { modules: { ...accumulator.modules, diff --git a/packages/react-native-codegen/src/cli/combine/combine-js-to-schema.js b/packages/react-native-codegen/src/cli/combine/combine-js-to-schema.js index e4bc8780db67..a2dcef55c43d 100644 --- a/packages/react-native-codegen/src/cli/combine/combine-js-to-schema.js +++ b/packages/react-native-codegen/src/cli/combine/combine-js-to-schema.js @@ -11,12 +11,14 @@ 'use strict'; import type {SchemaType} from '../../CodegenSchema.js'; -const {parseFile} = require('../../parsers/utils'); -const FlowParser = require('../../parsers/flow'); -const TypeScriptParser = require('../../parsers/typescript'); +const {FlowParser} = require('../../parsers/flow/parser'); +const {TypeScriptParser} = require('../../parsers/typescript/parser'); const fs = require('fs'); const path = require('path'); +const flowParser = new FlowParser(); +const typescriptParser = new TypeScriptParser(); + function combineSchemas(files: Array): SchemaType { return files.reduce( (merged, filename) => { @@ -30,10 +32,9 @@ function combineSchemas(files: Array): SchemaType { const isTypeScript = path.extname(filename) === '.ts' || path.extname(filename) === '.tsx'; - const schema = parseFile( - filename, - isTypeScript ? TypeScriptParser.buildSchema : FlowParser.buildSchema, - ); + const parser = isTypeScript ? typescriptParser : flowParser; + + const schema = parser.parseFile(filename); if (schema && schema.modules) { merged.modules = {...merged.modules, ...schema.modules}; diff --git a/packages/react-native-codegen/src/cli/parser/parser.js b/packages/react-native-codegen/src/cli/parser/parser.js index 1afc70035bc0..40a599c9ff9c 100644 --- a/packages/react-native-codegen/src/cli/parser/parser.js +++ b/packages/react-native-codegen/src/cli/parser/parser.js @@ -11,26 +11,20 @@ 'use strict'; const path = require('path'); -const {parseFile} = require('../../parsers/utils'); -const FlowParser = require('../../parsers/flow'); -const TypeScriptParser = require('../../parsers/typescript'); +const {FlowParser} = require('../../parsers/flow/parser'); +const {TypeScriptParser} = require('../../parsers/typescript/parser'); + +const flowParser = new FlowParser(); +const typescriptParser = new TypeScriptParser(); function parseFiles(files: Array) { files.forEach(filename => { const isTypeScript = path.extname(filename) === '.ts' || path.extname(filename) === '.tsx'; - console.log( - filename, - JSON.stringify( - parseFile( - filename, - isTypeScript ? TypeScriptParser.buildSchema : FlowParser.buildSchema, - ), - null, - 2, - ), - ); + const parser = isTypeScript ? typescriptParser : flowParser; + + console.log(filename, JSON.stringify(parser.parseFile(filename), null, 2)); }); } diff --git a/packages/react-native-codegen/src/parsers/__tests__/utils-test.js b/packages/react-native-codegen/src/parsers/__tests__/utils-test.js index 15e600403245..0421b65e3514 100644 --- a/packages/react-native-codegen/src/parsers/__tests__/utils-test.js +++ b/packages/react-native-codegen/src/parsers/__tests__/utils-test.js @@ -11,6 +11,8 @@ 'use strict'; +const {MockedParser} = require('../parserMock'); + const { extractNativeModuleName, createParserErrorCapturer, @@ -297,6 +299,8 @@ describe('visit', () => { }); describe('buildSchemaFromConfigType', () => { + const parser = new MockedParser(); + const astMock = { type: 'Program', loc: { @@ -332,7 +336,7 @@ describe('buildSchemaFromConfigType', () => { require('../parsers-commons'), 'wrapModuleSchema', ); - const buildModuleSchemaMock = jest.fn((_0, _1, _2) => moduleSchemaMock); + const buildModuleSchemaMock = jest.fn((_0, _1, _2, _3) => moduleSchemaMock); const buildSchemaFromConfigTypeHelper = ( configType: 'module' | 'component' | 'none', @@ -345,6 +349,7 @@ describe('buildSchemaFromConfigType', () => { wrapComponentSchemaMock, buildComponentSchemaMock, buildModuleSchemaMock, + parser, ); describe('when configType is none', () => { @@ -426,6 +431,7 @@ describe('buildSchemaFromConfigType', () => { 'filename', astMock, expect.any(Function), + parser, ); expect(wrapModuleSchemaMock).toHaveBeenCalledTimes(1); expect(wrapModuleSchemaMock).toHaveBeenCalledWith( diff --git a/packages/react-native-codegen/src/parsers/flow/buildSchema.js b/packages/react-native-codegen/src/parsers/flow/buildSchema.js new file mode 100644 index 000000000000..db8657da3725 --- /dev/null +++ b/packages/react-native-codegen/src/parsers/flow/buildSchema.js @@ -0,0 +1,78 @@ +/** + * 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 strict + * @format + */ + +'use strict'; + +import type {SchemaType} from '../../CodegenSchema'; +import type {Parser} from '../parser'; + +// $FlowFixMe[untyped-import] there's no flowtype flow-parser +const flowParser = require('flow-parser'); +const { + getConfigType, + buildSchemaFromConfigType, + isModuleRegistryCall, +} = require('../utils'); +const {buildComponentSchema} = require('./components'); +const {wrapComponentSchema} = require('./components/schema'); +const {buildModuleSchema} = require('./modules'); + +function Visitor(infoMap: {isComponent: boolean, isModule: boolean}) { + return { + CallExpression(node: $FlowFixMe) { + if ( + node.callee.type === 'Identifier' && + node.callee.name === 'codegenNativeComponent' + ) { + infoMap.isComponent = true; + } + + if (isModuleRegistryCall(node)) { + infoMap.isModule = true; + } + }, + InterfaceExtends(node: $FlowFixMe) { + if (node.id.name === 'TurboModule') { + infoMap.isModule = true; + } + }, + }; +} + +function buildSchema( + contents: string, + filename: ?string, + parser: Parser, +): SchemaType { + // Early return for non-Spec JavaScript files + if ( + !contents.includes('codegenNativeComponent') && + !contents.includes('TurboModule') + ) { + return {modules: {}}; + } + + const ast = flowParser.parse(contents, {enums: true}); + const configType = getConfigType(ast, Visitor); + + return buildSchemaFromConfigType( + configType, + filename, + ast, + wrapComponentSchema, + buildComponentSchema, + buildModuleSchema, + parser, + ); +} + +module.exports = { + buildSchema, +}; diff --git a/packages/react-native-codegen/src/parsers/flow/components/__tests__/component-parser-test.js b/packages/react-native-codegen/src/parsers/flow/components/__tests__/component-parser-test.js index 384b0af5b2c8..e01d1ade6e1f 100644 --- a/packages/react-native-codegen/src/parsers/flow/components/__tests__/component-parser-test.js +++ b/packages/react-native-codegen/src/parsers/flow/components/__tests__/component-parser-test.js @@ -11,8 +11,7 @@ 'use strict'; -const FlowParser = require('../../index.js'); -const {parseFile} = require('../../../utils.js'); +const {FlowParser} = require('../../parser'); const fixtures = require('../__test_fixtures__/fixtures.js'); const failureFixtures = require('../__test_fixtures__/failures.js'); jest.mock('fs', () => ({ @@ -26,12 +25,14 @@ jest.mock('fs', () => ({ }, })); +const parser = new FlowParser(); + describe('RN Codegen Flow Parser', () => { Object.keys(fixtures) .sort() .forEach(fixtureName => { it(`can generate fixture ${fixtureName}`, () => { - const schema = parseFile(fixtureName, FlowParser.buildSchema); + const schema = parser.parseFile(fixtureName); const serializedSchema = JSON.stringify(schema, null, 2).replace( /"/g, "'", @@ -45,7 +46,7 @@ describe('RN Codegen Flow Parser', () => { .forEach(fixtureName => { it(`Fails with error message ${fixtureName}`, () => { expect(() => { - parseFile(fixtureName, FlowParser.buildSchema); + parser.parseFile(fixtureName); }).toThrowErrorMatchingSnapshot(); }); }); diff --git a/packages/react-native-codegen/src/parsers/flow/components/commands.js b/packages/react-native-codegen/src/parsers/flow/components/commands.js index 4d0b8a62fa60..bf789178d996 100644 --- a/packages/react-native-codegen/src/parsers/flow/components/commands.js +++ b/packages/react-native-codegen/src/parsers/flow/components/commands.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow + * @flow strict * @format */ @@ -18,6 +18,7 @@ import type {TypeDeclarationMap} from '../../utils'; const {getValueFromTypes} = require('../utils.js'); +// $FlowFixMe[unclear-type] there's no flowtype for ASTs type EventTypeAST = Object; function buildCommandSchema(property: EventTypeAST, types: TypeDeclarationMap) { diff --git a/packages/react-native-codegen/src/parsers/flow/components/componentsUtils.js b/packages/react-native-codegen/src/parsers/flow/components/componentsUtils.js index be6ec2801055..9faaacab46cf 100644 --- a/packages/react-native-codegen/src/parsers/flow/components/componentsUtils.js +++ b/packages/react-native-codegen/src/parsers/flow/components/componentsUtils.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow strict-local + * @flow strict * @format */ diff --git a/packages/react-native-codegen/src/parsers/flow/components/events.js b/packages/react-native-codegen/src/parsers/flow/components/events.js index 8384edd789b8..9361f0c00505 100644 --- a/packages/react-native-codegen/src/parsers/flow/components/events.js +++ b/packages/react-native-codegen/src/parsers/flow/components/events.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow strict-local + * @flow strict * @format */ diff --git a/packages/react-native-codegen/src/parsers/flow/components/extends.js b/packages/react-native-codegen/src/parsers/flow/components/extends.js index 6f81f7b45408..d53147d9d4f5 100644 --- a/packages/react-native-codegen/src/parsers/flow/components/extends.js +++ b/packages/react-native-codegen/src/parsers/flow/components/extends.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow strict-local + * @flow strict * @format */ diff --git a/packages/react-native-codegen/src/parsers/flow/components/index.js b/packages/react-native-codegen/src/parsers/flow/components/index.js index a0b1eda6cdaa..50026ebf6c1f 100644 --- a/packages/react-native-codegen/src/parsers/flow/components/index.js +++ b/packages/react-native-codegen/src/parsers/flow/components/index.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow strict-local + * @flow strict * @format */ diff --git a/packages/react-native-codegen/src/parsers/flow/components/options.js b/packages/react-native-codegen/src/parsers/flow/components/options.js index beb032eae320..a83ba528b467 100644 --- a/packages/react-native-codegen/src/parsers/flow/components/options.js +++ b/packages/react-native-codegen/src/parsers/flow/components/options.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow strict-local + * @flow strict * @format */ diff --git a/packages/react-native-codegen/src/parsers/flow/components/props.js b/packages/react-native-codegen/src/parsers/flow/components/props.js index 05cdf06b0223..70ff36e8526e 100644 --- a/packages/react-native-codegen/src/parsers/flow/components/props.js +++ b/packages/react-native-codegen/src/parsers/flow/components/props.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow strict-local + * @flow strict * @format */ diff --git a/packages/react-native-codegen/src/parsers/flow/index.js b/packages/react-native-codegen/src/parsers/flow/index.js index 2dfd5ff37ccd..9e2e66a6d999 100644 --- a/packages/react-native-codegen/src/parsers/flow/index.js +++ b/packages/react-native-codegen/src/parsers/flow/index.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow strict-local + * @flow strict * @format */ @@ -12,74 +12,23 @@ import type {SchemaType} from '../../CodegenSchema.js'; -// $FlowFixMe[untyped-import] there's no flowtype flow-parser -const flowParser = require('flow-parser'); const fs = require('fs'); -const { - buildSchemaFromConfigType, - getConfigType, - isModuleRegistryCall, -} = require('../utils'); -const {buildComponentSchema} = require('./components'); -const {wrapComponentSchema} = require('./components/schema'); -const {buildModuleSchema} = require('./modules'); +const {buildSchema} = require('./buildSchema'); +const {FlowParser} = require('./parser'); -function Visitor(infoMap: {isComponent: boolean, isModule: boolean}) { - return { - CallExpression(node: $FlowFixMe) { - if ( - node.callee.type === 'Identifier' && - node.callee.name === 'codegenNativeComponent' - ) { - infoMap.isComponent = true; - } - - if (isModuleRegistryCall(node)) { - infoMap.isModule = true; - } - }, - InterfaceExtends(node: $FlowFixMe) { - if (node.id.name === 'TurboModule') { - infoMap.isModule = true; - } - }, - }; -} - -function buildSchema(contents: string, filename: ?string): SchemaType { - // Early return for non-Spec JavaScript files - if ( - !contents.includes('codegenNativeComponent') && - !contents.includes('TurboModule') - ) { - return {modules: {}}; - } - - const ast = flowParser.parse(contents, {enums: true}); - const configType = getConfigType(ast, Visitor); - - return buildSchemaFromConfigType( - configType, - filename, - ast, - wrapComponentSchema, - buildComponentSchema, - buildModuleSchema, - ); -} +const parser = new FlowParser(); function parseModuleFixture(filename: string): SchemaType { const contents = fs.readFileSync(filename, 'utf8'); - return buildSchema(contents, 'path/NativeSampleTurboModule.js'); + return buildSchema(contents, 'path/NativeSampleTurboModule.js', parser); } function parseString(contents: string, filename: ?string): SchemaType { - return buildSchema(contents, filename); + return buildSchema(contents, filename, parser); } module.exports = { - buildSchema, parseModuleFixture, parseString, }; diff --git a/packages/react-native-codegen/src/parsers/flow/modules/index.js b/packages/react-native-codegen/src/parsers/flow/modules/index.js index 120d6fa8443f..bed63191d6fb 100644 --- a/packages/react-native-codegen/src/parsers/flow/modules/index.js +++ b/packages/react-native-codegen/src/parsers/flow/modules/index.js @@ -19,6 +19,8 @@ import type { NativeModuleSchema, Nullable, } from '../../../CodegenSchema'; + +import type {Parser} from '../../parser'; import type {ParserErrorCapturer, TypeDeclarationMap} from '../../utils'; const {visit, isModuleRegistryCall, verifyPlatforms} = require('../../utils'); @@ -67,10 +69,7 @@ const { throwIfMoreThanOneModuleInterfaceParserError, } = require('../../error-utils'); -const {FlowParser} = require('../parser'); - const language = 'Flow'; -const parser = new FlowParser(); function translateTypeAnnotation( hasteModuleName: string, @@ -82,6 +81,7 @@ function translateTypeAnnotation( aliasMap: {...NativeModuleAliasMap}, tryParse: ParserErrorCapturer, cxxOnly: boolean, + parser: Parser, ): Nullable { const {nullable, typeAnnotation, typeAliasResolutionStatus} = resolveTypeAnnotation(flowTypeAnnotation, types); @@ -133,6 +133,7 @@ function translateTypeAnnotation( aliasMap, tryParse, cxxOnly, + parser, ), ); @@ -222,7 +223,7 @@ function translateTypeAnnotation( tryParse, cxxOnly, translateTypeAnnotation, - language, + parser, ); } case 'UnionTypeAnnotation': { @@ -267,6 +268,7 @@ function buildModuleSchema( */ ast: $FlowFixMe, tryParse: ParserErrorCapturer, + parser: Parser, ): NativeModuleSchema { const types = getTypes(ast); const moduleSpecs = (Object.values(types): $ReadOnlyArray<$FlowFixMe>).filter( @@ -386,9 +388,9 @@ function buildModuleSchema( aliasMap, tryParse, cxxOnly, - language, resolveTypeAnnotation, translateTypeAnnotation, + parser, ), })); }) diff --git a/packages/react-native-codegen/src/parsers/flow/parser.js b/packages/react-native-codegen/src/parsers/flow/parser.js index b0fd878f9c2c..835ae8b111d3 100644 --- a/packages/react-native-codegen/src/parsers/flow/parser.js +++ b/packages/react-native-codegen/src/parsers/flow/parser.js @@ -10,10 +10,17 @@ 'use strict'; -import type {UnionTypeAnnotationMemberType} from '../../CodegenSchema.js'; +import type { + UnionTypeAnnotationMemberType, + SchemaType, +} from '../../CodegenSchema'; import type {ParserType} from '../errors'; import type {Parser} from '../parser'; +const {buildSchema} = require('./buildSchema'); + +const fs = require('fs'); + const { UnsupportedObjectPropertyTypeAnnotationParserError, } = require('../errors'); @@ -76,6 +83,12 @@ class FlowParser implements Parser { return [...new Set(membersTypes.map(remapLiteral))]; } + + parseFile(filename: string): SchemaType { + const contents = fs.readFileSync(filename, 'utf8'); + + return buildSchema(contents, filename, this); + } } module.exports = { diff --git a/packages/react-native-codegen/src/parsers/parser.js b/packages/react-native-codegen/src/parsers/parser.js index fdb03cd32b8f..a78a3b17fb30 100644 --- a/packages/react-native-codegen/src/parsers/parser.js +++ b/packages/react-native-codegen/src/parsers/parser.js @@ -10,7 +10,7 @@ 'use strict'; -import type {UnionTypeAnnotationMemberType} from '../CodegenSchema.js'; +import type {UnionTypeAnnotationMemberType, SchemaType} from '../CodegenSchema'; import type {ParserType} from './errors'; /** @@ -67,4 +67,10 @@ export interface Parser { remapUnionTypeAnnotationMemberNames( types: $FlowFixMe, ): UnionTypeAnnotationMemberType[]; + /** + * Given the content of a file and options, it returns an AST. + * @parameter contents: the content of the file. + * @returns: the AST of the file (given in program property for typescript). + */ + parseFile(filename: string): SchemaType; } diff --git a/packages/react-native-codegen/src/parsers/parserMock.js b/packages/react-native-codegen/src/parsers/parserMock.js index 6f3d16eba978..54789c8f04c5 100644 --- a/packages/react-native-codegen/src/parsers/parserMock.js +++ b/packages/react-native-codegen/src/parsers/parserMock.js @@ -10,9 +10,9 @@ 'use strict'; -import type {UnionTypeAnnotationMemberType} from '../CodegenSchema.js'; import type {Parser} from './parser'; import type {ParserType} from './errors'; +import type {UnionTypeAnnotationMemberType, SchemaType} from '../CodegenSchema'; const { UnsupportedObjectPropertyTypeAnnotationParserError, @@ -65,4 +65,22 @@ export class MockedParser implements Parser { ): UnionTypeAnnotationMemberType[] { return []; } + + parseFile(filename: string): SchemaType { + return { + modules: { + StringPropNativeComponentView: { + type: 'Component', + components: { + StringPropNativeComponentView: { + extendsProps: [], + events: [], + props: [], + commands: [], + }, + }, + }, + }, + }; + } } diff --git a/packages/react-native-codegen/src/parsers/parsers-commons.js b/packages/react-native-codegen/src/parsers/parsers-commons.js index a7884085f910..db25d052a3c7 100644 --- a/packages/react-native-codegen/src/parsers/parsers-commons.js +++ b/packages/react-native-codegen/src/parsers/parsers-commons.js @@ -181,6 +181,7 @@ function parseObjectProperty( aliasMap, tryParse, cxxOnly, + parser, ), ); @@ -291,32 +292,36 @@ function translateFunctionTypeAnnotation( tryParse: ParserErrorCapturer, cxxOnly: boolean, translateTypeAnnotation: $FlowFixMe, - language: ParserType, + parser: Parser, ): NativeModuleFunctionTypeAnnotation { type Param = NamedShape>; const params: Array = []; - for (const param of getTypeAnnotationParameters(typeAnnotation, language)) { + for (const param of getTypeAnnotationParameters( + typeAnnotation, + parser.language(), + )) { const parsedParam = tryParse(() => { - if (getFunctionNameFromParameter(param, language) == null) { + if (getFunctionNameFromParameter(param, parser.language()) == null) { throw new UnnamedFunctionParamParserError( param, hasteModuleName, - language, + parser.language(), ); } - const paramName = getParameterName(param, language); + const paramName = getParameterName(param, parser.language()); const [paramTypeAnnotation, isParamTypeAnnotationNullable] = unwrapNullable<$FlowFixMe>( translateTypeAnnotation( hasteModuleName, - getParameterTypeAnnotation(param, language), + getParameterTypeAnnotation(param, parser.language()), types, aliasMap, tryParse, cxxOnly, + parser, ), ); @@ -351,11 +356,12 @@ function translateFunctionTypeAnnotation( unwrapNullable<$FlowFixMe>( translateTypeAnnotation( hasteModuleName, - getTypeAnnotationReturnType(typeAnnotation, language), + getTypeAnnotationReturnType(typeAnnotation, parser.language()), types, aliasMap, tryParse, cxxOnly, + parser, ), ); @@ -363,7 +369,7 @@ function translateFunctionTypeAnnotation( hasteModuleName, typeAnnotation, 'FunctionTypeAnnotation', - language, + parser.language(), cxxOnly, returnTypeAnnotation.type, ); @@ -390,15 +396,15 @@ function buildPropertySchema( aliasMap: {...NativeModuleAliasMap}, tryParse: ParserErrorCapturer, cxxOnly: boolean, - language: ParserType, resolveTypeAnnotation: $FlowFixMe, translateTypeAnnotation: $FlowFixMe, + parser: Parser, ): NativeModulePropertyShape { let nullable: boolean = false; let {key, value} = property; const methodName: string = key.name; - if (language === 'TypeScript') { + if (parser.language() === 'TypeScript') { value = property.type === 'TSMethodSignature' ? property @@ -412,7 +418,7 @@ function buildPropertySchema( property.value, key.name, value.type, - language, + parser.language(), ); return { @@ -428,7 +434,7 @@ function buildPropertySchema( tryParse, cxxOnly, translateTypeAnnotation, - language, + parser, ), ), }; diff --git a/packages/react-native-codegen/src/parsers/parsers-primitives.js b/packages/react-native-codegen/src/parsers/parsers-primitives.js index 5d4b41338f67..d29bac2b443f 100644 --- a/packages/react-native-codegen/src/parsers/parsers-primitives.js +++ b/packages/react-native-codegen/src/parsers/parsers-primitives.js @@ -31,7 +31,6 @@ import type { StringTypeAnnotation, VoidTypeAnnotation, } from '../CodegenSchema'; -import type {ParserType} from './errors'; import type {Parser} from './parser'; import type { ParserErrorCapturer, @@ -105,7 +104,7 @@ function emitFunction( tryParse: ParserErrorCapturer, cxxOnly: boolean, translateTypeAnnotation: $FlowFixMe, - language: ParserType, + parser: Parser, ): Nullable { const translateFunctionTypeAnnotationValue: NativeModuleFunctionTypeAnnotation = translateFunctionTypeAnnotation( @@ -116,7 +115,7 @@ function emitFunction( tryParse, cxxOnly, translateTypeAnnotation, - language, + parser, ); return wrapNullable(nullable, translateFunctionTypeAnnotationValue); } @@ -225,6 +224,7 @@ function emitPromise( aliasMap, tryParse, cxxOnly, + parser, ), }); } catch { @@ -285,8 +285,8 @@ function translateArrayTypeAnnotation( arrayType: 'Array' | 'ReadonlyArray', elementType: $FlowFixMe, nullable: boolean, - language: ParserType, translateTypeAnnotation: $FlowFixMe, + parser: Parser, ): Nullable { try { /** @@ -310,6 +310,7 @@ function translateArrayTypeAnnotation( */ nullGuard, cxxOnly, + parser, ), ); @@ -318,7 +319,7 @@ function translateArrayTypeAnnotation( elementType, arrayType, _elementType.type, - language, + parser.language(), ); return wrapNullable(nullable, { @@ -357,8 +358,8 @@ function emitArrayType( typeAnnotation.type, typeAnnotation.typeParameters.params[0], nullable, - parser.language(), translateTypeAnnotation, + parser, ); } diff --git a/packages/react-native-codegen/src/parsers/typescript/buildSchema.js b/packages/react-native-codegen/src/parsers/typescript/buildSchema.js new file mode 100644 index 000000000000..c3b56c3e535e --- /dev/null +++ b/packages/react-native-codegen/src/parsers/typescript/buildSchema.js @@ -0,0 +1,88 @@ +/** + * 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 strict + * @format + */ + +'use strict'; + +import type {SchemaType} from '../../CodegenSchema'; +import type {Parser} from '../parser'; + +// $FlowFixMe[untyped-import] Use flow-types for @babel/parser +const babelParser = require('@babel/parser'); +const { + buildSchemaFromConfigType, + getConfigType, + isModuleRegistryCall, +} = require('../utils'); +const {buildComponentSchema} = require('./components'); +const {wrapComponentSchema} = require('./components/schema'); +const {buildModuleSchema} = require('./modules'); + +function Visitor(infoMap: {isComponent: boolean, isModule: boolean}) { + return { + CallExpression(node: $FlowFixMe) { + if ( + node.callee.type === 'Identifier' && + node.callee.name === 'codegenNativeComponent' + ) { + infoMap.isComponent = true; + } + + if (isModuleRegistryCall(node)) { + infoMap.isModule = true; + } + }, + + TSInterfaceDeclaration(node: $FlowFixMe) { + if ( + Array.isArray(node.extends) && + node.extends.some( + extension => extension.expression.name === 'TurboModule', + ) + ) { + infoMap.isModule = true; + } + }, + }; +} + +function buildSchema( + contents: string, + filename: ?string, + parser: Parser, +): SchemaType { + // Early return for non-Spec JavaScript files + if ( + !contents.includes('codegenNativeComponent') && + !contents.includes('TurboModule') + ) { + return {modules: {}}; + } + + const ast = babelParser.parse(contents, { + sourceType: 'module', + plugins: ['typescript'], + }).program; + + const configType = getConfigType(ast, Visitor); + + return buildSchemaFromConfigType( + configType, + filename, + ast, + wrapComponentSchema, + buildComponentSchema, + buildModuleSchema, + parser, + ); +} + +module.exports = { + buildSchema, +}; diff --git a/packages/react-native-codegen/src/parsers/typescript/components/__tests__/typescript-component-parser-test.js b/packages/react-native-codegen/src/parsers/typescript/components/__tests__/typescript-component-parser-test.js index 19a49a6b8ad2..dd56de0b1351 100644 --- a/packages/react-native-codegen/src/parsers/typescript/components/__tests__/typescript-component-parser-test.js +++ b/packages/react-native-codegen/src/parsers/typescript/components/__tests__/typescript-component-parser-test.js @@ -11,8 +11,7 @@ 'use strict'; -const TypeScriptParser = require('../../index.js'); -const {parseFile} = require('../../../utils.js'); +const {TypeScriptParser} = require('../../parser'); const fixtures = require('../__test_fixtures__/fixtures.js'); const failureFixtures = require('../__test_fixtures__/failures.js'); jest.mock('fs', () => ({ @@ -26,12 +25,14 @@ jest.mock('fs', () => ({ }, })); +const parser = new TypeScriptParser(); + describe('RN Codegen TypeScript Parser', () => { Object.keys(fixtures) .sort() .forEach(fixtureName => { it(`can generate fixture ${fixtureName}`, () => { - const schema = parseFile(fixtureName, TypeScriptParser.buildSchema); + const schema = parser.parseFile(fixtureName); const serializedSchema = JSON.stringify(schema, null, 2).replace( /"/g, "'", @@ -45,7 +46,7 @@ describe('RN Codegen TypeScript Parser', () => { .forEach(fixtureName => { it(`Fails with error message ${fixtureName}`, () => { expect(() => { - parseFile(fixtureName, TypeScriptParser.buildSchema); + parser.parseFile(fixtureName); }).toThrowErrorMatchingSnapshot(); }); }); diff --git a/packages/react-native-codegen/src/parsers/typescript/components/commands.js b/packages/react-native-codegen/src/parsers/typescript/components/commands.js index 5e6d38de9c55..390a6d71b960 100644 --- a/packages/react-native-codegen/src/parsers/typescript/components/commands.js +++ b/packages/react-native-codegen/src/parsers/typescript/components/commands.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow + * @flow strict * @format */ @@ -17,6 +17,7 @@ import type { import type {TypeDeclarationMap} from '../../utils'; const {parseTopLevelType} = require('../parseTopLevelType'); +// $FlowFixMe[unclear-type] there's no flowtype for ASTs type EventTypeAST = Object; function buildCommandSchemaInternal( diff --git a/packages/react-native-codegen/src/parsers/typescript/components/componentsUtils.js b/packages/react-native-codegen/src/parsers/typescript/components/componentsUtils.js index ed8b283192de..70e9e33b8b9a 100644 --- a/packages/react-native-codegen/src/parsers/typescript/components/componentsUtils.js +++ b/packages/react-native-codegen/src/parsers/typescript/components/componentsUtils.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow strict-local + * @flow strict * @format */ diff --git a/packages/react-native-codegen/src/parsers/typescript/components/events.js b/packages/react-native-codegen/src/parsers/typescript/components/events.js index 7da6660752e7..dfd295f6ddce 100644 --- a/packages/react-native-codegen/src/parsers/typescript/components/events.js +++ b/packages/react-native-codegen/src/parsers/typescript/components/events.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow strict-local + * @flow strict * @format */ diff --git a/packages/react-native-codegen/src/parsers/typescript/components/extends.js b/packages/react-native-codegen/src/parsers/typescript/components/extends.js index e9fda8c46ee7..1d4d006f5d7c 100644 --- a/packages/react-native-codegen/src/parsers/typescript/components/extends.js +++ b/packages/react-native-codegen/src/parsers/typescript/components/extends.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow strict-local + * @flow strict * @format */ diff --git a/packages/react-native-codegen/src/parsers/typescript/components/index.js b/packages/react-native-codegen/src/parsers/typescript/components/index.js index 46cbba5dd505..d6c66d49a633 100644 --- a/packages/react-native-codegen/src/parsers/typescript/components/index.js +++ b/packages/react-native-codegen/src/parsers/typescript/components/index.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow strict-local + * @flow strict * @format */ diff --git a/packages/react-native-codegen/src/parsers/typescript/components/options.js b/packages/react-native-codegen/src/parsers/typescript/components/options.js index 4b0f9711172f..96ea8abc75b1 100644 --- a/packages/react-native-codegen/src/parsers/typescript/components/options.js +++ b/packages/react-native-codegen/src/parsers/typescript/components/options.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow strict-local + * @flow strict * @format */ diff --git a/packages/react-native-codegen/src/parsers/typescript/components/props.js b/packages/react-native-codegen/src/parsers/typescript/components/props.js index a2b1bcfad482..c7a1d3b04e65 100644 --- a/packages/react-native-codegen/src/parsers/typescript/components/props.js +++ b/packages/react-native-codegen/src/parsers/typescript/components/props.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow strict-local + * @flow strict * @format */ diff --git a/packages/react-native-codegen/src/parsers/typescript/index.js b/packages/react-native-codegen/src/parsers/typescript/index.js index 426e4c8eef95..d790980331c7 100644 --- a/packages/react-native-codegen/src/parsers/typescript/index.js +++ b/packages/react-native-codegen/src/parsers/typescript/index.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow + * @flow strict * @format */ @@ -12,83 +12,23 @@ import type {SchemaType} from '../../CodegenSchema.js'; -const babelParser = require('@babel/parser'); const fs = require('fs'); -const { - buildSchemaFromConfigType, - getConfigType, - isModuleRegistryCall, -} = require('../utils'); -const {buildComponentSchema} = require('./components'); -const {wrapComponentSchema} = require('./components/schema'); -const {buildModuleSchema} = require('./modules'); +const {buildSchema} = require('./buildSchema'); +const {TypeScriptParser} = require('./parser'); -function Visitor(infoMap: {isComponent: boolean, isModule: boolean}) { - return { - CallExpression(node: $FlowFixMe) { - if ( - node.callee.type === 'Identifier' && - node.callee.name === 'codegenNativeComponent' - ) { - infoMap.isComponent = true; - } - - if (isModuleRegistryCall(node)) { - infoMap.isModule = true; - } - }, - - TSInterfaceDeclaration(node: $FlowFixMe) { - if ( - Array.isArray(node.extends) && - node.extends.some( - extension => extension.expression.name === 'TurboModule', - ) - ) { - infoMap.isModule = true; - } - }, - }; -} - -function buildSchema(contents: string, filename: ?string): SchemaType { - // Early return for non-Spec JavaScript files - if ( - !contents.includes('codegenNativeComponent') && - !contents.includes('TurboModule') - ) { - return {modules: {}}; - } - - const ast = babelParser.parse(contents, { - sourceType: 'module', - plugins: ['typescript'], - }).program; - - const configType = getConfigType(ast, Visitor); - - return buildSchemaFromConfigType( - configType, - filename, - ast, - wrapComponentSchema, - buildComponentSchema, - buildModuleSchema, - ); -} +const parser = new TypeScriptParser(); function parseModuleFixture(filename: string): SchemaType { const contents = fs.readFileSync(filename, 'utf8'); - return buildSchema(contents, 'path/NativeSampleTurboModule.ts'); + return buildSchema(contents, 'path/NativeSampleTurboModule.ts', parser); } function parseString(contents: string, filename: ?string): SchemaType { - return buildSchema(contents, filename); + return buildSchema(contents, filename, parser); } module.exports = { - buildSchema, parseModuleFixture, parseString, }; diff --git a/packages/react-native-codegen/src/parsers/typescript/modules/index.js b/packages/react-native-codegen/src/parsers/typescript/modules/index.js index 5991c7c8e3b3..9051b0a8f89a 100644 --- a/packages/react-native-codegen/src/parsers/typescript/modules/index.js +++ b/packages/react-native-codegen/src/parsers/typescript/modules/index.js @@ -20,6 +20,7 @@ import type { Nullable, } from '../../../CodegenSchema'; +import type {Parser} from '../../parser'; import type {ParserErrorCapturer, TypeDeclarationMap} from '../../utils'; const {visit, isModuleRegistryCall, verifyPlatforms} = require('../../utils'); @@ -68,10 +69,7 @@ const { throwIfIncorrectModuleRegistryCallTypeParameterParserError, } = require('../../error-utils'); -const {TypeScriptParser} = require('../parser'); - const language = 'TypeScript'; -const parser = new TypeScriptParser(); function translateTypeAnnotation( hasteModuleName: string, @@ -83,6 +81,7 @@ function translateTypeAnnotation( aliasMap: {...NativeModuleAliasMap}, tryParse: ParserErrorCapturer, cxxOnly: boolean, + parser: Parser, ): Nullable { const {nullable, typeAnnotation, typeAliasResolutionStatus} = resolveTypeAnnotation(typeScriptTypeAnnotation, types); @@ -97,8 +96,8 @@ function translateTypeAnnotation( 'Array', typeAnnotation.elementType, nullable, - language, translateTypeAnnotation, + parser, ); } case 'TSTypeOperator': { @@ -114,8 +113,8 @@ function translateTypeAnnotation( 'ReadonlyArray', typeAnnotation.typeAnnotation.elementType, nullable, - language, translateTypeAnnotation, + parser, ); } else { throw new UnsupportedGenericParserError( @@ -237,7 +236,7 @@ function translateTypeAnnotation( tryParse, cxxOnly, translateTypeAnnotation, - language, + parser, ); } case 'TSUnionType': { @@ -275,6 +274,7 @@ function buildModuleSchema( */ ast: $FlowFixMe, tryParse: ParserErrorCapturer, + parser: Parser, ): NativeModuleSchema { const types = getTypes(ast); const moduleSpecs = (Object.values(types): $ReadOnlyArray<$FlowFixMe>).filter( @@ -399,9 +399,9 @@ function buildModuleSchema( aliasMap, tryParse, cxxOnly, - language, resolveTypeAnnotation, translateTypeAnnotation, + parser, ), })); }) diff --git a/packages/react-native-codegen/src/parsers/typescript/parser.js b/packages/react-native-codegen/src/parsers/typescript/parser.js index 12d2d5beb06b..0c077093ba0e 100644 --- a/packages/react-native-codegen/src/parsers/typescript/parser.js +++ b/packages/react-native-codegen/src/parsers/typescript/parser.js @@ -10,10 +10,17 @@ 'use strict'; -import type {UnionTypeAnnotationMemberType} from '../../CodegenSchema.js'; +import type { + UnionTypeAnnotationMemberType, + SchemaType, +} from '../../CodegenSchema'; import type {ParserType} from '../errors'; import type {Parser} from '../parser'; +const {buildSchema} = require('./buildSchema'); + +const fs = require('fs'); + const { UnsupportedObjectPropertyTypeAnnotationParserError, } = require('../errors'); @@ -81,6 +88,12 @@ class TypeScriptParser implements Parser { return [...new Set(membersTypes.map(remapLiteral))]; } + + parseFile(filename: string): SchemaType { + const contents = fs.readFileSync(filename, 'utf8'); + + return buildSchema(contents, filename, this); + } } module.exports = { TypeScriptParser, diff --git a/packages/react-native-codegen/src/parsers/typescript/utils.js b/packages/react-native-codegen/src/parsers/typescript/utils.js index 0bc07592f1d2..83e2f8bd2def 100644 --- a/packages/react-native-codegen/src/parsers/typescript/utils.js +++ b/packages/react-native-codegen/src/parsers/typescript/utils.js @@ -17,7 +17,6 @@ const {parseTopLevelType} = require('./parseTopLevelType'); /** * TODO(T108222691): Use flow-types for @babel/parser */ - function getTypes(ast: $FlowFixMe): TypeDeclarationMap { return ast.body.reduce((types, node) => { switch (node.type) { diff --git a/packages/react-native-codegen/src/parsers/utils.js b/packages/react-native-codegen/src/parsers/utils.js index 2d7ca6fae8c7..1c4362bbf04e 100644 --- a/packages/react-native-codegen/src/parsers/utils.js +++ b/packages/react-native-codegen/src/parsers/utils.js @@ -12,10 +12,11 @@ import type {ComponentSchemaBuilderConfig} from './flow/components/schema'; import type {NativeModuleSchema, SchemaType} from '../CodegenSchema'; +import type {Parser} from './parser'; + const {ParserError} = require('./errors'); const {wrapModuleSchema} = require('./parsers-commons'); -const fs = require('fs'); const path = require('path'); const invariant = require('invariant'); @@ -95,15 +96,6 @@ function verifyPlatforms( }; } -function parseFile( - filename: string, - callback: (contents: string, filename: string) => SchemaType, -): SchemaType { - const contents = fs.readFileSync(filename, 'utf8'); - - return callback(contents, filename); -} - // TODO(T108222691): Use flow-types for @babel/parser function visit( astNode: $FlowFixMe, @@ -143,7 +135,9 @@ function buildSchemaFromConfigType( hasteModuleName: string, ast: $FlowFixMe, tryParse: ParserErrorCapturer, + parser: Parser, ) => NativeModuleSchema, + parser: Parser, ): SchemaType { switch (configType) { case 'component': { @@ -158,7 +152,7 @@ function buildSchemaFromConfigType( const [parsingErrors, tryParse] = createParserErrorCapturer(); const schema = tryParse(() => - buildModuleSchema(nativeModuleName, ast, tryParse), + buildModuleSchema(nativeModuleName, ast, tryParse, parser), ); if (parsingErrors.length > 0) { @@ -259,7 +253,6 @@ module.exports = { extractNativeModuleName, createParserErrorCapturer, verifyPlatforms, - parseFile, visit, buildSchemaFromConfigType, isModuleRegistryCall,