Skip to content

Commit 078f631

Browse files
charlesbdudleyfacebook-github-bot
authored andcommitted
Choose parser based on file extension
Summary: This adds the main entry point for the TypeScript parser as well as adds the logic to use the new parser if the spec file's extension is `.ts` `js/react-native-github/packages/react-native-codegen/src/parsers/typescript/index.js` is mostly a direct copy from the flow parser. Changelog: [General][Add] - Choose Flow or WIP TypeScript Codegen parser based on spec's file extension Reviewed By: RSNara Differential Revision: D33081296 fbshipit-source-id: 267823685e6723e3c1f19752bbbe692e895c075b
1 parent 0d3036a commit 078f631

File tree

4 files changed

+175
-3
lines changed

4 files changed

+175
-3
lines changed

packages/react-native-codegen/src/cli/combine/combine-js-to-schema-cli.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ const allFiles = [];
3535
fileList.forEach(file => {
3636
if (fs.lstatSync(file).isDirectory()) {
3737
const dirFiles = glob
38-
.sync(`${file}/**/*.js`, {
38+
.sync(`${file}/**/*.{js,ts,tsx}`, {
3939
nodir: true,
4040
})
4141
.filter(filterJSFile);

packages/react-native-codegen/src/cli/combine/combine-js-to-schema.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,26 @@
1212
import type {SchemaType} from '../../CodegenSchema.js';
1313

1414
const FlowParser = require('../../parsers/flow');
15+
const TypeScriptParser = require('../../parsers/typescript');
1516
const fs = require('fs');
17+
const path = require('path');
1618

1719
function combineSchemas(files: Array<string>): SchemaType {
1820
return files.reduce(
1921
(merged, filename) => {
2022
const contents = fs.readFileSync(filename, 'utf8');
23+
2124
if (
2225
contents &&
2326
(/export\s+default\s+\(?codegenNativeComponent</.test(contents) ||
2427
/extends TurboModule/.test(contents))
2528
) {
26-
const schema = FlowParser.parseFile(filename);
29+
const isTypeScript =
30+
path.extname(filename) === '.ts' || path.extname(filename) === '.tsx';
31+
32+
const schema = isTypeScript
33+
? TypeScriptParser.parseFile(filename)
34+
: FlowParser.parseFile(filename);
2735

2836
if (schema && schema.modules) {
2937
merged.modules = {...merged.modules, ...schema.modules};

packages/react-native-codegen/src/cli/parser/parser.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,24 @@
1010

1111
'use strict';
1212

13+
const path = require('path');
1314
const FlowParser = require('../../parsers/flow');
15+
const TypeScriptParser = require('../../parsers/typescript');
1416

1517
function parseFiles(files: Array<string>) {
1618
files.forEach(filename => {
19+
const isTypeScript =
20+
path.extname(filename) === '.ts' || path.extname(filename) === '.tsx';
21+
1722
console.log(
1823
filename,
19-
JSON.stringify(FlowParser.parseFile(filename), null, 2),
24+
JSON.stringify(
25+
isTypeScript
26+
? TypeScriptParser.parseFile(filename)
27+
: FlowParser.parseFile(filename),
28+
null,
29+
2,
30+
),
2031
);
2132
});
2233
}
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow
8+
* @format
9+
*/
10+
11+
'use strict';
12+
13+
import type {SchemaType} from '../../CodegenSchema.js';
14+
const babelParser = require('@babel/parser');
15+
const fs = require('fs');
16+
const path = require('path');
17+
const {buildComponentSchema} = require('./components');
18+
const {wrapComponentSchema} = require('./components/schema');
19+
const {buildModuleSchema} = require('./modules');
20+
const {wrapModuleSchema} = require('./modules/schema');
21+
22+
const {
23+
createParserErrorCapturer,
24+
visit,
25+
isModuleRegistryCall,
26+
} = require('./utils');
27+
const invariant = require('invariant');
28+
29+
function getConfigType(
30+
// TODO(T108222691): Use flow-types for @babel/parser
31+
ast: $FlowFixMe,
32+
): 'module' | 'component' | 'none' {
33+
let isComponent = false;
34+
let isModule = false;
35+
36+
visit(ast, {
37+
CallExpression(node) {
38+
if (
39+
node.callee.type === 'Identifier' &&
40+
node.callee.name === 'codegenNativeComponent'
41+
) {
42+
isComponent = true;
43+
}
44+
45+
if (isModuleRegistryCall(node)) {
46+
isModule = true;
47+
}
48+
},
49+
50+
TSInterfaceDeclaration(node) {
51+
if (
52+
Array.isArray(node.extends) &&
53+
node.extends.some(
54+
extension => extension.expression.name === 'TurboModule',
55+
)
56+
) {
57+
isModule = true;
58+
}
59+
},
60+
});
61+
62+
if (isModule && isComponent) {
63+
throw new Error(
64+
'Found type extending "TurboModule" and exported "codegenNativeComponent" declaration in one file. Split them into separated files.',
65+
);
66+
}
67+
68+
if (isModule) {
69+
return 'module';
70+
} else if (isComponent) {
71+
return 'component';
72+
} else {
73+
return 'none';
74+
}
75+
}
76+
77+
function buildSchema(contents: string, filename: ?string): SchemaType {
78+
// Early return for non-Spec JavaScript files
79+
if (
80+
!contents.includes('codegenNativeComponent') &&
81+
!contents.includes('TurboModule')
82+
) {
83+
return {modules: {}};
84+
}
85+
86+
const ast = babelParser.parse(contents, {
87+
sourceType: 'module',
88+
plugins: ['typescript'],
89+
}).program;
90+
91+
const configType = getConfigType(ast);
92+
93+
switch (configType) {
94+
case 'component': {
95+
return wrapComponentSchema(buildComponentSchema(ast));
96+
}
97+
case 'module': {
98+
if (filename === undefined || filename === null) {
99+
throw new Error('Filepath expected while parasing a module');
100+
}
101+
const hasteModuleName = path.basename(filename).replace(/\.tsx?$/, '');
102+
103+
const [parsingErrors, tryParse] = createParserErrorCapturer();
104+
105+
const schema = tryParse(() =>
106+
buildModuleSchema(hasteModuleName, ast, tryParse),
107+
);
108+
109+
if (parsingErrors.length > 0) {
110+
/**
111+
* TODO(T77968131): We have two options:
112+
* - Throw the first error, but indicate there are more then one errors.
113+
* - Display all errors, nicely formatted.
114+
*
115+
* For the time being, we're just throw the first error.
116+
**/
117+
118+
throw parsingErrors[0];
119+
}
120+
121+
invariant(
122+
schema != null,
123+
'When there are no parsing errors, the schema should not be null',
124+
);
125+
126+
return wrapModuleSchema(schema, hasteModuleName);
127+
}
128+
default:
129+
return {modules: {}};
130+
}
131+
}
132+
133+
function parseFile(filename: string): SchemaType {
134+
const contents = fs.readFileSync(filename, 'utf8');
135+
136+
return buildSchema(contents, filename);
137+
}
138+
139+
function parseModuleFixture(filename: string): SchemaType {
140+
const contents = fs.readFileSync(filename, 'utf8');
141+
142+
return buildSchema(contents, 'path/NativeSampleTurboModule.ts');
143+
}
144+
145+
function parseString(contents: string, filename: ?string): SchemaType {
146+
return buildSchema(contents, filename);
147+
}
148+
149+
module.exports = {
150+
parseFile,
151+
parseModuleFixture,
152+
parseString,
153+
};

0 commit comments

Comments
 (0)