@callstack/brownfield-cli 4.2.0 → 4.3.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/dist/expoPluginConfig.d.ts +4 -0
- package/dist/expoPluginConfig.d.ts.map +1 -1
- package/dist/expoPluginConfig.js +3 -0
- package/dist/navigation/generators/android.d.ts.map +1 -1
- package/dist/navigation/generators/android.js +50 -23
- package/dist/navigation/generators/ios.d.ts.map +1 -1
- package/dist/navigation/generators/ios.js +62 -23
- package/dist/navigation/parser.d.ts.map +1 -1
- package/dist/navigation/parser.js +50 -3
- package/dist/navigation/types.d.ts +11 -0
- package/dist/navigation/types.d.ts.map +1 -1
- package/dist/types.d.ts +8 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/schema.json +11 -0
- package/src/expoPluginConfig.ts +9 -0
- package/src/navigation/generators/android.ts +74 -34
- package/src/navigation/generators/ios.ts +88 -29
- package/src/navigation/parser.ts +70 -3
- package/src/navigation/types.ts +13 -0
- package/src/types.ts +10 -0
|
@@ -22,6 +22,8 @@ export type BrownfieldPluginProps = {
|
|
|
22
22
|
artifactId?: string;
|
|
23
23
|
version?: string;
|
|
24
24
|
useLocalGradlePlugin?: boolean;
|
|
25
|
+
minifyEnabled?: boolean;
|
|
26
|
+
extraProguardRules?: string[];
|
|
25
27
|
useLocalMaven?: boolean;
|
|
26
28
|
missingDimensionStrategies?: string[];
|
|
27
29
|
};
|
|
@@ -36,6 +38,8 @@ export type ResolvedBrownfieldPluginAndroidConfig = {
|
|
|
36
38
|
artifactId: string;
|
|
37
39
|
version: string;
|
|
38
40
|
useLocalGradlePlugin: boolean;
|
|
41
|
+
minifyEnabled: boolean;
|
|
42
|
+
extraProguardRules: string[];
|
|
39
43
|
useLocalMaven: boolean;
|
|
40
44
|
missingDimensionStrategies: string[];
|
|
41
45
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"expoPluginConfig.d.ts","sourceRoot":"","sources":["../src/expoPluginConfig.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAEnD;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,GAAG,CAAC,EAAE;QACJ,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC,CAAC;QAC1D,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;KAC3B,CAAC;IACF,OAAO,CAAC,EAAE;QACR,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,oBAAoB,CAAC,EAAE,OAAO,CAAC;QAC/B,aAAa,CAAC,EAAE,OAAO,CAAC;QACxB,0BAA0B,CAAC,EAAE,MAAM,EAAE,CAAC;KACvC,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,qCAAqC,GAAG;IAClD,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,aAAa,EAAE,OAAO,CAAC;IACvB,0BAA0B,EAAE,MAAM,EAAE,CAAC;CACtC,CAAC;AAEF,MAAM,MAAM,iCAAiC,GAAG;IAC9C,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC,CAAC;IACzD,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,8BAA8B,GAAG;IAC3C,KAAK,EAAE,OAAO,CAAC;IACf,GAAG,EAAE,iCAAiC,GAAG,IAAI,CAAC;IAC9C,OAAO,EAAE,qCAAqC,GAAG,IAAI,CAAC;CACvD,CAAC;AAEF,KAAK,oBAAoB,GAAG;IAC1B,GAAG,CAAC,EAAE;QACJ,gBAAgB,CAAC,EAAE,MAAM,CAAC;KAC3B,CAAC;IACF,OAAO,CAAC,EAAE;QACR,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;CACH,CAAC;AA0DF;;;;;;GAMG;AACH,wBAAgB,+BAA+B,CAC7C,UAAU,EAAE,gBAAgB,GAAG,IAAI,EACnC,WAAW,EAAE,qBAAqB,GACjC,IAAI,CAQN;AAED;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAC3C,WAAW,EAAE,qBAAqB,EAClC,UAAU,EAAE,gBAAgB,GAAG,IAAI,EACnC,UAAU,EAAE,oBAAoB,GAC/B,8BAA8B,
|
|
1
|
+
{"version":3,"file":"expoPluginConfig.d.ts","sourceRoot":"","sources":["../src/expoPluginConfig.ts"],"names":[],"mappings":"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"}
|
package/dist/expoPluginConfig.js
CHANGED
|
@@ -66,6 +66,7 @@ export function assertNoConfigFilePluginOverlap(fileConfig, pluginProps) {
|
|
|
66
66
|
*/
|
|
67
67
|
export function resolveBrownfieldPluginConfig(pluginProps, fileConfig, expoConfig) {
|
|
68
68
|
const effectiveProps = fileConfig !== null ? fileConfigToPluginProps(fileConfig) : pluginProps;
|
|
69
|
+
const extraProguardRules = (effectiveProps.android?.extraProguardRules ?? []).filter((rule) => rule.trim().length > 0);
|
|
69
70
|
const androidPackage = expoConfig.android?.package;
|
|
70
71
|
/**
|
|
71
72
|
* Below: android.moduleName may be provided in the fully-qualified Gradle format
|
|
@@ -97,6 +98,8 @@ export function resolveBrownfieldPluginConfig(pluginProps, fileConfig, expoConfi
|
|
|
97
98
|
artifactId: effectiveProps.android?.artifactId ?? androidModuleName,
|
|
98
99
|
version: effectiveProps.android?.version ?? '0.0.1-SNAPSHOT',
|
|
99
100
|
useLocalGradlePlugin: effectiveProps.android?.useLocalGradlePlugin ?? false,
|
|
101
|
+
minifyEnabled: effectiveProps.android?.minifyEnabled ?? false,
|
|
102
|
+
extraProguardRules,
|
|
100
103
|
useLocalMaven: effectiveProps.android?.useLocalMaven ?? false,
|
|
101
104
|
missingDimensionStrategies: effectiveProps.android?.missingDimensionStrategies ?? [],
|
|
102
105
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"android.d.ts","sourceRoot":"","sources":["../../../src/navigation/generators/android.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"android.d.ts","sourceRoot":"","sources":["../../../src/navigation/generators/android.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAe,eAAe,EAAE,MAAM,aAAa,CAAC;AAUhE,UAAU,wBAAwB;IAChC,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAuED,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,eAAe,EAAE,EAC1B,iBAAiB,EAAE,MAAM,EACzB,OAAO,GAAE,wBAA6B,GACrC,MAAM,CAgCR;AAED,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,eAAe,EAAE,EAC1B,iBAAiB,EAAE,MAAM,EACzB,OAAO,GAAE,wBAA6B,GACrC,MAAM,CAuCR"}
|
|
@@ -5,6 +5,12 @@ const TS_TO_KOTLIN_TYPE = {
|
|
|
5
5
|
void: 'Unit',
|
|
6
6
|
Object: 'ReadableMap',
|
|
7
7
|
};
|
|
8
|
+
function buildKotlinImports(imports) {
|
|
9
|
+
if (imports.length === 0) {
|
|
10
|
+
return '\n';
|
|
11
|
+
}
|
|
12
|
+
return `\n${[...new Set(imports)].sort().join('\n')}\n`;
|
|
13
|
+
}
|
|
8
14
|
function mapTsTypeToKotlin(tsType, optional = false, options = {}, layer = 'delegate') {
|
|
9
15
|
if (tsType.startsWith('Promise<')) {
|
|
10
16
|
const inner = tsType.slice(8, -1);
|
|
@@ -22,27 +28,57 @@ function mapTsTypeToKotlin(tsType, optional = false, options = {}, layer = 'dele
|
|
|
22
28
|
}
|
|
23
29
|
return optional ? 'Any?' : 'Any';
|
|
24
30
|
}
|
|
31
|
+
function mapParamToKotlin(param, options = {}, layer = 'delegate') {
|
|
32
|
+
if (param.callback) {
|
|
33
|
+
return param.optional ? 'Callback?' : 'Callback';
|
|
34
|
+
}
|
|
35
|
+
return mapTsTypeToKotlin(param.type, param.optional, options, layer);
|
|
36
|
+
}
|
|
37
|
+
function prepareKotlinArgs(method, options = {}) {
|
|
38
|
+
const preparedParams = [];
|
|
39
|
+
const args = method.params
|
|
40
|
+
.map((param) => {
|
|
41
|
+
if (options.modelTypeNames?.includes(param.type)) {
|
|
42
|
+
const convertedName = `${param.name}Model`;
|
|
43
|
+
preparedParams.push(` val ${convertedName} = ${param.name}${param.optional
|
|
44
|
+
? `?.let(::to${param.type})`
|
|
45
|
+
: `.let(::to${param.type})`}`);
|
|
46
|
+
return convertedName;
|
|
47
|
+
}
|
|
48
|
+
return param.name;
|
|
49
|
+
})
|
|
50
|
+
.join(', ');
|
|
51
|
+
return { preparedParams, args };
|
|
52
|
+
}
|
|
25
53
|
export function generateKotlinDelegate(methods, kotlinPackageName, options = {}) {
|
|
54
|
+
const hasAsyncMethod = methods.some((method) => method.isAsync);
|
|
55
|
+
const hasCallbackParam = methods.some((method) => method.params.some((param) => param.callback));
|
|
56
|
+
const imports = buildKotlinImports([
|
|
57
|
+
...(hasCallbackParam ? ['import com.facebook.react.bridge.Callback'] : []),
|
|
58
|
+
...(hasAsyncMethod ? ['import com.facebook.react.bridge.Promise'] : []),
|
|
59
|
+
]);
|
|
26
60
|
const methodSignatures = methods
|
|
27
61
|
.map((method) => {
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
62
|
+
const methodParams = method.params.map((param) => `${param.name}: ${mapParamToKotlin(param, options)}`);
|
|
63
|
+
const params = [
|
|
64
|
+
...methodParams,
|
|
65
|
+
...(method.isAsync ? ['promise: Promise'] : []),
|
|
66
|
+
].join(', ');
|
|
67
|
+
const returnType = method.returnType === 'void' || method.isAsync
|
|
32
68
|
? ''
|
|
33
69
|
: `: ${mapTsTypeToKotlin(method.returnType, false, options, 'delegate')}`;
|
|
34
70
|
return ` fun ${method.name}(${params})${returnType}`;
|
|
35
71
|
})
|
|
36
72
|
.join('\n');
|
|
37
73
|
return `package ${kotlinPackageName}
|
|
38
|
-
|
|
39
|
-
interface BrownfieldNavigationDelegate {
|
|
74
|
+
${imports}interface BrownfieldNavigationDelegate {
|
|
40
75
|
${methodSignatures}
|
|
41
76
|
}
|
|
42
77
|
`;
|
|
43
78
|
}
|
|
44
79
|
export function generateKotlinModule(methods, kotlinPackageName, options = {}) {
|
|
45
80
|
const hasAsyncMethod = methods.some((method) => method.isAsync);
|
|
81
|
+
const hasCallbackParam = methods.some((method) => method.params.some((param) => param.callback));
|
|
46
82
|
const hasObjectType = methods.some((method) => method.returnType.includes('Object') ||
|
|
47
83
|
method.params.some((param) => param.type === 'Object' || options.modelTypeNames?.includes(param.type)));
|
|
48
84
|
const methodImplementations = methods
|
|
@@ -53,7 +89,7 @@ export function generateKotlinModule(methods, kotlinPackageName, options = {}) {
|
|
|
53
89
|
return `package ${kotlinPackageName}
|
|
54
90
|
|
|
55
91
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
56
|
-
import com.facebook.react.bridge.ReactMethod${hasAsyncMethod ? '\nimport com.facebook.react.bridge.Promise' : ''}${hasObjectType ? '\nimport com.facebook.react.bridge.ReadableMap' : ''}
|
|
92
|
+
import com.facebook.react.bridge.ReactMethod${hasCallbackParam ? '\nimport com.facebook.react.bridge.Callback' : ''}${hasAsyncMethod ? '\nimport com.facebook.react.bridge.Promise' : ''}${hasObjectType ? '\nimport com.facebook.react.bridge.ReadableMap' : ''}
|
|
57
93
|
|
|
58
94
|
class NativeBrownfieldNavigationModule(
|
|
59
95
|
reactContext: ReactApplicationContext
|
|
@@ -68,21 +104,9 @@ ${methodImplementations}
|
|
|
68
104
|
}
|
|
69
105
|
function generateSyncKotlinMethod(method, options = {}) {
|
|
70
106
|
const params = method.params
|
|
71
|
-
.map((param) => `${param.name}: ${
|
|
72
|
-
.join(', ');
|
|
73
|
-
const preparedParams = [];
|
|
74
|
-
const args = method.params
|
|
75
|
-
.map((param) => {
|
|
76
|
-
if (options.modelTypeNames?.includes(param.type)) {
|
|
77
|
-
const convertedName = `${param.name}Model`;
|
|
78
|
-
preparedParams.push(` val ${convertedName} = ${param.name}${param.optional
|
|
79
|
-
? `?.let(::to${param.type})`
|
|
80
|
-
: `.let(::to${param.type})`}`);
|
|
81
|
-
return convertedName;
|
|
82
|
-
}
|
|
83
|
-
return param.name;
|
|
84
|
-
})
|
|
107
|
+
.map((param) => `${param.name}: ${mapParamToKotlin(param, options, 'module')}`)
|
|
85
108
|
.join(', ');
|
|
109
|
+
const { preparedParams, args } = prepareKotlinArgs(method, options);
|
|
86
110
|
const signature = ` @ReactMethod\n override fun ${method.name}(${params})${method.returnType === 'void'
|
|
87
111
|
? ''
|
|
88
112
|
: `: ${mapTsTypeToKotlin(method.returnType, false, options, 'module')}`}`;
|
|
@@ -98,13 +122,16 @@ ${preparedPrefix} return BrownfieldNavigationManager.getDelegate().${method.n
|
|
|
98
122
|
}
|
|
99
123
|
function generateAsyncKotlinMethod(method, options = {}) {
|
|
100
124
|
const paramsWithTypes = method.params
|
|
101
|
-
.map((param) => `${param.name}: ${
|
|
125
|
+
.map((param) => `${param.name}: ${mapParamToKotlin(param, options, 'module')}`)
|
|
102
126
|
.join(', ');
|
|
103
127
|
const params = paramsWithTypes.length > 0
|
|
104
128
|
? `${paramsWithTypes}, promise: Promise`
|
|
105
129
|
: 'promise: Promise';
|
|
130
|
+
const { preparedParams, args } = prepareKotlinArgs(method, options);
|
|
131
|
+
const delegateArgs = args.length > 0 ? `${args}, promise` : 'promise';
|
|
132
|
+
const preparedPrefix = preparedParams.length > 0 ? `${preparedParams.join('\n')}\n` : '';
|
|
106
133
|
return ` @ReactMethod
|
|
107
134
|
override fun ${method.name}(${params}) {
|
|
108
|
-
|
|
135
|
+
${preparedPrefix} BrownfieldNavigationManager.getDelegate().${method.name}(${delegateArgs})
|
|
109
136
|
}`;
|
|
110
137
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ios.d.ts","sourceRoot":"","sources":["../../../src/navigation/generators/ios.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"ios.d.ts","sourceRoot":"","sources":["../../../src/navigation/generators/ios.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAe,eAAe,EAAE,MAAM,aAAa,CAAC;AAkBhE,UAAU,sBAAsB;IAC9B,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAuCD,UAAU,uBAAuB;IAC/B,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,UAAU,qBAAsB,SAAQ,sBAAsB;CAAG;AA2CjE,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,eAAe,EAAE,EAC1B,OAAO,GAAE,uBAA4B,GACpC,MAAM,CA+BR;AAED,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,eAAe,EAAE,EAC1B,OAAO,GAAE,qBAA0B,GAClC,MAAM,CAkCR"}
|
|
@@ -28,6 +28,14 @@ function mapTsTypeToObjC(tsType, nullable = false, options = {}) {
|
|
|
28
28
|
}
|
|
29
29
|
return nullable ? 'id _Nullable' : 'id';
|
|
30
30
|
}
|
|
31
|
+
function mapParamToObjC(param, nullable = false, options = {}) {
|
|
32
|
+
if (param.callback) {
|
|
33
|
+
return param.optional
|
|
34
|
+
? 'RCTResponseSenderBlock _Nullable'
|
|
35
|
+
: 'RCTResponseSenderBlock';
|
|
36
|
+
}
|
|
37
|
+
return mapTsTypeToObjC(param.type, nullable, options);
|
|
38
|
+
}
|
|
31
39
|
function mapTsTypeToSwift(tsType, optional = false, options = {}) {
|
|
32
40
|
if (tsType.startsWith('Promise<')) {
|
|
33
41
|
const inner = tsType.slice(8, -1);
|
|
@@ -42,17 +50,35 @@ function mapTsTypeToSwift(tsType, optional = false, options = {}) {
|
|
|
42
50
|
}
|
|
43
51
|
return optional ? 'Any?' : 'Any';
|
|
44
52
|
}
|
|
53
|
+
/** ObjC-compatible closures that match RCT typedefs without importing React. */
|
|
54
|
+
const SWIFT_RESPONSE_SENDER_BLOCK = '@escaping ([Any]?) -> Void';
|
|
55
|
+
const SWIFT_OPTIONAL_RESPONSE_SENDER_BLOCK = '(([Any]?) -> Void)?';
|
|
56
|
+
const SWIFT_PROMISE_RESOLVE_BLOCK = '@escaping (Any?) -> Void';
|
|
57
|
+
const SWIFT_PROMISE_REJECT_BLOCK = '@escaping (String?, String?, (any Error)?) -> Void';
|
|
58
|
+
function mapParamToSwift(param, options = {}) {
|
|
59
|
+
if (param.callback) {
|
|
60
|
+
return param.optional
|
|
61
|
+
? SWIFT_OPTIONAL_RESPONSE_SENDER_BLOCK
|
|
62
|
+
: SWIFT_RESPONSE_SENDER_BLOCK;
|
|
63
|
+
}
|
|
64
|
+
return mapTsTypeToSwift(param.type, param.optional, options);
|
|
65
|
+
}
|
|
45
66
|
export function generateSwiftDelegate(methods, options = {}) {
|
|
46
67
|
const protocolMethods = methods
|
|
47
68
|
.map((method) => {
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
const swiftType = mapTsTypeToSwift(param.type, param.optional, options);
|
|
69
|
+
const methodParams = method.params.map((param, index) => {
|
|
70
|
+
const swiftType = mapParamToSwift(param, options);
|
|
51
71
|
const label = index === 0 ? '_' : param.name;
|
|
52
72
|
return `${label} ${param.name}: ${swiftType}`;
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
|
|
73
|
+
});
|
|
74
|
+
const promiseParams = method.isAsync
|
|
75
|
+
? [
|
|
76
|
+
`${methodParams.length === 0 ? '_ resolve' : 'resolve'}: ${SWIFT_PROMISE_RESOLVE_BLOCK}`,
|
|
77
|
+
`reject: ${SWIFT_PROMISE_REJECT_BLOCK}`,
|
|
78
|
+
]
|
|
79
|
+
: [];
|
|
80
|
+
const params = [...methodParams, ...promiseParams].join(', ');
|
|
81
|
+
const returnType = method.returnType === 'void' || method.isAsync
|
|
56
82
|
? ''
|
|
57
83
|
: ` -> ${mapTsTypeToSwift(method.returnType, false, options)}`;
|
|
58
84
|
return ` @objc func ${method.name}(${params})${returnType}`;
|
|
@@ -97,20 +123,10 @@ ${methodImplementations}
|
|
|
97
123
|
@end
|
|
98
124
|
`;
|
|
99
125
|
}
|
|
100
|
-
function
|
|
101
|
-
const { name, params, returnType } = method;
|
|
102
|
-
let signature = `- (${mapTsTypeToObjC(returnType, false, options)})${name}`;
|
|
103
|
-
if (params.length > 0) {
|
|
104
|
-
signature += params
|
|
105
|
-
.map((param, index) => {
|
|
106
|
-
const prefix = index === 0 ? ':' : ` ${param.name}:`;
|
|
107
|
-
return `${prefix}(${mapTsTypeToObjC(param.type, param.optional, options)})${param.name}`;
|
|
108
|
-
})
|
|
109
|
-
.join('');
|
|
110
|
-
}
|
|
126
|
+
function prepareObjCArgs(method, options) {
|
|
111
127
|
const preparedParams = [];
|
|
112
128
|
const delegateArgs = [];
|
|
113
|
-
for (const param of params) {
|
|
129
|
+
for (const param of method.params) {
|
|
114
130
|
if (options.modelTypeNames?.includes(param.type)) {
|
|
115
131
|
const convertedParamName = `${param.name}Model`;
|
|
116
132
|
preparedParams.push(`${param.type} *${convertedParamName} = ${param.name} == nil ? nil : [${param.type} fromDictionary:${param.name}];`);
|
|
@@ -120,6 +136,9 @@ function generateSyncObjCMethod(method, options) {
|
|
|
120
136
|
delegateArgs.push(param.name);
|
|
121
137
|
}
|
|
122
138
|
}
|
|
139
|
+
return { preparedParams, delegateArgs };
|
|
140
|
+
}
|
|
141
|
+
function buildObjCDelegateCall(name, params, delegateArgs) {
|
|
123
142
|
let delegateCall = `[[[BrownfieldNavigationManager shared] getDelegate] ${name}`;
|
|
124
143
|
if (delegateArgs.length > 0) {
|
|
125
144
|
delegateCall += delegateArgs
|
|
@@ -131,6 +150,21 @@ function generateSyncObjCMethod(method, options) {
|
|
|
131
150
|
.join(' ');
|
|
132
151
|
}
|
|
133
152
|
delegateCall += ']';
|
|
153
|
+
return delegateCall;
|
|
154
|
+
}
|
|
155
|
+
function generateSyncObjCMethod(method, options) {
|
|
156
|
+
const { name, params, returnType } = method;
|
|
157
|
+
let signature = `- (${mapTsTypeToObjC(returnType, false, options)})${name}`;
|
|
158
|
+
if (params.length > 0) {
|
|
159
|
+
signature += params
|
|
160
|
+
.map((param, index) => {
|
|
161
|
+
const prefix = index === 0 ? ':' : ` ${param.name}:`;
|
|
162
|
+
return `${prefix}(${mapParamToObjC(param, param.optional, options)})${param.name}`;
|
|
163
|
+
})
|
|
164
|
+
.join('');
|
|
165
|
+
}
|
|
166
|
+
const { preparedParams, delegateArgs } = prepareObjCArgs(method, options);
|
|
167
|
+
const delegateCall = buildObjCDelegateCall(name, params, delegateArgs);
|
|
134
168
|
const returnPrefix = returnType === 'void' ? '' : 'return ';
|
|
135
169
|
const preparedLines = preparedParams.map((line) => ` ${line}`).join('\n');
|
|
136
170
|
const bodyPrefix = preparedLines.length > 0 ? `${preparedLines}\n` : '';
|
|
@@ -141,24 +175,29 @@ ${bodyPrefix} ${returnPrefix}${delegateCall};
|
|
|
141
175
|
function generateAsyncObjCMethod(method, options) {
|
|
142
176
|
const { name, params } = method;
|
|
143
177
|
let signature = `- (void)${name}`;
|
|
144
|
-
const
|
|
145
|
-
...params,
|
|
178
|
+
const promiseParams = [
|
|
146
179
|
{ name: 'resolve', type: 'RCTPromiseResolveBlock', optional: false },
|
|
147
180
|
{ name: 'reject', type: 'RCTPromiseRejectBlock', optional: false },
|
|
148
181
|
];
|
|
182
|
+
const allParams = [...params, ...promiseParams];
|
|
149
183
|
signature += ':';
|
|
150
184
|
signature += allParams
|
|
151
185
|
.map((param, index) => {
|
|
152
|
-
const prefix = index === 0 ? '' : param.name
|
|
186
|
+
const prefix = index === 0 ? '' : `${param.name}:`;
|
|
153
187
|
const type = param.type === 'RCTPromiseResolveBlock'
|
|
154
188
|
? 'RCTPromiseResolveBlock'
|
|
155
189
|
: param.type === 'RCTPromiseRejectBlock'
|
|
156
190
|
? 'RCTPromiseRejectBlock'
|
|
157
|
-
:
|
|
191
|
+
: mapParamToObjC(param, param.optional, options);
|
|
158
192
|
return `${prefix}(${type})${param.name}`;
|
|
159
193
|
})
|
|
160
194
|
.join(' ');
|
|
195
|
+
const { preparedParams, delegateArgs } = prepareObjCArgs(method, options);
|
|
196
|
+
const asyncDelegateArgs = [...delegateArgs, 'resolve', 'reject'];
|
|
197
|
+
const delegateCall = buildObjCDelegateCall(name, allParams, asyncDelegateArgs);
|
|
198
|
+
const preparedLines = preparedParams.map((line) => ` ${line}`).join('\n');
|
|
199
|
+
const bodyPrefix = preparedLines.length > 0 ? `${preparedLines}\n` : '';
|
|
161
200
|
return `${signature} {
|
|
162
|
-
|
|
201
|
+
${bodyPrefix} ${delegateCall};
|
|
163
202
|
}`;
|
|
164
203
|
}
|
|
@@ -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,EAMV,oBAAoB,EAErB,MAAM,YAAY,CAAC;AAyFpB,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,oBAAoB,CAuH1E"}
|
|
@@ -29,6 +29,42 @@ function collectReferencedTypesFromText(typeText) {
|
|
|
29
29
|
}
|
|
30
30
|
return matches.filter((match) => !SKIP_TYPE_TOKENS.has(match));
|
|
31
31
|
}
|
|
32
|
+
function getPromiseInnerType(typeText) {
|
|
33
|
+
return typeText.startsWith('Promise<') && typeText.endsWith('>')
|
|
34
|
+
? typeText.slice(8, -1)
|
|
35
|
+
: undefined;
|
|
36
|
+
}
|
|
37
|
+
function parseCallbackSignature(typeNode) {
|
|
38
|
+
if (!typeNode || !Node.isFunctionTypeNode(typeNode)) {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
params: typeNode.getParameters().map((param) => ({
|
|
43
|
+
name: param.getName(),
|
|
44
|
+
type: param.getTypeNode()?.getText() ?? 'unknown',
|
|
45
|
+
optional: param.isOptional(),
|
|
46
|
+
})),
|
|
47
|
+
returnType: typeNode.getReturnTypeNode()?.getText() ?? 'void',
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
const RESERVED_ASYNC_PARAM_NAMES = new Set(['promise', 'resolve', 'reject']);
|
|
51
|
+
function validateMethodSignature(method) {
|
|
52
|
+
for (const param of method.params) {
|
|
53
|
+
if (getPromiseInnerType(param.type)) {
|
|
54
|
+
throw new Error(`Unsupported Promise parameter "${param.name}" in method "${method.name}": Promise<T> is only supported as a method return type.`);
|
|
55
|
+
}
|
|
56
|
+
if (method.isAsync &&
|
|
57
|
+
RESERVED_ASYNC_PARAM_NAMES.has(param.name)) {
|
|
58
|
+
throw new Error(`Reserved parameter name "${param.name}" in async method "${method.name}": this name is used by the generated bridging code. Rename the parameter.`);
|
|
59
|
+
}
|
|
60
|
+
if (!param.callback) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (param.callback.returnType !== 'void') {
|
|
64
|
+
throw new Error(`Unsupported callback parameter "${param.name}" in method "${method.name}": callback return type "${param.callback.returnType}" is not supported. Use a void callback or model the result as a Promise-returning navigation method.`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
32
68
|
export function parseNavigationSpec(specPath) {
|
|
33
69
|
if (!fs.existsSync(specPath)) {
|
|
34
70
|
throw new Error(`Spec file not found: ${specPath}`);
|
|
@@ -44,20 +80,31 @@ export function parseNavigationSpec(specPath) {
|
|
|
44
80
|
const name = method.getName();
|
|
45
81
|
const params = method.getParameters().map((param) => {
|
|
46
82
|
const typeNode = param.getTypeNode();
|
|
47
|
-
|
|
83
|
+
const callback = parseCallbackSignature(typeNode);
|
|
84
|
+
const methodParam = {
|
|
48
85
|
name: param.getName(),
|
|
49
86
|
type: typeNode?.getText() ?? 'unknown',
|
|
50
87
|
optional: param.isOptional(),
|
|
51
88
|
};
|
|
89
|
+
if (callback) {
|
|
90
|
+
methodParam.callback = callback;
|
|
91
|
+
}
|
|
92
|
+
return methodParam;
|
|
52
93
|
});
|
|
53
94
|
const returnTypeNode = method.getReturnTypeNode();
|
|
54
95
|
const returnType = returnTypeNode?.getText() ?? 'void';
|
|
55
|
-
|
|
96
|
+
const promiseReturnType = getPromiseInnerType(returnType);
|
|
97
|
+
const methodSignature = {
|
|
56
98
|
name,
|
|
57
99
|
params,
|
|
58
100
|
returnType,
|
|
59
|
-
isAsync:
|
|
101
|
+
isAsync: Boolean(promiseReturnType),
|
|
60
102
|
};
|
|
103
|
+
if (promiseReturnType) {
|
|
104
|
+
methodSignature.promiseReturnType = promiseReturnType;
|
|
105
|
+
}
|
|
106
|
+
validateMethodSignature(methodSignature);
|
|
107
|
+
return methodSignature;
|
|
61
108
|
});
|
|
62
109
|
const declarationNodes = [
|
|
63
110
|
...sourceFile.getTypeAliases(),
|
|
@@ -1,13 +1,24 @@
|
|
|
1
|
+
export interface CallbackParam {
|
|
2
|
+
name: string;
|
|
3
|
+
type: string;
|
|
4
|
+
optional: boolean;
|
|
5
|
+
}
|
|
6
|
+
export interface CallbackSignature {
|
|
7
|
+
params: CallbackParam[];
|
|
8
|
+
returnType: string;
|
|
9
|
+
}
|
|
1
10
|
export interface MethodParam {
|
|
2
11
|
name: string;
|
|
3
12
|
type: string;
|
|
4
13
|
optional: boolean;
|
|
14
|
+
callback?: CallbackSignature;
|
|
5
15
|
}
|
|
6
16
|
export interface MethodSignature {
|
|
7
17
|
name: string;
|
|
8
18
|
params: MethodParam[];
|
|
9
19
|
returnType: string;
|
|
10
20
|
isAsync: boolean;
|
|
21
|
+
promiseReturnType?: string;
|
|
11
22
|
}
|
|
12
23
|
export interface TypeDeclaration {
|
|
13
24
|
name: 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;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/navigation/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;CAC9B;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;IACjB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;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/dist/types.d.ts
CHANGED
|
@@ -39,6 +39,14 @@ export type BrownfieldExpoAndroidConfig = {
|
|
|
39
39
|
* The package name for the Android library module.
|
|
40
40
|
*/
|
|
41
41
|
packageName?: string;
|
|
42
|
+
/**
|
|
43
|
+
* Toggle minification for the generated Android library.
|
|
44
|
+
*/
|
|
45
|
+
minifyEnabled?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Extra Proguard rules appended to the default rules.
|
|
48
|
+
*/
|
|
49
|
+
extraProguardRules?: string[];
|
|
42
50
|
/**
|
|
43
51
|
* Minimum SDK version for the Android library.
|
|
44
52
|
*/
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/schema.json
CHANGED
|
@@ -57,6 +57,13 @@
|
|
|
57
57
|
"description": "Compile SDK version for the Android library.",
|
|
58
58
|
"type": "number"
|
|
59
59
|
},
|
|
60
|
+
"extraProguardRules": {
|
|
61
|
+
"description": "Extra Proguard rules appended to the default rules. Each entry must be a non-empty string.",
|
|
62
|
+
"items": {
|
|
63
|
+
"type": "string"
|
|
64
|
+
},
|
|
65
|
+
"type": "array"
|
|
66
|
+
},
|
|
60
67
|
"groupId": {
|
|
61
68
|
"description": "Maven group ID used when publishing the AAR.",
|
|
62
69
|
"type": "string"
|
|
@@ -65,6 +72,10 @@
|
|
|
65
72
|
"description": "Minimum SDK version for the Android library.",
|
|
66
73
|
"type": "number"
|
|
67
74
|
},
|
|
75
|
+
"minifyEnabled": {
|
|
76
|
+
"description": "Toggle minification for the generated Android library.",
|
|
77
|
+
"type": "boolean"
|
|
78
|
+
},
|
|
68
79
|
"missingDimensionStrategies": {
|
|
69
80
|
"description": "Missing dimension strategies as dimension + flavor(s), e.g. \"type, alpha\".",
|
|
70
81
|
"items": {
|
package/src/expoPluginConfig.ts
CHANGED
|
@@ -24,6 +24,8 @@ export type BrownfieldPluginProps = {
|
|
|
24
24
|
artifactId?: string;
|
|
25
25
|
version?: string;
|
|
26
26
|
useLocalGradlePlugin?: boolean;
|
|
27
|
+
minifyEnabled?: boolean;
|
|
28
|
+
extraProguardRules?: string[];
|
|
27
29
|
useLocalMaven?: boolean;
|
|
28
30
|
missingDimensionStrategies?: string[];
|
|
29
31
|
};
|
|
@@ -39,6 +41,8 @@ export type ResolvedBrownfieldPluginAndroidConfig = {
|
|
|
39
41
|
artifactId: string;
|
|
40
42
|
version: string;
|
|
41
43
|
useLocalGradlePlugin: boolean;
|
|
44
|
+
minifyEnabled: boolean;
|
|
45
|
+
extraProguardRules: string[];
|
|
42
46
|
useLocalMaven: boolean;
|
|
43
47
|
missingDimensionStrategies: string[];
|
|
44
48
|
};
|
|
@@ -156,6 +160,9 @@ export function resolveBrownfieldPluginConfig(
|
|
|
156
160
|
): ResolvedBrownfieldPluginConfig {
|
|
157
161
|
const effectiveProps =
|
|
158
162
|
fileConfig !== null ? fileConfigToPluginProps(fileConfig) : pluginProps;
|
|
163
|
+
const extraProguardRules = (
|
|
164
|
+
effectiveProps.android?.extraProguardRules ?? []
|
|
165
|
+
).filter((rule) => rule.trim().length > 0);
|
|
159
166
|
|
|
160
167
|
const androidPackage = expoConfig.android?.package;
|
|
161
168
|
/**
|
|
@@ -193,6 +200,8 @@ export function resolveBrownfieldPluginConfig(
|
|
|
193
200
|
version: effectiveProps.android?.version ?? '0.0.1-SNAPSHOT',
|
|
194
201
|
useLocalGradlePlugin:
|
|
195
202
|
effectiveProps.android?.useLocalGradlePlugin ?? false,
|
|
203
|
+
minifyEnabled: effectiveProps.android?.minifyEnabled ?? false,
|
|
204
|
+
extraProguardRules,
|
|
196
205
|
useLocalMaven: effectiveProps.android?.useLocalMaven ?? false,
|
|
197
206
|
missingDimensionStrategies:
|
|
198
207
|
effectiveProps.android?.missingDimensionStrategies ?? [],
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { MethodSignature } from '../types.js';
|
|
1
|
+
import type { MethodParam, MethodSignature } from '../types.js';
|
|
2
2
|
|
|
3
3
|
const TS_TO_KOTLIN_TYPE: Record<string, string> = {
|
|
4
4
|
string: 'String',
|
|
@@ -12,6 +12,13 @@ interface KotlinTypeMappingOptions {
|
|
|
12
12
|
modelTypeNames?: string[];
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
function buildKotlinImports(imports: string[]): string {
|
|
16
|
+
if (imports.length === 0) {
|
|
17
|
+
return '\n';
|
|
18
|
+
}
|
|
19
|
+
return `\n${[...new Set(imports)].sort().join('\n')}\n`;
|
|
20
|
+
}
|
|
21
|
+
|
|
15
22
|
function mapTsTypeToKotlin(
|
|
16
23
|
tsType: string,
|
|
17
24
|
optional: boolean = false,
|
|
@@ -38,21 +45,67 @@ function mapTsTypeToKotlin(
|
|
|
38
45
|
return optional ? 'Any?' : 'Any';
|
|
39
46
|
}
|
|
40
47
|
|
|
48
|
+
function mapParamToKotlin(
|
|
49
|
+
param: MethodParam,
|
|
50
|
+
options: KotlinTypeMappingOptions = {},
|
|
51
|
+
layer: 'delegate' | 'module' = 'delegate'
|
|
52
|
+
): string {
|
|
53
|
+
if (param.callback) {
|
|
54
|
+
return param.optional ? 'Callback?' : 'Callback';
|
|
55
|
+
}
|
|
56
|
+
return mapTsTypeToKotlin(param.type, param.optional, options, layer);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function prepareKotlinArgs(
|
|
60
|
+
method: MethodSignature,
|
|
61
|
+
options: KotlinTypeMappingOptions = {}
|
|
62
|
+
): { preparedParams: string[]; args: string } {
|
|
63
|
+
const preparedParams: string[] = [];
|
|
64
|
+
const args = method.params
|
|
65
|
+
.map((param) => {
|
|
66
|
+
if (options.modelTypeNames?.includes(param.type)) {
|
|
67
|
+
const convertedName = `${param.name}Model`;
|
|
68
|
+
preparedParams.push(
|
|
69
|
+
` val ${convertedName} = ${param.name}${
|
|
70
|
+
param.optional
|
|
71
|
+
? `?.let(::to${param.type})`
|
|
72
|
+
: `.let(::to${param.type})`
|
|
73
|
+
}`
|
|
74
|
+
);
|
|
75
|
+
return convertedName;
|
|
76
|
+
}
|
|
77
|
+
return param.name;
|
|
78
|
+
})
|
|
79
|
+
.join(', ');
|
|
80
|
+
|
|
81
|
+
return { preparedParams, args };
|
|
82
|
+
}
|
|
83
|
+
|
|
41
84
|
export function generateKotlinDelegate(
|
|
42
85
|
methods: MethodSignature[],
|
|
43
86
|
kotlinPackageName: string,
|
|
44
87
|
options: KotlinTypeMappingOptions = {}
|
|
45
88
|
): string {
|
|
89
|
+
const hasAsyncMethod = methods.some((method) => method.isAsync);
|
|
90
|
+
const hasCallbackParam = methods.some((method) =>
|
|
91
|
+
method.params.some((param) => param.callback)
|
|
92
|
+
);
|
|
93
|
+
const imports = buildKotlinImports([
|
|
94
|
+
...(hasCallbackParam ? ['import com.facebook.react.bridge.Callback'] : []),
|
|
95
|
+
...(hasAsyncMethod ? ['import com.facebook.react.bridge.Promise'] : []),
|
|
96
|
+
]);
|
|
97
|
+
|
|
46
98
|
const methodSignatures = methods
|
|
47
99
|
.map((method) => {
|
|
48
|
-
const
|
|
49
|
-
.
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
.
|
|
100
|
+
const methodParams = method.params.map(
|
|
101
|
+
(param) => `${param.name}: ${mapParamToKotlin(param, options)}`
|
|
102
|
+
);
|
|
103
|
+
const params = [
|
|
104
|
+
...methodParams,
|
|
105
|
+
...(method.isAsync ? ['promise: Promise'] : []),
|
|
106
|
+
].join(', ');
|
|
54
107
|
const returnType =
|
|
55
|
-
method.returnType === 'void'
|
|
108
|
+
method.returnType === 'void' || method.isAsync
|
|
56
109
|
? ''
|
|
57
110
|
: `: ${mapTsTypeToKotlin(method.returnType, false, options, 'delegate')}`;
|
|
58
111
|
return ` fun ${method.name}(${params})${returnType}`;
|
|
@@ -60,8 +113,7 @@ export function generateKotlinDelegate(
|
|
|
60
113
|
.join('\n');
|
|
61
114
|
|
|
62
115
|
return `package ${kotlinPackageName}
|
|
63
|
-
|
|
64
|
-
interface BrownfieldNavigationDelegate {
|
|
116
|
+
${imports}interface BrownfieldNavigationDelegate {
|
|
65
117
|
${methodSignatures}
|
|
66
118
|
}
|
|
67
119
|
`;
|
|
@@ -73,6 +125,9 @@ export function generateKotlinModule(
|
|
|
73
125
|
options: KotlinTypeMappingOptions = {}
|
|
74
126
|
): string {
|
|
75
127
|
const hasAsyncMethod = methods.some((method) => method.isAsync);
|
|
128
|
+
const hasCallbackParam = methods.some((method) =>
|
|
129
|
+
method.params.some((param) => param.callback)
|
|
130
|
+
);
|
|
76
131
|
const hasObjectType = methods.some(
|
|
77
132
|
(method) =>
|
|
78
133
|
method.returnType.includes('Object') ||
|
|
@@ -94,8 +149,8 @@ export function generateKotlinModule(
|
|
|
94
149
|
|
|
95
150
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
96
151
|
import com.facebook.react.bridge.ReactMethod${
|
|
97
|
-
|
|
98
|
-
}${hasObjectType ? '\nimport com.facebook.react.bridge.ReadableMap' : ''}
|
|
152
|
+
hasCallbackParam ? '\nimport com.facebook.react.bridge.Callback' : ''
|
|
153
|
+
}${hasAsyncMethod ? '\nimport com.facebook.react.bridge.Promise' : ''}${hasObjectType ? '\nimport com.facebook.react.bridge.ReadableMap' : ''}
|
|
99
154
|
|
|
100
155
|
class NativeBrownfieldNavigationModule(
|
|
101
156
|
reactContext: ReactApplicationContext
|
|
@@ -115,27 +170,10 @@ function generateSyncKotlinMethod(
|
|
|
115
170
|
): string {
|
|
116
171
|
const params = method.params
|
|
117
172
|
.map(
|
|
118
|
-
(param) =>
|
|
119
|
-
`${param.name}: ${mapTsTypeToKotlin(param.type, param.optional, options, 'module')}`
|
|
173
|
+
(param) => `${param.name}: ${mapParamToKotlin(param, options, 'module')}`
|
|
120
174
|
)
|
|
121
175
|
.join(', ');
|
|
122
|
-
const preparedParams
|
|
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
|
-
})
|
|
138
|
-
.join(', ');
|
|
176
|
+
const { preparedParams, args } = prepareKotlinArgs(method, options);
|
|
139
177
|
|
|
140
178
|
const signature = ` @ReactMethod\n override fun ${method.name}(${params})${
|
|
141
179
|
method.returnType === 'void'
|
|
@@ -161,17 +199,19 @@ function generateAsyncKotlinMethod(
|
|
|
161
199
|
): string {
|
|
162
200
|
const paramsWithTypes = method.params
|
|
163
201
|
.map(
|
|
164
|
-
(param) =>
|
|
165
|
-
`${param.name}: ${mapTsTypeToKotlin(param.type, param.optional, options, 'module')}`
|
|
202
|
+
(param) => `${param.name}: ${mapParamToKotlin(param, options, 'module')}`
|
|
166
203
|
)
|
|
167
204
|
.join(', ');
|
|
168
205
|
const params =
|
|
169
206
|
paramsWithTypes.length > 0
|
|
170
207
|
? `${paramsWithTypes}, promise: Promise`
|
|
171
208
|
: 'promise: Promise';
|
|
209
|
+
const { preparedParams, args } = prepareKotlinArgs(method, options);
|
|
210
|
+
const delegateArgs = args.length > 0 ? `${args}, promise` : 'promise';
|
|
211
|
+
const preparedPrefix = preparedParams.length > 0 ? `${preparedParams.join('\n')}\n` : '';
|
|
172
212
|
|
|
173
213
|
return ` @ReactMethod
|
|
174
214
|
override fun ${method.name}(${params}) {
|
|
175
|
-
|
|
215
|
+
${preparedPrefix} BrownfieldNavigationManager.getDelegate().${method.name}(${delegateArgs})
|
|
176
216
|
}`;
|
|
177
217
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { MethodSignature } from '../types.js';
|
|
1
|
+
import type { MethodParam, MethodSignature } from '../types.js';
|
|
2
2
|
|
|
3
3
|
const TS_TO_OBJC_TYPE: Record<string, string> = {
|
|
4
4
|
string: 'NSString *',
|
|
@@ -44,6 +44,19 @@ function mapTsTypeToObjC(
|
|
|
44
44
|
return nullable ? 'id _Nullable' : 'id';
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
function mapParamToObjC(
|
|
48
|
+
param: MethodParam,
|
|
49
|
+
nullable: boolean = false,
|
|
50
|
+
options: ObjCTypeMappingOptions = {}
|
|
51
|
+
): string {
|
|
52
|
+
if (param.callback) {
|
|
53
|
+
return param.optional
|
|
54
|
+
? 'RCTResponseSenderBlock _Nullable'
|
|
55
|
+
: 'RCTResponseSenderBlock';
|
|
56
|
+
}
|
|
57
|
+
return mapTsTypeToObjC(param.type, nullable, options);
|
|
58
|
+
}
|
|
59
|
+
|
|
47
60
|
interface SwiftTypeMappingOptions {
|
|
48
61
|
modelTypeNames?: string[];
|
|
49
62
|
}
|
|
@@ -72,22 +85,46 @@ function mapTsTypeToSwift(
|
|
|
72
85
|
return optional ? 'Any?' : 'Any';
|
|
73
86
|
}
|
|
74
87
|
|
|
88
|
+
/** ObjC-compatible closures that match RCT typedefs without importing React. */
|
|
89
|
+
const SWIFT_RESPONSE_SENDER_BLOCK = '@escaping ([Any]?) -> Void';
|
|
90
|
+
const SWIFT_OPTIONAL_RESPONSE_SENDER_BLOCK = '(([Any]?) -> Void)?';
|
|
91
|
+
const SWIFT_PROMISE_RESOLVE_BLOCK = '@escaping (Any?) -> Void';
|
|
92
|
+
const SWIFT_PROMISE_REJECT_BLOCK =
|
|
93
|
+
'@escaping (String?, String?, (any Error)?) -> Void';
|
|
94
|
+
|
|
95
|
+
function mapParamToSwift(
|
|
96
|
+
param: MethodParam,
|
|
97
|
+
options: SwiftTypeMappingOptions = {}
|
|
98
|
+
): string {
|
|
99
|
+
if (param.callback) {
|
|
100
|
+
return param.optional
|
|
101
|
+
? SWIFT_OPTIONAL_RESPONSE_SENDER_BLOCK
|
|
102
|
+
: SWIFT_RESPONSE_SENDER_BLOCK;
|
|
103
|
+
}
|
|
104
|
+
return mapTsTypeToSwift(param.type, param.optional, options);
|
|
105
|
+
}
|
|
106
|
+
|
|
75
107
|
export function generateSwiftDelegate(
|
|
76
108
|
methods: MethodSignature[],
|
|
77
109
|
options: SwiftTypeMappingOptions = {}
|
|
78
110
|
): string {
|
|
79
111
|
const protocolMethods = methods
|
|
80
112
|
.map((method) => {
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
113
|
+
const methodParams = method.params.map((param, index) => {
|
|
114
|
+
const swiftType = mapParamToSwift(param, options);
|
|
115
|
+
const label = index === 0 ? '_' : param.name;
|
|
116
|
+
return `${label} ${param.name}: ${swiftType}`;
|
|
117
|
+
});
|
|
118
|
+
const promiseParams = method.isAsync
|
|
119
|
+
? [
|
|
120
|
+
`${methodParams.length === 0 ? '_ resolve' : 'resolve'}: ${SWIFT_PROMISE_RESOLVE_BLOCK}`,
|
|
121
|
+
`reject: ${SWIFT_PROMISE_REJECT_BLOCK}`,
|
|
122
|
+
]
|
|
123
|
+
: [];
|
|
124
|
+
const params = [...methodParams, ...promiseParams].join(', ');
|
|
88
125
|
|
|
89
126
|
const returnType =
|
|
90
|
-
method.returnType === 'void'
|
|
127
|
+
method.returnType === 'void' || method.isAsync
|
|
91
128
|
? ''
|
|
92
129
|
: ` -> ${mapTsTypeToSwift(method.returnType, false, options)}`;
|
|
93
130
|
|
|
@@ -142,25 +179,13 @@ ${methodImplementations}
|
|
|
142
179
|
`;
|
|
143
180
|
}
|
|
144
181
|
|
|
145
|
-
function
|
|
182
|
+
function prepareObjCArgs(
|
|
146
183
|
method: MethodSignature,
|
|
147
184
|
options: ObjCGenerationOptions
|
|
148
|
-
): string {
|
|
149
|
-
const { name, params, returnType } = method;
|
|
150
|
-
|
|
151
|
-
let signature = `- (${mapTsTypeToObjC(returnType, false, options)})${name}`;
|
|
152
|
-
if (params.length > 0) {
|
|
153
|
-
signature += params
|
|
154
|
-
.map((param, index) => {
|
|
155
|
-
const prefix = index === 0 ? ':' : ` ${param.name}:`;
|
|
156
|
-
return `${prefix}(${mapTsTypeToObjC(param.type, param.optional, options)})${param.name}`;
|
|
157
|
-
})
|
|
158
|
-
.join('');
|
|
159
|
-
}
|
|
160
|
-
|
|
185
|
+
): { preparedParams: string[]; delegateArgs: string[] } {
|
|
161
186
|
const preparedParams: string[] = [];
|
|
162
187
|
const delegateArgs: string[] = [];
|
|
163
|
-
for (const param of params) {
|
|
188
|
+
for (const param of method.params) {
|
|
164
189
|
if (options.modelTypeNames?.includes(param.type)) {
|
|
165
190
|
const convertedParamName = `${param.name}Model`;
|
|
166
191
|
preparedParams.push(
|
|
@@ -171,7 +196,14 @@ function generateSyncObjCMethod(
|
|
|
171
196
|
delegateArgs.push(param.name);
|
|
172
197
|
}
|
|
173
198
|
}
|
|
199
|
+
return { preparedParams, delegateArgs };
|
|
200
|
+
}
|
|
174
201
|
|
|
202
|
+
function buildObjCDelegateCall(
|
|
203
|
+
name: string,
|
|
204
|
+
params: Array<{ name: string; type: string; optional: boolean }>,
|
|
205
|
+
delegateArgs: string[]
|
|
206
|
+
): string {
|
|
175
207
|
let delegateCall = `[[[BrownfieldNavigationManager shared] getDelegate] ${name}`;
|
|
176
208
|
if (delegateArgs.length > 0) {
|
|
177
209
|
delegateCall += delegateArgs
|
|
@@ -183,6 +215,27 @@ function generateSyncObjCMethod(
|
|
|
183
215
|
.join(' ');
|
|
184
216
|
}
|
|
185
217
|
delegateCall += ']';
|
|
218
|
+
return delegateCall;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function generateSyncObjCMethod(
|
|
222
|
+
method: MethodSignature,
|
|
223
|
+
options: ObjCGenerationOptions
|
|
224
|
+
): string {
|
|
225
|
+
const { name, params, returnType } = method;
|
|
226
|
+
|
|
227
|
+
let signature = `- (${mapTsTypeToObjC(returnType, false, options)})${name}`;
|
|
228
|
+
if (params.length > 0) {
|
|
229
|
+
signature += params
|
|
230
|
+
.map((param, index) => {
|
|
231
|
+
const prefix = index === 0 ? ':' : ` ${param.name}:`;
|
|
232
|
+
return `${prefix}(${mapParamToObjC(param, param.optional, options)})${param.name}`;
|
|
233
|
+
})
|
|
234
|
+
.join('');
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const { preparedParams, delegateArgs } = prepareObjCArgs(method, options);
|
|
238
|
+
const delegateCall = buildObjCDelegateCall(name, params, delegateArgs);
|
|
186
239
|
|
|
187
240
|
const returnPrefix = returnType === 'void' ? '' : 'return ';
|
|
188
241
|
const preparedLines = preparedParams.map((line) => ` ${line}`).join('\n');
|
|
@@ -200,27 +253,33 @@ function generateAsyncObjCMethod(
|
|
|
200
253
|
const { name, params } = method;
|
|
201
254
|
|
|
202
255
|
let signature = `- (void)${name}`;
|
|
203
|
-
const
|
|
204
|
-
...params,
|
|
256
|
+
const promiseParams: Array<{ name: string; type: string; optional: boolean }> = [
|
|
205
257
|
{ name: 'resolve', type: 'RCTPromiseResolveBlock', optional: false },
|
|
206
258
|
{ name: 'reject', type: 'RCTPromiseRejectBlock', optional: false },
|
|
207
259
|
];
|
|
260
|
+
const allParams = [...params, ...promiseParams];
|
|
208
261
|
|
|
209
262
|
signature += ':';
|
|
210
263
|
signature += allParams
|
|
211
264
|
.map((param, index) => {
|
|
212
|
-
const prefix = index === 0 ? '' : param.name
|
|
265
|
+
const prefix = index === 0 ? '' : `${param.name}:`;
|
|
213
266
|
const type =
|
|
214
267
|
param.type === 'RCTPromiseResolveBlock'
|
|
215
268
|
? 'RCTPromiseResolveBlock'
|
|
216
269
|
: param.type === 'RCTPromiseRejectBlock'
|
|
217
270
|
? 'RCTPromiseRejectBlock'
|
|
218
|
-
:
|
|
271
|
+
: mapParamToObjC(param, param.optional, options);
|
|
219
272
|
return `${prefix}(${type})${param.name}`;
|
|
220
273
|
})
|
|
221
274
|
.join(' ');
|
|
222
275
|
|
|
276
|
+
const { preparedParams, delegateArgs } = prepareObjCArgs(method, options);
|
|
277
|
+
const asyncDelegateArgs = [...delegateArgs, 'resolve', 'reject'];
|
|
278
|
+
const delegateCall = buildObjCDelegateCall(name, allParams, asyncDelegateArgs);
|
|
279
|
+
const preparedLines = preparedParams.map((line) => ` ${line}`).join('\n');
|
|
280
|
+
const bodyPrefix = preparedLines.length > 0 ? `${preparedLines}\n` : '';
|
|
281
|
+
|
|
223
282
|
return `${signature} {
|
|
224
|
-
|
|
283
|
+
${bodyPrefix} ${delegateCall};
|
|
225
284
|
}`;
|
|
226
285
|
}
|
package/src/navigation/parser.ts
CHANGED
|
@@ -2,6 +2,7 @@ import fs from 'node:fs';
|
|
|
2
2
|
import { Project } from 'ts-morph';
|
|
3
3
|
|
|
4
4
|
import type {
|
|
5
|
+
CallbackSignature,
|
|
5
6
|
ModelDefinition,
|
|
6
7
|
ModelFieldDefinition,
|
|
7
8
|
MethodParam,
|
|
@@ -10,6 +11,7 @@ import type {
|
|
|
10
11
|
TypeDeclaration,
|
|
11
12
|
} from './types.js';
|
|
12
13
|
import { Node } from 'ts-morph';
|
|
14
|
+
import type { TypeNode } from 'ts-morph';
|
|
13
15
|
|
|
14
16
|
const SKIP_TYPE_TOKENS = new Set([
|
|
15
17
|
'Array',
|
|
@@ -42,6 +44,60 @@ function collectReferencedTypesFromText(typeText: string): string[] {
|
|
|
42
44
|
return matches.filter((match) => !SKIP_TYPE_TOKENS.has(match));
|
|
43
45
|
}
|
|
44
46
|
|
|
47
|
+
function getPromiseInnerType(typeText: string): string | undefined {
|
|
48
|
+
return typeText.startsWith('Promise<') && typeText.endsWith('>')
|
|
49
|
+
? typeText.slice(8, -1)
|
|
50
|
+
: undefined;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function parseCallbackSignature(
|
|
54
|
+
typeNode: TypeNode | undefined
|
|
55
|
+
): CallbackSignature | undefined {
|
|
56
|
+
if (!typeNode || !Node.isFunctionTypeNode(typeNode)) {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
params: typeNode.getParameters().map((param) => ({
|
|
62
|
+
name: param.getName(),
|
|
63
|
+
type: param.getTypeNode()?.getText() ?? 'unknown',
|
|
64
|
+
optional: param.isOptional(),
|
|
65
|
+
})),
|
|
66
|
+
returnType: typeNode.getReturnTypeNode()?.getText() ?? 'void',
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const RESERVED_ASYNC_PARAM_NAMES = new Set(['promise', 'resolve', 'reject']);
|
|
71
|
+
|
|
72
|
+
function validateMethodSignature(method: MethodSignature): void {
|
|
73
|
+
for (const param of method.params) {
|
|
74
|
+
if (getPromiseInnerType(param.type)) {
|
|
75
|
+
throw new Error(
|
|
76
|
+
`Unsupported Promise parameter "${param.name}" in method "${method.name}": Promise<T> is only supported as a method return type.`
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (
|
|
81
|
+
method.isAsync &&
|
|
82
|
+
RESERVED_ASYNC_PARAM_NAMES.has(param.name)
|
|
83
|
+
) {
|
|
84
|
+
throw new Error(
|
|
85
|
+
`Reserved parameter name "${param.name}" in async method "${method.name}": this name is used by the generated bridging code. Rename the parameter.`
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (!param.callback) {
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (param.callback.returnType !== 'void') {
|
|
94
|
+
throw new Error(
|
|
95
|
+
`Unsupported callback parameter "${param.name}" in method "${method.name}": callback return type "${param.callback.returnType}" is not supported. Use a void callback or model the result as a Promise-returning navigation method.`
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
45
101
|
export function parseNavigationSpec(specPath: string): ParsedNavigationSpec {
|
|
46
102
|
if (!fs.existsSync(specPath)) {
|
|
47
103
|
throw new Error(`Spec file not found: ${specPath}`);
|
|
@@ -63,21 +119,32 @@ export function parseNavigationSpec(specPath: string): ParsedNavigationSpec {
|
|
|
63
119
|
const name = method.getName();
|
|
64
120
|
const params: MethodParam[] = method.getParameters().map((param) => {
|
|
65
121
|
const typeNode = param.getTypeNode();
|
|
66
|
-
|
|
122
|
+
const callback = parseCallbackSignature(typeNode);
|
|
123
|
+
const methodParam: MethodParam = {
|
|
67
124
|
name: param.getName(),
|
|
68
125
|
type: typeNode?.getText() ?? 'unknown',
|
|
69
126
|
optional: param.isOptional(),
|
|
70
127
|
};
|
|
128
|
+
if (callback) {
|
|
129
|
+
methodParam.callback = callback;
|
|
130
|
+
}
|
|
131
|
+
return methodParam;
|
|
71
132
|
});
|
|
72
133
|
const returnTypeNode = method.getReturnTypeNode();
|
|
73
134
|
const returnType = returnTypeNode?.getText() ?? 'void';
|
|
135
|
+
const promiseReturnType = getPromiseInnerType(returnType);
|
|
74
136
|
|
|
75
|
-
|
|
137
|
+
const methodSignature: MethodSignature = {
|
|
76
138
|
name,
|
|
77
139
|
params,
|
|
78
140
|
returnType,
|
|
79
|
-
isAsync:
|
|
141
|
+
isAsync: Boolean(promiseReturnType),
|
|
80
142
|
};
|
|
143
|
+
if (promiseReturnType) {
|
|
144
|
+
methodSignature.promiseReturnType = promiseReturnType;
|
|
145
|
+
}
|
|
146
|
+
validateMethodSignature(methodSignature);
|
|
147
|
+
return methodSignature;
|
|
81
148
|
});
|
|
82
149
|
|
|
83
150
|
const declarationNodes = [
|
package/src/navigation/types.ts
CHANGED
|
@@ -1,7 +1,19 @@
|
|
|
1
|
+
export interface CallbackParam {
|
|
2
|
+
name: string;
|
|
3
|
+
type: string;
|
|
4
|
+
optional: boolean;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface CallbackSignature {
|
|
8
|
+
params: CallbackParam[];
|
|
9
|
+
returnType: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
1
12
|
export interface MethodParam {
|
|
2
13
|
name: string;
|
|
3
14
|
type: string;
|
|
4
15
|
optional: boolean;
|
|
16
|
+
callback?: CallbackSignature;
|
|
5
17
|
}
|
|
6
18
|
|
|
7
19
|
export interface MethodSignature {
|
|
@@ -9,6 +21,7 @@ export interface MethodSignature {
|
|
|
9
21
|
params: MethodParam[];
|
|
10
22
|
returnType: string;
|
|
11
23
|
isAsync: boolean;
|
|
24
|
+
promiseReturnType?: string;
|
|
12
25
|
}
|
|
13
26
|
|
|
14
27
|
export interface TypeDeclaration {
|
package/src/types.ts
CHANGED
|
@@ -56,6 +56,16 @@ export type BrownfieldExpoAndroidConfig = {
|
|
|
56
56
|
*/
|
|
57
57
|
packageName?: string;
|
|
58
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Toggle minification for the generated Android library.
|
|
61
|
+
*/
|
|
62
|
+
minifyEnabled?: boolean;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Extra Proguard rules appended to the default rules.
|
|
66
|
+
*/
|
|
67
|
+
extraProguardRules?: string[];
|
|
68
|
+
|
|
59
69
|
/**
|
|
60
70
|
* Minimum SDK version for the Android library.
|
|
61
71
|
*/
|