@callstack/brownfield-cli 3.8.0 → 3.8.1
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 +6 -0
- package/dist/navigation/generators/android.d.ts +6 -2
- package/dist/navigation/generators/android.d.ts.map +1 -1
- package/dist/navigation/generators/android.js +36 -17
- package/dist/navigation/generators/ios.d.ts +11 -2
- package/dist/navigation/generators/ios.d.ts.map +1 -1
- package/dist/navigation/generators/ios.js +41 -18
- package/dist/navigation/generators/models.d.ts +3 -2
- package/dist/navigation/generators/models.d.ts.map +1 -1
- package/dist/navigation/generators/models.js +223 -3
- package/dist/navigation/generators/ts.d.ts +8 -4
- package/dist/navigation/generators/ts.d.ts.map +1 -1
- package/dist/navigation/generators/ts.js +30 -7
- package/dist/navigation/parser.d.ts +2 -2
- package/dist/navigation/parser.d.ts.map +1 -1
- package/dist/navigation/parser.js +110 -1
- package/dist/navigation/runner.d.ts.map +1 -1
- package/dist/navigation/runner.js +19 -8
- package/dist/navigation/types.d.ts +18 -0
- package/dist/navigation/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/navigation/generators/android.ts +67 -17
- package/src/navigation/generators/ios.ts +75 -18
- package/src/navigation/generators/models.ts +319 -3
- package/src/navigation/generators/ts.ts +59 -8
- package/src/navigation/parser.ts +137 -3
- package/src/navigation/runner.ts +20 -8
- package/src/navigation/types.ts +22 -0
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export declare function parseNavigationSpec(specPath: string):
|
|
1
|
+
import type { ParsedNavigationSpec } from './types.js';
|
|
2
|
+
export declare function parseNavigationSpec(specPath: string): ParsedNavigationSpec;
|
|
3
3
|
//# sourceMappingURL=parser.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/navigation/parser.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/navigation/parser.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAKV,oBAAoB,EAErB,MAAM,YAAY,CAAC;AAkCpB,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,oBAAoB,CA4G1E"}
|
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import { Project } from 'ts-morph';
|
|
3
|
+
import { Node } from 'ts-morph';
|
|
4
|
+
const SKIP_TYPE_TOKENS = new Set([
|
|
5
|
+
'Array',
|
|
6
|
+
'Date',
|
|
7
|
+
'Map',
|
|
8
|
+
'Object',
|
|
9
|
+
'Promise',
|
|
10
|
+
'ReadonlyArray',
|
|
11
|
+
'Record',
|
|
12
|
+
'Set',
|
|
13
|
+
'any',
|
|
14
|
+
'boolean',
|
|
15
|
+
'false',
|
|
16
|
+
'null',
|
|
17
|
+
'number',
|
|
18
|
+
'object',
|
|
19
|
+
'string',
|
|
20
|
+
'true',
|
|
21
|
+
'undefined',
|
|
22
|
+
'unknown',
|
|
23
|
+
'void',
|
|
24
|
+
]);
|
|
25
|
+
function collectReferencedTypesFromText(typeText) {
|
|
26
|
+
const matches = typeText.match(/\b[A-Za-z_]\w*\b/g);
|
|
27
|
+
if (!matches) {
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
return matches.filter((match) => !SKIP_TYPE_TOKENS.has(match));
|
|
31
|
+
}
|
|
3
32
|
export function parseNavigationSpec(specPath) {
|
|
4
33
|
if (!fs.existsSync(specPath)) {
|
|
5
34
|
throw new Error(`Spec file not found: ${specPath}`);
|
|
@@ -11,7 +40,7 @@ export function parseNavigationSpec(specPath) {
|
|
|
11
40
|
if (!specInterface) {
|
|
12
41
|
throw new Error('Could not find BrownfieldNavigationSpec or Spec interface in spec file');
|
|
13
42
|
}
|
|
14
|
-
|
|
43
|
+
const methods = specInterface.getMethods().map((method) => {
|
|
15
44
|
const name = method.getName();
|
|
16
45
|
const params = method.getParameters().map((param) => {
|
|
17
46
|
const typeNode = param.getTypeNode();
|
|
@@ -30,4 +59,84 @@ export function parseNavigationSpec(specPath) {
|
|
|
30
59
|
isAsync: returnType.startsWith('Promise<'),
|
|
31
60
|
};
|
|
32
61
|
});
|
|
62
|
+
const declarationNodes = [
|
|
63
|
+
...sourceFile.getTypeAliases(),
|
|
64
|
+
...sourceFile.getInterfaces(),
|
|
65
|
+
].filter((node) => {
|
|
66
|
+
const nodeName = node.getName();
|
|
67
|
+
return nodeName !== 'BrownfieldNavigationSpec' && nodeName !== 'Spec';
|
|
68
|
+
});
|
|
69
|
+
const declarationByName = new Map(declarationNodes.map((node) => {
|
|
70
|
+
const declarationText = node.getText();
|
|
71
|
+
const normalizedDeclaration = node.isExported()
|
|
72
|
+
? declarationText
|
|
73
|
+
: `export ${declarationText}`;
|
|
74
|
+
return [node.getName(), normalizedDeclaration];
|
|
75
|
+
}));
|
|
76
|
+
const referencedTypeNames = new Set();
|
|
77
|
+
for (const method of methods) {
|
|
78
|
+
for (const typeText of [
|
|
79
|
+
method.returnType,
|
|
80
|
+
...method.params.map((param) => param.type),
|
|
81
|
+
]) {
|
|
82
|
+
for (const typeName of collectReferencedTypesFromText(typeText)) {
|
|
83
|
+
referencedTypeNames.add(typeName);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const referencedTypeDeclarations = [];
|
|
88
|
+
const modelDefinitions = [];
|
|
89
|
+
const addedTypeNames = new Set();
|
|
90
|
+
const queue = [...referencedTypeNames];
|
|
91
|
+
while (queue.length > 0) {
|
|
92
|
+
const typeName = queue.shift();
|
|
93
|
+
if (!typeName || addedTypeNames.has(typeName)) {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
const declaration = declarationByName.get(typeName);
|
|
97
|
+
if (!declaration) {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
addedTypeNames.add(typeName);
|
|
101
|
+
referencedTypeDeclarations.push({ name: typeName, declaration });
|
|
102
|
+
const declarationNode = declarationNodes.find((node) => node.getName() === typeName);
|
|
103
|
+
if (declarationNode) {
|
|
104
|
+
const fields = extractModelFields(declarationNode);
|
|
105
|
+
if (fields.length > 0) {
|
|
106
|
+
modelDefinitions.push({ name: typeName, fields });
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
for (const nestedTypeName of collectReferencedTypesFromText(declaration)) {
|
|
110
|
+
if (!addedTypeNames.has(nestedTypeName) &&
|
|
111
|
+
declarationByName.has(nestedTypeName)) {
|
|
112
|
+
queue.push(nestedTypeName);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
methods,
|
|
118
|
+
referencedTypeDeclarations,
|
|
119
|
+
modelDefinitions,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function extractModelFields(node) {
|
|
123
|
+
if (Node.isInterfaceDeclaration(node)) {
|
|
124
|
+
return node.getProperties().map((property) => ({
|
|
125
|
+
name: property.getName(),
|
|
126
|
+
type: property.getTypeNode()?.getText() ?? 'unknown',
|
|
127
|
+
optional: property.hasQuestionToken(),
|
|
128
|
+
}));
|
|
129
|
+
}
|
|
130
|
+
if (Node.isTypeAliasDeclaration(node)) {
|
|
131
|
+
const typeNode = node.getTypeNode();
|
|
132
|
+
if (!typeNode || !Node.isTypeLiteral(typeNode)) {
|
|
133
|
+
return [];
|
|
134
|
+
}
|
|
135
|
+
return typeNode.getProperties().map((property) => ({
|
|
136
|
+
name: property.getName(),
|
|
137
|
+
type: property.getTypeNode()?.getText() ?? 'unknown',
|
|
138
|
+
optional: property.hasQuestionToken(),
|
|
139
|
+
}));
|
|
140
|
+
}
|
|
141
|
+
return [];
|
|
33
142
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/navigation/runner.ts"],"names":[],"mappings":"AA2BA,UAAU,2BAA2B;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAgLD,wBAAsB,oBAAoB,CAAC,EACzC,QAAQ,EACR,MAAc,EACd,WAA2B,GAC5B,EAAE,2BAA2B,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/navigation/runner.ts"],"names":[],"mappings":"AA2BA,UAAU,2BAA2B;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAgLD,wBAAsB,oBAAoB,CAAC,EACzC,QAAQ,EACR,MAAc,EACd,WAA2B,GAC5B,EAAE,2BAA2B,GAAG,OAAO,CAAC,IAAI,CAAC,CA0E7C"}
|
|
@@ -88,28 +88,39 @@ export async function runNavigationCodegen({ specPath, dryRun = false, projectRo
|
|
|
88
88
|
}
|
|
89
89
|
intro(`Running Brownfield Navigation codegen`);
|
|
90
90
|
logger.info(`Parsing spec file: ${resolvedSpecPath}`);
|
|
91
|
-
const methods = parseNavigationSpec(resolvedSpecPath);
|
|
91
|
+
const { methods, referencedTypeDeclarations, modelDefinitions } = parseNavigationSpec(resolvedSpecPath);
|
|
92
92
|
if (methods.length === 0) {
|
|
93
93
|
throw new Error('No methods found in spec file');
|
|
94
94
|
}
|
|
95
95
|
logger.info(`Found ${methods.length} method${methods.length === 1 ? '' : 's'}: ${methods.map((method) => method.name).join(', ')}`);
|
|
96
96
|
const packageRoot = getNavigationPackagePath(projectRoot);
|
|
97
97
|
const androidJavaPackageName = DEFAULT_ANDROID_JAVA_PACKAGE;
|
|
98
|
-
const indexTs = generateIndexTs(methods);
|
|
98
|
+
const indexTs = generateIndexTs(methods, referencedTypeDeclarations);
|
|
99
99
|
const models = await generateNavigationModels({
|
|
100
100
|
specPath: resolvedSpecPath,
|
|
101
101
|
methods,
|
|
102
102
|
kotlinPackageName: androidJavaPackageName,
|
|
103
|
+
modelDefinitions,
|
|
103
104
|
});
|
|
104
105
|
const artifacts = {
|
|
105
|
-
turboModuleSpec: generateTurboModuleSpec(methods
|
|
106
|
+
turboModuleSpec: generateTurboModuleSpec(methods, referencedTypeDeclarations, {
|
|
107
|
+
modelTypeNames: models.modelTypeNames,
|
|
108
|
+
}),
|
|
106
109
|
indexTs,
|
|
107
110
|
indexJs: transpileWithConsumerBabel(indexTs, projectRoot, packageRoot),
|
|
108
|
-
indexDts: generateIndexDts(methods),
|
|
109
|
-
swiftDelegate: generateSwiftDelegate(methods
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
111
|
+
indexDts: generateIndexDts(methods, referencedTypeDeclarations),
|
|
112
|
+
swiftDelegate: generateSwiftDelegate(methods, {
|
|
113
|
+
modelTypeNames: models.modelTypeNames,
|
|
114
|
+
}),
|
|
115
|
+
objcImplementation: generateObjCImplementation(methods, {
|
|
116
|
+
modelTypeNames: models.modelTypeNames,
|
|
117
|
+
}),
|
|
118
|
+
kotlinDelegate: generateKotlinDelegate(methods, androidJavaPackageName, {
|
|
119
|
+
modelTypeNames: models.modelTypeNames,
|
|
120
|
+
}),
|
|
121
|
+
kotlinModule: generateKotlinModule(methods, androidJavaPackageName, {
|
|
122
|
+
modelTypeNames: models.modelTypeNames,
|
|
123
|
+
}),
|
|
113
124
|
};
|
|
114
125
|
if (models.modelTypeNames.length > 0) {
|
|
115
126
|
logger.info(`Generating quicktype models for types: ${models.modelTypeNames.join(', ')}`);
|
|
@@ -9,6 +9,24 @@ export interface MethodSignature {
|
|
|
9
9
|
returnType: string;
|
|
10
10
|
isAsync: boolean;
|
|
11
11
|
}
|
|
12
|
+
export interface TypeDeclaration {
|
|
13
|
+
name: string;
|
|
14
|
+
declaration: string;
|
|
15
|
+
}
|
|
16
|
+
export interface ModelFieldDefinition {
|
|
17
|
+
name: string;
|
|
18
|
+
type: string;
|
|
19
|
+
optional: boolean;
|
|
20
|
+
}
|
|
21
|
+
export interface ModelDefinition {
|
|
22
|
+
name: string;
|
|
23
|
+
fields: ModelFieldDefinition[];
|
|
24
|
+
}
|
|
25
|
+
export interface ParsedNavigationSpec {
|
|
26
|
+
methods: MethodSignature[];
|
|
27
|
+
referencedTypeDeclarations: TypeDeclaration[];
|
|
28
|
+
modelDefinitions: ModelDefinition[];
|
|
29
|
+
}
|
|
12
30
|
export interface GeneratedNavigationArtifacts {
|
|
13
31
|
turboModuleSpec: string;
|
|
14
32
|
indexTs: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/navigation/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,4BAA4B;IAC3C,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/navigation/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,oBAAoB,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,0BAA0B,EAAE,eAAe,EAAE,CAAC;IAC9C,gBAAgB,EAAE,eAAe,EAAE,CAAC;CACrC;AAED,MAAM,WAAW,4BAA4B;IAC3C,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB"}
|
package/package.json
CHANGED
|
@@ -8,10 +8,19 @@ const TS_TO_KOTLIN_TYPE: Record<string, string> = {
|
|
|
8
8
|
Object: 'ReadableMap',
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
interface KotlinTypeMappingOptions {
|
|
12
|
+
modelTypeNames?: string[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function mapTsTypeToKotlin(
|
|
16
|
+
tsType: string,
|
|
17
|
+
optional: boolean = false,
|
|
18
|
+
options: KotlinTypeMappingOptions = {},
|
|
19
|
+
layer: 'delegate' | 'module' = 'delegate'
|
|
20
|
+
): string {
|
|
12
21
|
if (tsType.startsWith('Promise<')) {
|
|
13
22
|
const inner = tsType.slice(8, -1);
|
|
14
|
-
return mapTsTypeToKotlin(inner, optional);
|
|
23
|
+
return mapTsTypeToKotlin(inner, optional, options, layer);
|
|
15
24
|
}
|
|
16
25
|
|
|
17
26
|
const mapped = TS_TO_KOTLIN_TYPE[tsType];
|
|
@@ -19,25 +28,33 @@ function mapTsTypeToKotlin(tsType: string, optional: boolean = false): string {
|
|
|
19
28
|
return optional ? `${mapped}?` : mapped;
|
|
20
29
|
}
|
|
21
30
|
|
|
31
|
+
if (options.modelTypeNames?.includes(tsType)) {
|
|
32
|
+
if (layer === 'module') {
|
|
33
|
+
return optional ? 'ReadableMap?' : 'ReadableMap';
|
|
34
|
+
}
|
|
35
|
+
return optional ? `${tsType}?` : tsType;
|
|
36
|
+
}
|
|
37
|
+
|
|
22
38
|
return optional ? 'Any?' : 'Any';
|
|
23
39
|
}
|
|
24
40
|
|
|
25
41
|
export function generateKotlinDelegate(
|
|
26
42
|
methods: MethodSignature[],
|
|
27
|
-
kotlinPackageName: string
|
|
43
|
+
kotlinPackageName: string,
|
|
44
|
+
options: KotlinTypeMappingOptions = {}
|
|
28
45
|
): string {
|
|
29
46
|
const methodSignatures = methods
|
|
30
47
|
.map((method) => {
|
|
31
48
|
const params = method.params
|
|
32
49
|
.map(
|
|
33
50
|
(param) =>
|
|
34
|
-
`${param.name}: ${mapTsTypeToKotlin(param.type, param.optional)}`
|
|
51
|
+
`${param.name}: ${mapTsTypeToKotlin(param.type, param.optional, options)}`
|
|
35
52
|
)
|
|
36
53
|
.join(', ');
|
|
37
54
|
const returnType =
|
|
38
55
|
method.returnType === 'void'
|
|
39
56
|
? ''
|
|
40
|
-
: `: ${mapTsTypeToKotlin(method.returnType, false)}`;
|
|
57
|
+
: `: ${mapTsTypeToKotlin(method.returnType, false, options, 'delegate')}`;
|
|
41
58
|
return ` fun ${method.name}(${params})${returnType}`;
|
|
42
59
|
})
|
|
43
60
|
.join('\n');
|
|
@@ -52,20 +69,24 @@ ${methodSignatures}
|
|
|
52
69
|
|
|
53
70
|
export function generateKotlinModule(
|
|
54
71
|
methods: MethodSignature[],
|
|
55
|
-
kotlinPackageName: string
|
|
72
|
+
kotlinPackageName: string,
|
|
73
|
+
options: KotlinTypeMappingOptions = {}
|
|
56
74
|
): string {
|
|
57
75
|
const hasAsyncMethod = methods.some((method) => method.isAsync);
|
|
58
76
|
const hasObjectType = methods.some(
|
|
59
77
|
(method) =>
|
|
60
78
|
method.returnType.includes('Object') ||
|
|
61
|
-
method.params.some(
|
|
79
|
+
method.params.some(
|
|
80
|
+
(param) =>
|
|
81
|
+
param.type === 'Object' || options.modelTypeNames?.includes(param.type)
|
|
82
|
+
)
|
|
62
83
|
);
|
|
63
84
|
|
|
64
85
|
const methodImplementations = methods
|
|
65
86
|
.map((method) =>
|
|
66
87
|
method.isAsync
|
|
67
|
-
? generateAsyncKotlinMethod(method)
|
|
68
|
-
: generateSyncKotlinMethod(method)
|
|
88
|
+
? generateAsyncKotlinMethod(method, options)
|
|
89
|
+
: generateSyncKotlinMethod(method, options)
|
|
69
90
|
)
|
|
70
91
|
.join('\n\n');
|
|
71
92
|
|
|
@@ -88,32 +109,61 @@ ${methodImplementations}
|
|
|
88
109
|
`;
|
|
89
110
|
}
|
|
90
111
|
|
|
91
|
-
function generateSyncKotlinMethod(
|
|
112
|
+
function generateSyncKotlinMethod(
|
|
113
|
+
method: MethodSignature,
|
|
114
|
+
options: KotlinTypeMappingOptions = {}
|
|
115
|
+
): string {
|
|
92
116
|
const params = method.params
|
|
93
|
-
.map(
|
|
117
|
+
.map(
|
|
118
|
+
(param) =>
|
|
119
|
+
`${param.name}: ${mapTsTypeToKotlin(param.type, param.optional, options, 'module')}`
|
|
120
|
+
)
|
|
121
|
+
.join(', ');
|
|
122
|
+
const preparedParams: string[] = [];
|
|
123
|
+
const args = method.params
|
|
124
|
+
.map((param) => {
|
|
125
|
+
if (options.modelTypeNames?.includes(param.type)) {
|
|
126
|
+
const convertedName = `${param.name}Model`;
|
|
127
|
+
preparedParams.push(
|
|
128
|
+
` val ${convertedName} = ${param.name}${
|
|
129
|
+
param.optional
|
|
130
|
+
? `?.let(::to${param.type})`
|
|
131
|
+
: `.let(::to${param.type})`
|
|
132
|
+
}`
|
|
133
|
+
);
|
|
134
|
+
return convertedName;
|
|
135
|
+
}
|
|
136
|
+
return param.name;
|
|
137
|
+
})
|
|
94
138
|
.join(', ');
|
|
95
|
-
const args = method.params.map((param) => param.name).join(', ');
|
|
96
139
|
|
|
97
140
|
const signature = ` @ReactMethod\n override fun ${method.name}(${params})${
|
|
98
141
|
method.returnType === 'void'
|
|
99
142
|
? ''
|
|
100
|
-
: `: ${mapTsTypeToKotlin(method.returnType, false)}`
|
|
143
|
+
: `: ${mapTsTypeToKotlin(method.returnType, false, options, 'module')}`
|
|
101
144
|
}`;
|
|
145
|
+
const preparedPrefix = preparedParams.length > 0 ? `${preparedParams.join('\n')}\n` : '';
|
|
102
146
|
|
|
103
147
|
if (method.returnType === 'void') {
|
|
104
148
|
return `${signature} {
|
|
105
|
-
BrownfieldNavigationManager.getDelegate().${method.name}(${args})
|
|
149
|
+
${preparedPrefix} BrownfieldNavigationManager.getDelegate().${method.name}(${args})
|
|
106
150
|
}`;
|
|
107
151
|
}
|
|
108
152
|
|
|
109
153
|
return `${signature} {
|
|
110
|
-
return BrownfieldNavigationManager.getDelegate().${method.name}(${args})
|
|
154
|
+
${preparedPrefix} return BrownfieldNavigationManager.getDelegate().${method.name}(${args})
|
|
111
155
|
}`;
|
|
112
156
|
}
|
|
113
157
|
|
|
114
|
-
function generateAsyncKotlinMethod(
|
|
158
|
+
function generateAsyncKotlinMethod(
|
|
159
|
+
method: MethodSignature,
|
|
160
|
+
options: KotlinTypeMappingOptions = {}
|
|
161
|
+
): string {
|
|
115
162
|
const paramsWithTypes = method.params
|
|
116
|
-
.map(
|
|
163
|
+
.map(
|
|
164
|
+
(param) =>
|
|
165
|
+
`${param.name}: ${mapTsTypeToKotlin(param.type, param.optional, options, 'module')}`
|
|
166
|
+
)
|
|
117
167
|
.join(', ');
|
|
118
168
|
const params =
|
|
119
169
|
paramsWithTypes.length > 0
|
|
@@ -16,11 +16,23 @@ const TS_TO_SWIFT_TYPE: Record<string, string> = {
|
|
|
16
16
|
Object: '[String: Any]',
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
interface ObjCTypeMappingOptions {
|
|
20
|
+
modelTypeNames?: string[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function mapTsTypeToObjC(
|
|
24
|
+
tsType: string,
|
|
25
|
+
nullable: boolean = false,
|
|
26
|
+
options: ObjCTypeMappingOptions = {}
|
|
27
|
+
): string {
|
|
20
28
|
if (tsType.startsWith('Promise<')) {
|
|
21
29
|
return 'void';
|
|
22
30
|
}
|
|
23
31
|
|
|
32
|
+
if (options.modelTypeNames?.includes(tsType)) {
|
|
33
|
+
return nullable ? 'NSDictionary * _Nullable' : 'NSDictionary *';
|
|
34
|
+
}
|
|
35
|
+
|
|
24
36
|
const mapped = TS_TO_OBJC_TYPE[tsType];
|
|
25
37
|
if (mapped) {
|
|
26
38
|
if (nullable && mapped.includes('*')) {
|
|
@@ -32,10 +44,20 @@ function mapTsTypeToObjC(tsType: string, nullable: boolean = false): string {
|
|
|
32
44
|
return nullable ? 'id _Nullable' : 'id';
|
|
33
45
|
}
|
|
34
46
|
|
|
35
|
-
|
|
47
|
+
interface SwiftTypeMappingOptions {
|
|
48
|
+
modelTypeNames?: string[];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
interface ObjCGenerationOptions extends ObjCTypeMappingOptions {}
|
|
52
|
+
|
|
53
|
+
function mapTsTypeToSwift(
|
|
54
|
+
tsType: string,
|
|
55
|
+
optional: boolean = false,
|
|
56
|
+
options: SwiftTypeMappingOptions = {}
|
|
57
|
+
): string {
|
|
36
58
|
if (tsType.startsWith('Promise<')) {
|
|
37
59
|
const inner = tsType.slice(8, -1);
|
|
38
|
-
return mapTsTypeToSwift(inner, optional);
|
|
60
|
+
return mapTsTypeToSwift(inner, optional, options);
|
|
39
61
|
}
|
|
40
62
|
|
|
41
63
|
const mapped = TS_TO_SWIFT_TYPE[tsType];
|
|
@@ -43,15 +65,22 @@ function mapTsTypeToSwift(tsType: string, optional: boolean = false): string {
|
|
|
43
65
|
return optional ? `${mapped}?` : mapped;
|
|
44
66
|
}
|
|
45
67
|
|
|
68
|
+
if (options.modelTypeNames?.includes(tsType)) {
|
|
69
|
+
return optional ? `${tsType}?` : tsType;
|
|
70
|
+
}
|
|
71
|
+
|
|
46
72
|
return optional ? 'Any?' : 'Any';
|
|
47
73
|
}
|
|
48
74
|
|
|
49
|
-
export function generateSwiftDelegate(
|
|
75
|
+
export function generateSwiftDelegate(
|
|
76
|
+
methods: MethodSignature[],
|
|
77
|
+
options: SwiftTypeMappingOptions = {}
|
|
78
|
+
): string {
|
|
50
79
|
const protocolMethods = methods
|
|
51
80
|
.map((method) => {
|
|
52
81
|
const params = method.params
|
|
53
82
|
.map((param, index) => {
|
|
54
|
-
const swiftType = mapTsTypeToSwift(param.type, param.optional);
|
|
83
|
+
const swiftType = mapTsTypeToSwift(param.type, param.optional, options);
|
|
55
84
|
const label = index === 0 ? '_' : param.name;
|
|
56
85
|
return `${label} ${param.name}: ${swiftType}`;
|
|
57
86
|
})
|
|
@@ -60,7 +89,7 @@ export function generateSwiftDelegate(methods: MethodSignature[]): string {
|
|
|
60
89
|
const returnType =
|
|
61
90
|
method.returnType === 'void'
|
|
62
91
|
? ''
|
|
63
|
-
: ` -> ${mapTsTypeToSwift(method.returnType, false)}`;
|
|
92
|
+
: ` -> ${mapTsTypeToSwift(method.returnType, false, options)}`;
|
|
64
93
|
|
|
65
94
|
return ` @objc func ${method.name}(${params})${returnType}`;
|
|
66
95
|
})
|
|
@@ -74,10 +103,15 @@ ${protocolMethods}
|
|
|
74
103
|
`;
|
|
75
104
|
}
|
|
76
105
|
|
|
77
|
-
export function generateObjCImplementation(
|
|
106
|
+
export function generateObjCImplementation(
|
|
107
|
+
methods: MethodSignature[],
|
|
108
|
+
options: ObjCGenerationOptions = {}
|
|
109
|
+
): string {
|
|
78
110
|
const methodImplementations = methods
|
|
79
111
|
.map((method) =>
|
|
80
|
-
method.isAsync
|
|
112
|
+
method.isAsync
|
|
113
|
+
? generateAsyncObjCMethod(method, options)
|
|
114
|
+
: generateSyncObjCMethod(method, options)
|
|
81
115
|
)
|
|
82
116
|
.join('\n\n');
|
|
83
117
|
|
|
@@ -108,38 +142,61 @@ ${methodImplementations}
|
|
|
108
142
|
`;
|
|
109
143
|
}
|
|
110
144
|
|
|
111
|
-
function generateSyncObjCMethod(
|
|
145
|
+
function generateSyncObjCMethod(
|
|
146
|
+
method: MethodSignature,
|
|
147
|
+
options: ObjCGenerationOptions
|
|
148
|
+
): string {
|
|
112
149
|
const { name, params, returnType } = method;
|
|
113
150
|
|
|
114
|
-
let signature = `- (${mapTsTypeToObjC(returnType)})${name}`;
|
|
151
|
+
let signature = `- (${mapTsTypeToObjC(returnType, false, options)})${name}`;
|
|
115
152
|
if (params.length > 0) {
|
|
116
153
|
signature += params
|
|
117
154
|
.map((param, index) => {
|
|
118
155
|
const prefix = index === 0 ? ':' : ` ${param.name}:`;
|
|
119
|
-
return `${prefix}(${mapTsTypeToObjC(param.type, param.optional)})${param.name}`;
|
|
156
|
+
return `${prefix}(${mapTsTypeToObjC(param.type, param.optional, options)})${param.name}`;
|
|
120
157
|
})
|
|
121
158
|
.join('');
|
|
122
159
|
}
|
|
123
160
|
|
|
161
|
+
const preparedParams: string[] = [];
|
|
162
|
+
const delegateArgs: string[] = [];
|
|
163
|
+
for (const param of params) {
|
|
164
|
+
if (options.modelTypeNames?.includes(param.type)) {
|
|
165
|
+
const convertedParamName = `${param.name}Model`;
|
|
166
|
+
preparedParams.push(
|
|
167
|
+
`${param.type} *${convertedParamName} = ${param.name} == nil ? nil : [${param.type} fromDictionary:${param.name}];`
|
|
168
|
+
);
|
|
169
|
+
delegateArgs.push(convertedParamName);
|
|
170
|
+
} else {
|
|
171
|
+
delegateArgs.push(param.name);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
124
175
|
let delegateCall = `[[[BrownfieldNavigationManager shared] getDelegate] ${name}`;
|
|
125
|
-
if (
|
|
126
|
-
delegateCall +=
|
|
127
|
-
.map((
|
|
176
|
+
if (delegateArgs.length > 0) {
|
|
177
|
+
delegateCall += delegateArgs
|
|
178
|
+
.map((argName, index) => {
|
|
179
|
+
const param = params[index];
|
|
128
180
|
const label = index === 0 ? '' : param.name;
|
|
129
|
-
return `${label}:${
|
|
181
|
+
return `${label}:${argName}`;
|
|
130
182
|
})
|
|
131
183
|
.join(' ');
|
|
132
184
|
}
|
|
133
185
|
delegateCall += ']';
|
|
134
186
|
|
|
135
187
|
const returnPrefix = returnType === 'void' ? '' : 'return ';
|
|
188
|
+
const preparedLines = preparedParams.map((line) => ` ${line}`).join('\n');
|
|
189
|
+
const bodyPrefix = preparedLines.length > 0 ? `${preparedLines}\n` : '';
|
|
136
190
|
|
|
137
191
|
return `${signature} {
|
|
138
|
-
${returnPrefix}${delegateCall};
|
|
192
|
+
${bodyPrefix} ${returnPrefix}${delegateCall};
|
|
139
193
|
}`;
|
|
140
194
|
}
|
|
141
195
|
|
|
142
|
-
function generateAsyncObjCMethod(
|
|
196
|
+
function generateAsyncObjCMethod(
|
|
197
|
+
method: MethodSignature,
|
|
198
|
+
options: ObjCGenerationOptions
|
|
199
|
+
): string {
|
|
143
200
|
const { name, params } = method;
|
|
144
201
|
|
|
145
202
|
let signature = `- (void)${name}`;
|
|
@@ -158,7 +215,7 @@ function generateAsyncObjCMethod(method: MethodSignature): string {
|
|
|
158
215
|
? 'RCTPromiseResolveBlock'
|
|
159
216
|
: param.type === 'RCTPromiseRejectBlock'
|
|
160
217
|
? 'RCTPromiseRejectBlock'
|
|
161
|
-
: mapTsTypeToObjC(param.type, param.optional);
|
|
218
|
+
: mapTsTypeToObjC(param.type, param.optional, options);
|
|
162
219
|
return `${prefix}(${type})${param.name}`;
|
|
163
220
|
})
|
|
164
221
|
.join(' ');
|