@react-native-windows/codegen 0.0.0-canary.9 → 0.0.1-0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +726 -15
- package/README.md +1 -1
- package/bin.js +0 -0
- package/lib-commonjs/Cli.d.ts +7 -0
- package/lib-commonjs/Cli.js +92 -0
- package/lib-commonjs/Cli.js.map +1 -0
- package/lib-commonjs/generators/AliasGen.d.ts +12 -0
- package/lib-commonjs/generators/AliasGen.js +88 -0
- package/lib-commonjs/generators/AliasGen.js.map +1 -0
- package/lib-commonjs/generators/AliasManaging.d.ts +15 -0
- package/lib-commonjs/generators/AliasManaging.js +49 -0
- package/lib-commonjs/generators/AliasManaging.js.map +1 -0
- package/lib-commonjs/generators/GenerateNM2.d.ts +15 -0
- package/lib-commonjs/generators/GenerateNM2.js +142 -0
- package/lib-commonjs/generators/GenerateNM2.js.map +1 -0
- package/lib-commonjs/generators/GenerateTypeScript.d.ts +11 -0
- package/lib-commonjs/generators/GenerateTypeScript.js +166 -0
- package/lib-commonjs/generators/GenerateTypeScript.js.map +1 -0
- package/lib-commonjs/generators/ObjectTypes.d.ts +13 -0
- package/lib-commonjs/generators/ObjectTypes.js +75 -0
- package/lib-commonjs/generators/ObjectTypes.js.map +1 -0
- package/lib-commonjs/generators/ParamTypes.d.ts +12 -0
- package/lib-commonjs/generators/ParamTypes.js +137 -0
- package/lib-commonjs/generators/ParamTypes.js.map +1 -0
- package/lib-commonjs/generators/ReturnTypes.d.ts +10 -0
- package/lib-commonjs/generators/ReturnTypes.js +29 -0
- package/lib-commonjs/generators/ReturnTypes.js.map +1 -0
- package/lib-commonjs/generators/ValidateConstants.d.ts +8 -0
- package/lib-commonjs/generators/ValidateConstants.js +38 -0
- package/lib-commonjs/generators/ValidateConstants.js.map +1 -0
- package/lib-commonjs/generators/ValidateMethods.d.ts +9 -0
- package/lib-commonjs/generators/ValidateMethods.js +75 -0
- package/lib-commonjs/generators/ValidateMethods.js.map +1 -0
- package/lib-commonjs/index.d.ts +37 -0
- package/lib-commonjs/index.js +224 -0
- package/lib-commonjs/index.js.map +1 -0
- package/package.json +37 -20
- package/src/Cli.ts +57 -190
- package/src/generators/AliasGen.ts +149 -0
- package/src/generators/AliasManaging.ts +75 -0
- package/src/generators/GenerateNM2.ts +138 -295
- package/src/generators/GenerateTypeScript.ts +247 -0
- package/src/generators/ObjectTypes.ts +130 -0
- package/src/generators/ParamTypes.ts +298 -0
- package/src/generators/ReturnTypes.ts +70 -0
- package/src/generators/ValidateConstants.ts +50 -0
- package/src/generators/ValidateMethods.ts +177 -0
- package/src/index.ts +393 -0
- package/.eslintrc.js +0 -4
- package/.vscode/launch.json +0 -23
- package/CHANGELOG.json +0 -583
- package/jest.config.js +0 -1
- package/tsconfig.json +0 -5
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Microsoft Corporation.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
* @format
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
import type {
|
|
10
|
+
NativeModuleReturnTypeAnnotation,
|
|
11
|
+
Nullable,
|
|
12
|
+
} from '@react-native/codegen/lib/CodegenSchema';
|
|
13
|
+
import {AliasMap} from './AliasManaging';
|
|
14
|
+
import {CppCodegenOptions, translateFieldOrReturnType} from './ObjectTypes';
|
|
15
|
+
|
|
16
|
+
function translateReturnType(
|
|
17
|
+
type: Nullable<NativeModuleReturnTypeAnnotation>,
|
|
18
|
+
aliases: AliasMap,
|
|
19
|
+
baseAliasName: string,
|
|
20
|
+
options: CppCodegenOptions,
|
|
21
|
+
): string {
|
|
22
|
+
switch (type.type) {
|
|
23
|
+
case 'VoidTypeAnnotation':
|
|
24
|
+
case 'PromiseTypeAnnotation':
|
|
25
|
+
return 'void';
|
|
26
|
+
case 'NullableTypeAnnotation':
|
|
27
|
+
return `std::optional<${translateReturnType(
|
|
28
|
+
type.typeAnnotation,
|
|
29
|
+
aliases,
|
|
30
|
+
baseAliasName,
|
|
31
|
+
options,
|
|
32
|
+
)}>`;
|
|
33
|
+
default:
|
|
34
|
+
return translateFieldOrReturnType(
|
|
35
|
+
type,
|
|
36
|
+
aliases,
|
|
37
|
+
baseAliasName,
|
|
38
|
+
'translateReturnType',
|
|
39
|
+
options,
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function translateSpecReturnType(
|
|
45
|
+
type: Nullable<NativeModuleReturnTypeAnnotation>,
|
|
46
|
+
aliases: AliasMap,
|
|
47
|
+
baseAliasName: string,
|
|
48
|
+
options: CppCodegenOptions,
|
|
49
|
+
) {
|
|
50
|
+
return translateReturnType(
|
|
51
|
+
type,
|
|
52
|
+
aliases,
|
|
53
|
+
`${baseAliasName}_returnType`,
|
|
54
|
+
options,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function translateImplReturnType(
|
|
59
|
+
type: Nullable<NativeModuleReturnTypeAnnotation>,
|
|
60
|
+
aliases: AliasMap,
|
|
61
|
+
baseAliasName: string,
|
|
62
|
+
options: CppCodegenOptions,
|
|
63
|
+
) {
|
|
64
|
+
return translateReturnType(
|
|
65
|
+
type,
|
|
66
|
+
aliases,
|
|
67
|
+
`${baseAliasName}_returnType`,
|
|
68
|
+
options,
|
|
69
|
+
);
|
|
70
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Microsoft Corporation.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
* @format
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
import type {NativeModuleSchema} from '@react-native/codegen/lib/CodegenSchema';
|
|
10
|
+
import {AliasMap, getAnonymousAliasCppName} from './AliasManaging';
|
|
11
|
+
|
|
12
|
+
export function generateValidateConstants(
|
|
13
|
+
nativeModule: NativeModuleSchema,
|
|
14
|
+
aliases: AliasMap,
|
|
15
|
+
): [string, string] | undefined {
|
|
16
|
+
const candidates = nativeModule.spec.properties.filter(
|
|
17
|
+
prop => prop.name === 'getConstants',
|
|
18
|
+
);
|
|
19
|
+
if (candidates.length === 0) {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const getConstant = candidates[0];
|
|
24
|
+
const funcType =
|
|
25
|
+
getConstant.typeAnnotation.type === 'NullableTypeAnnotation'
|
|
26
|
+
? getConstant.typeAnnotation.typeAnnotation
|
|
27
|
+
: getConstant.typeAnnotation;
|
|
28
|
+
if (
|
|
29
|
+
funcType.params.length > 0 ||
|
|
30
|
+
funcType.returnTypeAnnotation.type !== 'ObjectTypeAnnotation'
|
|
31
|
+
) {
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const constantType = funcType.returnTypeAnnotation;
|
|
36
|
+
if (constantType.properties.length === 0) {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const cppName = getAnonymousAliasCppName(aliases, 'Constants', constantType);
|
|
41
|
+
|
|
42
|
+
return [
|
|
43
|
+
` TypedConstant<${cppName}>{0},`,
|
|
44
|
+
` REACT_SHOW_CONSTANT_SPEC_ERRORS(
|
|
45
|
+
0,
|
|
46
|
+
"${cppName}",
|
|
47
|
+
" REACT_GET_CONSTANTS(GetConstants) ${cppName} GetConstants() noexcept {/*implementation*/}\\n"
|
|
48
|
+
" REACT_GET_CONSTANTS(GetConstants) static ${cppName} GetConstants() noexcept {/*implementation*/}\\n");`,
|
|
49
|
+
];
|
|
50
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Microsoft Corporation.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
* @format
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
import type {
|
|
10
|
+
NativeModuleFunctionTypeAnnotation,
|
|
11
|
+
NativeModulePropertyShape,
|
|
12
|
+
NativeModuleSchema,
|
|
13
|
+
} from '@react-native/codegen/lib/CodegenSchema';
|
|
14
|
+
import {AliasMap} from './AliasManaging';
|
|
15
|
+
import type {CppCodegenOptions} from './ObjectTypes';
|
|
16
|
+
import {translateArgs, translateSpecArgs} from './ParamTypes';
|
|
17
|
+
import {translateImplReturnType, translateSpecReturnType} from './ReturnTypes';
|
|
18
|
+
|
|
19
|
+
function isMethodSync(funcType: NativeModuleFunctionTypeAnnotation) {
|
|
20
|
+
return (
|
|
21
|
+
funcType.returnTypeAnnotation.type !== 'VoidTypeAnnotation' &&
|
|
22
|
+
funcType.returnTypeAnnotation.type !== 'PromiseTypeAnnotation'
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function getPossibleMethodSignatures(
|
|
27
|
+
prop: NativeModulePropertyShape,
|
|
28
|
+
funcType: NativeModuleFunctionTypeAnnotation,
|
|
29
|
+
aliases: AliasMap,
|
|
30
|
+
baseAliasName: string,
|
|
31
|
+
options: CppCodegenOptions,
|
|
32
|
+
): string[] {
|
|
33
|
+
const args = translateArgs(funcType.params, aliases, baseAliasName, options);
|
|
34
|
+
if (funcType.returnTypeAnnotation.type === 'PromiseTypeAnnotation') {
|
|
35
|
+
if (funcType.returnTypeAnnotation.elementType) {
|
|
36
|
+
args.push(
|
|
37
|
+
`::React::ReactPromise<${translateImplReturnType(
|
|
38
|
+
funcType.returnTypeAnnotation.elementType,
|
|
39
|
+
aliases,
|
|
40
|
+
baseAliasName,
|
|
41
|
+
options,
|
|
42
|
+
)}> &&result`,
|
|
43
|
+
);
|
|
44
|
+
} else {
|
|
45
|
+
args.push('::React::ReactPromise<::React::JSValue> &&result');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// TODO: be much more exhaustive on the possible method signatures that can be used..
|
|
50
|
+
const sig = `REACT_${isMethodSync(funcType) ? 'SYNC_' : ''}METHOD(${
|
|
51
|
+
prop.name
|
|
52
|
+
}) ${translateImplReturnType(
|
|
53
|
+
funcType.returnTypeAnnotation,
|
|
54
|
+
aliases,
|
|
55
|
+
baseAliasName,
|
|
56
|
+
options,
|
|
57
|
+
)} ${prop.name}(${args.join(', ')}) noexcept { /* implementation */ }`;
|
|
58
|
+
|
|
59
|
+
const staticsig = `REACT_${isMethodSync(funcType) ? 'SYNC_' : ''}METHOD(${
|
|
60
|
+
prop.name
|
|
61
|
+
}) static ${translateImplReturnType(
|
|
62
|
+
funcType.returnTypeAnnotation,
|
|
63
|
+
aliases,
|
|
64
|
+
baseAliasName,
|
|
65
|
+
options,
|
|
66
|
+
)} ${prop.name}(${args.join(', ')}) noexcept { /* implementation */ }`;
|
|
67
|
+
|
|
68
|
+
return [sig, staticsig];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function translatePossibleMethodSignatures(
|
|
72
|
+
prop: NativeModulePropertyShape,
|
|
73
|
+
funcType: NativeModuleFunctionTypeAnnotation,
|
|
74
|
+
aliases: AliasMap,
|
|
75
|
+
baseAliasName: string,
|
|
76
|
+
options: CppCodegenOptions,
|
|
77
|
+
): string {
|
|
78
|
+
return getPossibleMethodSignatures(
|
|
79
|
+
prop,
|
|
80
|
+
funcType,
|
|
81
|
+
aliases,
|
|
82
|
+
baseAliasName,
|
|
83
|
+
options,
|
|
84
|
+
)
|
|
85
|
+
.map(sig => `" ${sig}\\n"`)
|
|
86
|
+
.join('\n ');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function renderProperties(
|
|
90
|
+
properties: ReadonlyArray<NativeModulePropertyShape>,
|
|
91
|
+
aliases: AliasMap,
|
|
92
|
+
tuple: boolean,
|
|
93
|
+
options: CppCodegenOptions,
|
|
94
|
+
): string {
|
|
95
|
+
// TODO: generate code for constants
|
|
96
|
+
return properties
|
|
97
|
+
.filter(prop => prop.name !== 'getConstants')
|
|
98
|
+
.map((prop, index) => {
|
|
99
|
+
// TODO: prop.optional === true
|
|
100
|
+
// TODO: prop.typeAnnotation.type === 'NullableTypeAnnotation'
|
|
101
|
+
const propAliasName = prop.name;
|
|
102
|
+
const funcType =
|
|
103
|
+
prop.typeAnnotation.type === 'NullableTypeAnnotation'
|
|
104
|
+
? prop.typeAnnotation.typeAnnotation
|
|
105
|
+
: prop.typeAnnotation;
|
|
106
|
+
|
|
107
|
+
const traversedArgs = translateSpecArgs(
|
|
108
|
+
funcType.params,
|
|
109
|
+
aliases,
|
|
110
|
+
propAliasName,
|
|
111
|
+
options,
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
const translatedReturnParam = translateSpecReturnType(
|
|
115
|
+
funcType.returnTypeAnnotation,
|
|
116
|
+
aliases,
|
|
117
|
+
propAliasName,
|
|
118
|
+
options,
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
if (funcType.returnTypeAnnotation.type === 'PromiseTypeAnnotation') {
|
|
122
|
+
if (funcType.returnTypeAnnotation.elementType) {
|
|
123
|
+
traversedArgs.push(
|
|
124
|
+
`Promise<${translateSpecReturnType(
|
|
125
|
+
funcType.returnTypeAnnotation.elementType,
|
|
126
|
+
aliases,
|
|
127
|
+
propAliasName,
|
|
128
|
+
options,
|
|
129
|
+
)}>`,
|
|
130
|
+
);
|
|
131
|
+
} else {
|
|
132
|
+
traversedArgs.push('Promise<::React::JSValue>');
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (tuple) {
|
|
137
|
+
return ` ${
|
|
138
|
+
isMethodSync(funcType) ? 'Sync' : ''
|
|
139
|
+
}Method<${translatedReturnParam}(${traversedArgs.join(
|
|
140
|
+
', ',
|
|
141
|
+
)}) noexcept>{${index}, L"${prop.name}"},`;
|
|
142
|
+
} else {
|
|
143
|
+
return ` REACT_SHOW_METHOD_SPEC_ERRORS(
|
|
144
|
+
${index},
|
|
145
|
+
"${prop.name}",
|
|
146
|
+
${translatePossibleMethodSignatures(
|
|
147
|
+
prop,
|
|
148
|
+
funcType,
|
|
149
|
+
aliases,
|
|
150
|
+
propAliasName,
|
|
151
|
+
options,
|
|
152
|
+
)});`;
|
|
153
|
+
}
|
|
154
|
+
})
|
|
155
|
+
.join('\n');
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export function generateValidateMethods(
|
|
159
|
+
nativeModule: NativeModuleSchema,
|
|
160
|
+
aliases: AliasMap,
|
|
161
|
+
options: CppCodegenOptions,
|
|
162
|
+
): [string, string] {
|
|
163
|
+
const properties = nativeModule.spec.properties;
|
|
164
|
+
const traversedProperties = renderProperties(
|
|
165
|
+
properties,
|
|
166
|
+
aliases,
|
|
167
|
+
false,
|
|
168
|
+
options,
|
|
169
|
+
);
|
|
170
|
+
const traversedPropertyTuples = renderProperties(
|
|
171
|
+
properties,
|
|
172
|
+
aliases,
|
|
173
|
+
true,
|
|
174
|
+
options,
|
|
175
|
+
);
|
|
176
|
+
return [traversedPropertyTuples, traversedProperties];
|
|
177
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Microsoft Corporation.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*
|
|
5
|
+
* @format
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import fs from '@react-native-windows/fs';
|
|
10
|
+
import globby from 'globby';
|
|
11
|
+
import type {CppStringTypes} from './generators/GenerateNM2';
|
|
12
|
+
import {createNM2Generator} from './generators/GenerateNM2';
|
|
13
|
+
import {
|
|
14
|
+
generateTypeScript,
|
|
15
|
+
setOptionalTurboModule,
|
|
16
|
+
} from './generators/GenerateTypeScript';
|
|
17
|
+
import type {SchemaType} from '@react-native/codegen/lib/CodegenSchema';
|
|
18
|
+
import type {Parser} from '@react-native/codegen/lib/parsers/parser';
|
|
19
|
+
|
|
20
|
+
export type {CppStringTypes} from './generators/GenerateNM2';
|
|
21
|
+
|
|
22
|
+
// Load @react-native/codegen from react-native
|
|
23
|
+
const rnPath = path.dirname(require.resolve('react-native/package.json'));
|
|
24
|
+
const rncodegenPath = path.dirname(
|
|
25
|
+
require.resolve('@react-native/codegen/package.json', {paths: [rnPath]}),
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
function getParser(isTypeScript: boolean): Parser {
|
|
29
|
+
if (isTypeScript) {
|
|
30
|
+
const fp = require(path.resolve(
|
|
31
|
+
rncodegenPath,
|
|
32
|
+
'lib/parsers/typescript/parser',
|
|
33
|
+
));
|
|
34
|
+
return new fp.TypeScriptParser();
|
|
35
|
+
} else {
|
|
36
|
+
const fp = require(path.resolve(rncodegenPath, 'lib/parsers/flow/parser'));
|
|
37
|
+
return new fp.FlowParser();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const schemaValidator = require(path.resolve(
|
|
42
|
+
rncodegenPath,
|
|
43
|
+
'lib/SchemaValidator',
|
|
44
|
+
));
|
|
45
|
+
|
|
46
|
+
export interface SharedOptions {
|
|
47
|
+
libraryName: string;
|
|
48
|
+
methodOnly: boolean;
|
|
49
|
+
modulesCxx: boolean;
|
|
50
|
+
modulesTypeScriptTypes: boolean;
|
|
51
|
+
modulesWindows: boolean;
|
|
52
|
+
namespace: string;
|
|
53
|
+
outputDirectory: string;
|
|
54
|
+
cppStringType: CppStringTypes;
|
|
55
|
+
separateDataTypes: boolean;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
interface Options extends SharedOptions {
|
|
59
|
+
moduleSpecName: string;
|
|
60
|
+
schema: SchemaType;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
interface Config {
|
|
64
|
+
generators: any[] /*Generators[]*/;
|
|
65
|
+
test?: boolean;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function normalizeFileMap(
|
|
69
|
+
map: Map<string, string>,
|
|
70
|
+
outputDir: string,
|
|
71
|
+
outMap: Map<string, string>,
|
|
72
|
+
): void {
|
|
73
|
+
for (const [fileName, contents] of map) {
|
|
74
|
+
const location = path.join(outputDir, fileName);
|
|
75
|
+
outMap.set(path.normalize(location), contents);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function checkFilesForChanges(
|
|
80
|
+
map: Map<string, string>,
|
|
81
|
+
outputDir: string,
|
|
82
|
+
): boolean {
|
|
83
|
+
let hasChanges = false;
|
|
84
|
+
|
|
85
|
+
outputDir = path.resolve(outputDir);
|
|
86
|
+
const globbyDir = outputDir.replace(/\\/g, '/');
|
|
87
|
+
const allExistingFiles = globby
|
|
88
|
+
.sync([`${globbyDir}/**`, `${globbyDir}/**/.*`], {absolute: true})
|
|
89
|
+
.map(_ => path.normalize(_));
|
|
90
|
+
const allGeneratedFiles = [...map.keys()].map(_ => path.normalize(_)).sort();
|
|
91
|
+
|
|
92
|
+
if (
|
|
93
|
+
allExistingFiles.length !== allGeneratedFiles.length ||
|
|
94
|
+
!allGeneratedFiles.every(filepath =>
|
|
95
|
+
allExistingFiles.includes(
|
|
96
|
+
path.normalize(path.resolve(process.cwd(), filepath)),
|
|
97
|
+
),
|
|
98
|
+
)
|
|
99
|
+
)
|
|
100
|
+
return true;
|
|
101
|
+
|
|
102
|
+
for (const [fileName, contents] of map) {
|
|
103
|
+
if (!fs.existsSync(fileName)) {
|
|
104
|
+
hasChanges = true;
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const currentContents = fs.readFileSync(fileName, 'utf8');
|
|
109
|
+
if (currentContents !== contents) {
|
|
110
|
+
console.log(`- ${fileName} has changed`);
|
|
111
|
+
hasChanges = true;
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return hasChanges;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function writeMapToFiles(map: Map<string, string>, outputDir: string) {
|
|
120
|
+
let success = true;
|
|
121
|
+
|
|
122
|
+
outputDir = path.resolve(outputDir);
|
|
123
|
+
const globbyDir = outputDir.replace(/\\/g, '/');
|
|
124
|
+
|
|
125
|
+
// This ensures that we delete any generated files from modules that have been deleted
|
|
126
|
+
const allExistingFiles = globby.sync(
|
|
127
|
+
[`${globbyDir}/**`, `${globbyDir}/**/.*`],
|
|
128
|
+
{absolute: true},
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
const allGeneratedFiles = [...map.keys()].map(_ => path.normalize(_)).sort();
|
|
132
|
+
allExistingFiles.forEach(existingFile => {
|
|
133
|
+
if (!allGeneratedFiles.includes(path.normalize(existingFile))) {
|
|
134
|
+
console.log('Deleting ', existingFile);
|
|
135
|
+
fs.unlinkSync(existingFile);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
for (const [fileName, contents] of map) {
|
|
140
|
+
try {
|
|
141
|
+
fs.mkdirSync(path.dirname(fileName), {recursive: true});
|
|
142
|
+
|
|
143
|
+
if (fs.existsSync(fileName)) {
|
|
144
|
+
const currentContents = fs.readFileSync(fileName, 'utf8');
|
|
145
|
+
// Don't update the files if there are no changes as this breaks incremental builds
|
|
146
|
+
if (currentContents === contents) {
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
console.log('Writing ', fileName);
|
|
152
|
+
fs.writeFileSync(fileName, contents);
|
|
153
|
+
} catch (error) {
|
|
154
|
+
success = false;
|
|
155
|
+
console.error(`Failed to write ${fileName} to ${fileName}`, error);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return success;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function parseFile(filename: string): SchemaType {
|
|
163
|
+
try {
|
|
164
|
+
const isTypeScript =
|
|
165
|
+
path.extname(filename) === '.ts' || path.extname(filename) === '.tsx';
|
|
166
|
+
const contents = fs.readFileSync(filename, 'utf8');
|
|
167
|
+
const schema = getParser(isTypeScript).parseString(contents, filename);
|
|
168
|
+
// there will be at most one turbo module per file
|
|
169
|
+
const moduleName = Object.keys(schema.modules)[0];
|
|
170
|
+
if (moduleName) {
|
|
171
|
+
const spec = schema.modules[moduleName];
|
|
172
|
+
if (spec.type === 'NativeModule') {
|
|
173
|
+
if (contents) {
|
|
174
|
+
// This is a temporary implementation until such information is added to the schema in facebook/react-native
|
|
175
|
+
if (contents.includes('TurboModuleRegistry.get<')) {
|
|
176
|
+
setOptionalTurboModule(spec, true);
|
|
177
|
+
} else if (contents.includes('TurboModuleRegistry.getEnforcing<')) {
|
|
178
|
+
setOptionalTurboModule(spec, false);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return schema;
|
|
184
|
+
} catch (e) {
|
|
185
|
+
if (e instanceof Error) {
|
|
186
|
+
e.message = `(${filename}): ${e.message}`;
|
|
187
|
+
}
|
|
188
|
+
throw e;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export function combineSchemas(files: string[]): SchemaType {
|
|
193
|
+
return files.reduce(
|
|
194
|
+
(merged, filename) => {
|
|
195
|
+
const contents = fs.readFileSync(filename, 'utf8');
|
|
196
|
+
if (
|
|
197
|
+
contents &&
|
|
198
|
+
(/export\s+default\s+\(?codegenNativeComponent</.test(contents) ||
|
|
199
|
+
contents.includes('extends TurboModule'))
|
|
200
|
+
) {
|
|
201
|
+
const schema = parseFile(filename);
|
|
202
|
+
merged.modules = {...merged.modules, ...schema.modules};
|
|
203
|
+
}
|
|
204
|
+
return merged;
|
|
205
|
+
},
|
|
206
|
+
{modules: {}},
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export function generate(
|
|
211
|
+
{
|
|
212
|
+
libraryName,
|
|
213
|
+
methodOnly,
|
|
214
|
+
modulesCxx,
|
|
215
|
+
modulesTypeScriptTypes,
|
|
216
|
+
modulesWindows,
|
|
217
|
+
namespace,
|
|
218
|
+
outputDirectory,
|
|
219
|
+
cppStringType,
|
|
220
|
+
separateDataTypes,
|
|
221
|
+
moduleSpecName,
|
|
222
|
+
schema,
|
|
223
|
+
}: Options,
|
|
224
|
+
{/*generators,*/ test}: Config,
|
|
225
|
+
): boolean {
|
|
226
|
+
schemaValidator.validate(schema);
|
|
227
|
+
|
|
228
|
+
const componentOutputdir = path.join(
|
|
229
|
+
outputDirectory,
|
|
230
|
+
'react/components',
|
|
231
|
+
libraryName,
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
const generatedFiles = new Map<string, string>();
|
|
235
|
+
|
|
236
|
+
generatedFiles.set(
|
|
237
|
+
path.join(outputDirectory, '.clang-format'),
|
|
238
|
+
'DisableFormat: true\nSortIncludes: false',
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
const generateNM2 = createNM2Generator({
|
|
242
|
+
methodOnly,
|
|
243
|
+
namespace,
|
|
244
|
+
cppStringType,
|
|
245
|
+
separateDataTypes,
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
const generateJsiModuleH = require(path.resolve(
|
|
249
|
+
rncodegenPath,
|
|
250
|
+
'lib/generators/modules/GenerateModuleH',
|
|
251
|
+
)).generate;
|
|
252
|
+
const generateJsiModuleCpp = require(path.resolve(
|
|
253
|
+
rncodegenPath,
|
|
254
|
+
'lib/generators/modules/GenerateModuleCpp',
|
|
255
|
+
)).generate;
|
|
256
|
+
const generatorPropsH = require(path.resolve(
|
|
257
|
+
rncodegenPath,
|
|
258
|
+
'lib/generators/components/GeneratePropsH',
|
|
259
|
+
)).generate;
|
|
260
|
+
const generatorPropsCPP = require(path.resolve(
|
|
261
|
+
rncodegenPath,
|
|
262
|
+
'lib/generators/components/GeneratePropsCpp',
|
|
263
|
+
)).generate;
|
|
264
|
+
const generatorShadowNodeH = require(path.resolve(
|
|
265
|
+
rncodegenPath,
|
|
266
|
+
'lib/generators/components/GenerateShadowNodeH',
|
|
267
|
+
)).generate;
|
|
268
|
+
const generatorShadowNodeCPP = require(path.resolve(
|
|
269
|
+
rncodegenPath,
|
|
270
|
+
'lib/generators/components/GenerateShadowNodeCpp',
|
|
271
|
+
)).generate;
|
|
272
|
+
const generatorComponentDescriptorH = require(path.resolve(
|
|
273
|
+
rncodegenPath,
|
|
274
|
+
'lib/generators/components/GenerateComponentDescriptorH',
|
|
275
|
+
)).generate;
|
|
276
|
+
const generatorEventEmitterH = require(path.resolve(
|
|
277
|
+
rncodegenPath,
|
|
278
|
+
'lib/generators/components/GenerateEventEmitterH',
|
|
279
|
+
)).generate;
|
|
280
|
+
const generatorEventEmitterCPP = require(path.resolve(
|
|
281
|
+
rncodegenPath,
|
|
282
|
+
'lib/generators/components/GenerateEventEmitterCpp',
|
|
283
|
+
)).generate;
|
|
284
|
+
const generatorStateCPP = require(path.resolve(
|
|
285
|
+
rncodegenPath,
|
|
286
|
+
'lib/generators/components/GenerateStateCpp',
|
|
287
|
+
)).generate;
|
|
288
|
+
const generatorStateH = require(path.resolve(
|
|
289
|
+
rncodegenPath,
|
|
290
|
+
'lib/generators/components/GenerateStateH',
|
|
291
|
+
)).generate;
|
|
292
|
+
|
|
293
|
+
const moduleGenerators = [];
|
|
294
|
+
|
|
295
|
+
if (modulesWindows) {
|
|
296
|
+
moduleGenerators.push(generateNM2);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (modulesCxx) {
|
|
300
|
+
moduleGenerators.push(generateJsiModuleH);
|
|
301
|
+
moduleGenerators.push(generateJsiModuleCpp);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (modulesTypeScriptTypes) {
|
|
305
|
+
moduleGenerators.push(generateTypeScript);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
moduleGenerators.forEach(generator => {
|
|
309
|
+
const generated: Map<string, string> = generator(
|
|
310
|
+
libraryName,
|
|
311
|
+
schema,
|
|
312
|
+
moduleSpecName,
|
|
313
|
+
);
|
|
314
|
+
normalizeFileMap(generated, outputDirectory, generatedFiles);
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
if (
|
|
318
|
+
Object.keys(schema.modules).some(
|
|
319
|
+
moduleName => schema.modules[moduleName].type === 'Component',
|
|
320
|
+
)
|
|
321
|
+
) {
|
|
322
|
+
const componentGenerators = [
|
|
323
|
+
generatorComponentDescriptorH,
|
|
324
|
+
generatorEventEmitterCPP,
|
|
325
|
+
generatorEventEmitterH,
|
|
326
|
+
generatorPropsCPP,
|
|
327
|
+
generatorPropsH,
|
|
328
|
+
generatorShadowNodeCPP,
|
|
329
|
+
generatorShadowNodeH,
|
|
330
|
+
generatorStateCPP,
|
|
331
|
+
generatorStateH,
|
|
332
|
+
];
|
|
333
|
+
|
|
334
|
+
componentGenerators.forEach(generator => {
|
|
335
|
+
const generated: Map<string, string> = generator(
|
|
336
|
+
libraryName,
|
|
337
|
+
schema,
|
|
338
|
+
moduleSpecName,
|
|
339
|
+
);
|
|
340
|
+
normalizeFileMap(generated, componentOutputdir, generatedFiles);
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
if (test === true) {
|
|
345
|
+
return checkFilesForChanges(generatedFiles, outputDirectory);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
return writeMapToFiles(generatedFiles, outputDirectory);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
export interface CodeGenOptions extends SharedOptions {
|
|
352
|
+
file?: string;
|
|
353
|
+
files?: string[];
|
|
354
|
+
test: boolean;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
export function runCodeGen(options: CodeGenOptions): boolean {
|
|
358
|
+
if (!options.file && !options.files)
|
|
359
|
+
throw new Error('Must specify file or files option');
|
|
360
|
+
|
|
361
|
+
const schema = options.file
|
|
362
|
+
? parseFile(options.file)
|
|
363
|
+
: combineSchemas(globby.sync(options.files!));
|
|
364
|
+
|
|
365
|
+
const libraryName = options.libraryName;
|
|
366
|
+
const moduleSpecName = 'moduleSpecName';
|
|
367
|
+
const {
|
|
368
|
+
methodOnly,
|
|
369
|
+
modulesCxx,
|
|
370
|
+
modulesTypeScriptTypes,
|
|
371
|
+
modulesWindows,
|
|
372
|
+
namespace,
|
|
373
|
+
outputDirectory,
|
|
374
|
+
cppStringType,
|
|
375
|
+
separateDataTypes,
|
|
376
|
+
} = options;
|
|
377
|
+
return generate(
|
|
378
|
+
{
|
|
379
|
+
libraryName,
|
|
380
|
+
methodOnly,
|
|
381
|
+
modulesCxx,
|
|
382
|
+
modulesTypeScriptTypes,
|
|
383
|
+
modulesWindows,
|
|
384
|
+
namespace,
|
|
385
|
+
outputDirectory,
|
|
386
|
+
cppStringType,
|
|
387
|
+
separateDataTypes,
|
|
388
|
+
moduleSpecName,
|
|
389
|
+
schema,
|
|
390
|
+
},
|
|
391
|
+
{generators: [], test: options.test},
|
|
392
|
+
);
|
|
393
|
+
}
|
package/.eslintrc.js
DELETED