Extract the parseFile function in the typescript and flow parsers (#35318)

Summary:
This PR aims to extract  the parseFile function in the typescript and flow parsers.  This is to solve the problem described [here](https://github.com/facebook/react-native/pull/35158#issuecomment-1298330753) and help with the work done in https://github.com/facebook/react-native/issues/34872.

## Changelog

<!-- Help reviewers and the release process by writing your own changelog entry. For an example, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests
-->
[Internal] [Changed] - Extract the parseFile function in the typescript and flow parsers

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

Test Plan:
yarn flow:
<img width="496" alt="image" src="https://user-images.githubusercontent.com/40902940/206518024-83084c3d-ab0d-4a04-810a-d40270add4b0.png">

yarn lint:
<img width="495" alt="image" src="https://user-images.githubusercontent.com/40902940/206518076-9e07eafe-db61-4c6e-8aaa-f92f190cf4f3.png">

yarn test:
<img width="389" alt="image" src="https://user-images.githubusercontent.com/40902940/206518118-5633b28c-b79b-4421-80f7-de1e03fb8ff2.png">

Reviewed By: cortinico

Differential Revision: D41248581

Pulled By: cipolleschi

fbshipit-source-id: f5b878a28a7de612fcdd1528f064b44f668503af
This commit is contained in:
MaeIg 2022-12-13 09:00:46 -08:00 коммит произвёл Facebook GitHub Bot
Родитель 234486068e
Коммит 3f2691cf84
46 изменённых файлов: 382 добавлений и 289 удалений

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

@ -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 => {

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

@ -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();
});

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

@ -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();
});

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

@ -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();
});

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

@ -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();
});

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

@ -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();
});

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

@ -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();
});

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

@ -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();
});

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

@ -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();
});

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

@ -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();
});

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

@ -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();
});

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

@ -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();
});

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

@ -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<string> = fs.readdirSync(FIXTURE_DIR);
return filenames.reduce<SchemaType>(
(accumulator, file) => {
const schema = parseFile(
`${FIXTURE_DIR}/${file}`,
FlowParser.buildSchema,
);
const schema = parser.parseFile(`${FIXTURE_DIR}/${file}`);
return {
modules: {
...accumulator.modules,

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

@ -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<string>): SchemaType {
return files.reduce(
(merged, filename) => {
@ -30,10 +32,9 @@ function combineSchemas(files: Array<string>): 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};

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

@ -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<string>) {
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));
});
}

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

@ -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(

78
packages/react-native-codegen/src/parsers/flow/buildSchema.js поставляемый Normal file
Просмотреть файл

@ -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,
};

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

@ -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();
});
});

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

@ -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) {

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

@ -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
*/

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

@ -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
*/

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

@ -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
*/

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

@ -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
*/

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

@ -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
*/

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

@ -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
*/

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

@ -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,
};

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

@ -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<NativeModuleTypeAnnotation> {
const {nullable, typeAnnotation, typeAliasResolutionStatus} =
resolveTypeAnnotation(flowTypeAnnotation, types);
@ -133,6 +133,7 @@ function translateTypeAnnotation(
aliasMap,
tryParse,
cxxOnly,
parser,
),
);
@ -182,6 +183,7 @@ function translateTypeAnnotation(
aliasMap,
tryParse,
cxxOnly,
parser,
);
// no need to do further checking
return emitObject(nullable);
@ -244,7 +246,7 @@ function translateTypeAnnotation(
tryParse,
cxxOnly,
translateTypeAnnotation,
language,
parser,
);
}
case 'UnionTypeAnnotation': {
@ -290,6 +292,7 @@ function buildModuleSchema(
*/
ast: $FlowFixMe,
tryParse: ParserErrorCapturer,
parser: Parser,
): NativeModuleSchema {
const types = getTypes(ast);
const moduleSpecs = (Object.values(types): $ReadOnlyArray<$FlowFixMe>).filter(
@ -409,9 +412,9 @@ function buildModuleSchema(
aliasMap,
tryParse,
cxxOnly,
language,
resolveTypeAnnotation,
translateTypeAnnotation,
parser,
),
}));
})

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

@ -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');
@ -75,6 +82,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 = {

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

@ -10,7 +10,7 @@
'use strict';
import type {UnionTypeAnnotationMemberType} from '../CodegenSchema.js';
import type {UnionTypeAnnotationMemberType, SchemaType} from '../CodegenSchema';
import type {ParserType} from './errors';
/**
@ -71,4 +71,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;
}

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

@ -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,
@ -64,4 +64,22 @@ export class MockedParser implements Parser {
): UnionTypeAnnotationMemberType[] {
return [];
}
parseFile(filename: string): SchemaType {
return {
modules: {
StringPropNativeComponentView: {
type: 'Component',
components: {
StringPropNativeComponentView: {
extendsProps: [],
events: [],
props: [],
commands: [],
},
},
},
},
};
}
}

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

@ -152,6 +152,7 @@ function parseObjectProperty(
aliasMap,
tryParse,
cxxOnly,
parser,
),
);
@ -262,32 +263,36 @@ function translateFunctionTypeAnnotation(
tryParse: ParserErrorCapturer,
cxxOnly: boolean,
translateTypeAnnotation: $FlowFixMe,
language: ParserType,
parser: Parser,
): NativeModuleFunctionTypeAnnotation {
type Param = NamedShape<Nullable<NativeModuleParamTypeAnnotation>>;
const params: Array<Param> = [];
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,
),
);
@ -322,11 +327,12 @@ function translateFunctionTypeAnnotation(
unwrapNullable<$FlowFixMe>(
translateTypeAnnotation(
hasteModuleName,
getTypeAnnotationReturnType(typeAnnotation, language),
getTypeAnnotationReturnType(typeAnnotation, parser.language()),
types,
aliasMap,
tryParse,
cxxOnly,
parser,
),
);
@ -334,7 +340,7 @@ function translateFunctionTypeAnnotation(
hasteModuleName,
typeAnnotation,
'FunctionTypeAnnotation',
language,
parser.language(),
cxxOnly,
returnTypeAnnotation.type,
);
@ -361,15 +367,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
@ -383,7 +389,7 @@ function buildPropertySchema(
property.value,
key.name,
value.type,
language,
parser.language(),
);
return {
@ -399,7 +405,7 @@ function buildPropertySchema(
tryParse,
cxxOnly,
translateTypeAnnotation,
language,
parser,
),
),
};

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

@ -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<NativeModuleFunctionTypeAnnotation> {
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<NativeModuleTypeAnnotation> {
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,
);
}

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

@ -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,
};

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

@ -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();
});
});

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

@ -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(

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

@ -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
*/

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

@ -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
*/

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

@ -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
*/

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

@ -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
*/

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

@ -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
*/

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

@ -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
*/

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

@ -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,
};

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

@ -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<NativeModuleTypeAnnotation> {
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(
@ -200,6 +199,7 @@ function translateTypeAnnotation(
aliasMap,
tryParse,
cxxOnly,
parser,
);
// no need to do further checking
return emitObject(nullable);
@ -259,7 +259,7 @@ function translateTypeAnnotation(
tryParse,
cxxOnly,
translateTypeAnnotation,
language,
parser,
);
}
case 'TSUnionType': {
@ -297,6 +297,7 @@ function buildModuleSchema(
*/
ast: $FlowFixMe,
tryParse: ParserErrorCapturer,
parser: Parser,
): NativeModuleSchema {
const types = getTypes(ast);
const moduleSpecs = (Object.values(types): $ReadOnlyArray<$FlowFixMe>).filter(
@ -421,9 +422,9 @@ function buildModuleSchema(
aliasMap,
tryParse,
cxxOnly,
language,
resolveTypeAnnotation,
translateTypeAnnotation,
parser,
),
}));
})

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

@ -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,

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

@ -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) {

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

@ -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,