nitrogen 0.35.0 → 0.35.1-beta.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/lib/autolinking/android/createHybridObjectInitializer.js +27 -18
- package/lib/autolinking/ios/createHybridObjectInitializer.js +28 -19
- package/lib/config/NitroConfig.d.ts +4 -1
- package/lib/config/NitroConfig.js +32 -0
- package/lib/config/NitroUserConfig.d.ts +133 -2
- package/lib/config/NitroUserConfig.js +95 -7
- package/lib/config/getConfig.js +29 -0
- package/lib/syntax/kotlin/FbjniHybridObject.js +2 -2
- package/lib/syntax/kotlin/KotlinCxxBridgedType.d.ts +2 -2
- package/lib/syntax/kotlin/KotlinCxxBridgedType.js +14 -16
- package/lib/syntax/kotlin/KotlinEnum.js +1 -1
- package/lib/syntax/kotlin/KotlinFunction.js +2 -2
- package/lib/syntax/kotlin/KotlinHybridObjectRegistration.js +2 -2
- package/lib/syntax/kotlin/KotlinStruct.js +1 -1
- package/lib/syntax/kotlin/KotlinVariant.js +2 -2
- package/lib/views/kotlin/KotlinHybridViewManager.js +4 -4
- package/lib/views/swift/SwiftHybridViewManager.js +3 -4
- package/package.json +2 -2
- package/src/autolinking/android/createHybridObjectInitializer.ts +30 -18
- package/src/autolinking/ios/createHybridObjectInitializer.ts +31 -19
- package/src/config/NitroConfig.ts +49 -1
- package/src/config/NitroUserConfig.ts +134 -10
- package/src/config/getConfig.ts +38 -0
- package/src/syntax/kotlin/FbjniHybridObject.ts +2 -2
- package/src/syntax/kotlin/KotlinCxxBridgedType.ts +16 -16
- package/src/syntax/kotlin/KotlinEnum.ts +1 -1
- package/src/syntax/kotlin/KotlinFunction.ts +2 -2
- package/src/syntax/kotlin/KotlinHybridObjectRegistration.ts +2 -2
- package/src/syntax/kotlin/KotlinStruct.ts +1 -1
- package/src/syntax/kotlin/KotlinVariant.ts +2 -2
- package/src/views/kotlin/KotlinHybridViewManager.ts +6 -4
- package/src/views/swift/SwiftHybridViewManager.ts +3 -4
|
@@ -19,25 +19,34 @@ export function createHybridObjectIntializer() {
|
|
|
19
19
|
const cppRegistrations = [];
|
|
20
20
|
const cppDefinitions = [];
|
|
21
21
|
for (const hybridObjectName of Object.keys(autolinkedHybridObjects)) {
|
|
22
|
-
const
|
|
23
|
-
if (
|
|
24
|
-
|
|
25
|
-
const { cppCode, requiredImports } = createCppHybridObjectRegistration({
|
|
26
|
-
hybridObjectName: hybridObjectName,
|
|
27
|
-
cppClassName: config.cpp,
|
|
28
|
-
});
|
|
29
|
-
cppHybridObjectImports.push(...requiredImports);
|
|
30
|
-
cppRegistrations.push(cppCode);
|
|
22
|
+
const implementation = NitroConfig.current.getAndroidAutolinkedImplementation(hybridObjectName);
|
|
23
|
+
if (implementation == null) {
|
|
24
|
+
continue;
|
|
31
25
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
26
|
+
switch (implementation.language) {
|
|
27
|
+
case 'cpp': {
|
|
28
|
+
// Autolink a C++ HybridObject!
|
|
29
|
+
const { cppCode, requiredImports } = createCppHybridObjectRegistration({
|
|
30
|
+
hybridObjectName: hybridObjectName,
|
|
31
|
+
cppClassName: implementation.implementationClassName,
|
|
32
|
+
});
|
|
33
|
+
cppHybridObjectImports.push(...requiredImports);
|
|
34
|
+
cppRegistrations.push(cppCode);
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
case 'kotlin': {
|
|
38
|
+
// Autolink a Kotlin HybridObject through JNI/C++!
|
|
39
|
+
const { cppCode, cppDefinition, requiredImports } = createJNIHybridObjectRegistration({
|
|
40
|
+
hybridObjectName: hybridObjectName,
|
|
41
|
+
jniClassName: implementation.implementationClassName,
|
|
42
|
+
});
|
|
43
|
+
cppHybridObjectImports.push(...requiredImports);
|
|
44
|
+
cppDefinitions.push(cppDefinition);
|
|
45
|
+
cppRegistrations.push(cppCode);
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
default:
|
|
49
|
+
throw new Error(`The HybridObject "${hybridObjectName}" cannot be autolinked on Android - Language "${implementation.language}" is not supported on Android!`);
|
|
41
50
|
}
|
|
42
51
|
}
|
|
43
52
|
const buildingWithDefinition = getBuildingWithGeneratedCmakeDefinition();
|
|
@@ -15,26 +15,35 @@ export function createHybridObjectIntializer() {
|
|
|
15
15
|
const cppImports = [];
|
|
16
16
|
let containsSwiftObjects = false;
|
|
17
17
|
for (const hybridObjectName of Object.keys(autolinkedHybridObjects)) {
|
|
18
|
-
const
|
|
19
|
-
if (
|
|
20
|
-
|
|
21
|
-
const { cppCode, requiredImports } = createCppHybridObjectRegistration({
|
|
22
|
-
hybridObjectName: hybridObjectName,
|
|
23
|
-
cppClassName: config.cpp,
|
|
24
|
-
});
|
|
25
|
-
cppImports.push(...requiredImports);
|
|
26
|
-
cppRegistrations.push(cppCode);
|
|
18
|
+
const implementation = NitroConfig.current.getIosAutolinkedImplementation(hybridObjectName);
|
|
19
|
+
if (implementation == null) {
|
|
20
|
+
continue;
|
|
27
21
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
22
|
+
switch (implementation.language) {
|
|
23
|
+
case 'cpp': {
|
|
24
|
+
// Autolink a C++ HybridObject!
|
|
25
|
+
const { cppCode, requiredImports } = createCppHybridObjectRegistration({
|
|
26
|
+
hybridObjectName: hybridObjectName,
|
|
27
|
+
cppClassName: implementation.implementationClassName,
|
|
28
|
+
});
|
|
29
|
+
cppImports.push(...requiredImports);
|
|
30
|
+
cppRegistrations.push(cppCode);
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
case 'swift': {
|
|
34
|
+
// Autolink a Swift HybridObject!
|
|
35
|
+
containsSwiftObjects = true;
|
|
36
|
+
const { cppCode, requiredImports, swiftRegistrationMethods } = createSwiftHybridObjectRegistration({
|
|
37
|
+
hybridObjectName: hybridObjectName,
|
|
38
|
+
swiftClassName: implementation.implementationClassName,
|
|
39
|
+
});
|
|
40
|
+
cppImports.push(...requiredImports);
|
|
41
|
+
cppRegistrations.push(cppCode);
|
|
42
|
+
swiftRegistrations.push(swiftRegistrationMethods);
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
default:
|
|
46
|
+
throw new Error(`The HybridObject "${hybridObjectName}" cannot be autolinked on iOS - Language "${implementation.language}" is not supported on iOS!`);
|
|
38
47
|
}
|
|
39
48
|
}
|
|
40
49
|
if (cppRegistrations.length === 0) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { NitroUserConfig } from './NitroUserConfig.js';
|
|
1
|
+
import type { AutolinkingAndroidImplementation, AutolinkingIOSImplementation, AutolinkingPlatformImplementation, NitroUserConfig } from './NitroUserConfig.js';
|
|
2
2
|
/**
|
|
3
3
|
* Represents the properly parsed `nitro.json` config of the current executing directory.
|
|
4
4
|
*/
|
|
@@ -40,6 +40,9 @@ export declare class NitroConfig {
|
|
|
40
40
|
* Those will be generated and default-constructed.
|
|
41
41
|
*/
|
|
42
42
|
getAutolinkedHybridObjects(): NitroUserConfig['autolinking'];
|
|
43
|
+
getPlatformAutolinkedImplementation(hybridObjectName: string, platform: string): AutolinkingPlatformImplementation | undefined;
|
|
44
|
+
getIosAutolinkedImplementation(hybridObjectName: string): AutolinkingIOSImplementation | undefined;
|
|
45
|
+
getAndroidAutolinkedImplementation(hybridObjectName: string): AutolinkingAndroidImplementation | undefined;
|
|
43
46
|
/**
|
|
44
47
|
* Get the paths that will be ignored when loading the TypeScript project.
|
|
45
48
|
* In most cases, this just contains `node_modules/`.
|
|
@@ -88,6 +88,38 @@ export class NitroConfig {
|
|
|
88
88
|
getAutolinkedHybridObjects() {
|
|
89
89
|
return this.config.autolinking;
|
|
90
90
|
}
|
|
91
|
+
getPlatformAutolinkedImplementation(hybridObjectName, platform) {
|
|
92
|
+
const objectConfig = this.config.autolinking[hybridObjectName];
|
|
93
|
+
if (objectConfig == null) {
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
// "all" applies to every platform and should automatically include
|
|
97
|
+
// future platforms without having to expand explicit key lists here.
|
|
98
|
+
if (objectConfig.all != null) {
|
|
99
|
+
return objectConfig.all;
|
|
100
|
+
}
|
|
101
|
+
return objectConfig[platform];
|
|
102
|
+
}
|
|
103
|
+
getIosAutolinkedImplementation(hybridObjectName) {
|
|
104
|
+
const objectConfig = this.config.autolinking[hybridObjectName];
|
|
105
|
+
if (objectConfig == null) {
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
if (objectConfig.all != null) {
|
|
109
|
+
return objectConfig.all;
|
|
110
|
+
}
|
|
111
|
+
return objectConfig.ios;
|
|
112
|
+
}
|
|
113
|
+
getAndroidAutolinkedImplementation(hybridObjectName) {
|
|
114
|
+
const objectConfig = this.config.autolinking[hybridObjectName];
|
|
115
|
+
if (objectConfig == null) {
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
if (objectConfig.all != null) {
|
|
119
|
+
return objectConfig.all;
|
|
120
|
+
}
|
|
121
|
+
return objectConfig.android;
|
|
122
|
+
}
|
|
91
123
|
/**
|
|
92
124
|
* Get the paths that will be ignored when loading the TypeScript project.
|
|
93
125
|
* In most cases, this just contains `node_modules/`.
|
|
@@ -1,4 +1,87 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
declare const autolinkingAllImplementationSchema: z.ZodObject<{
|
|
3
|
+
language: z.ZodLiteral<"cpp">;
|
|
4
|
+
implementationClassName: z.ZodString;
|
|
5
|
+
}, z.core.$strip>;
|
|
6
|
+
declare const autolinkingIOSImplementationSchema: z.ZodObject<{
|
|
7
|
+
language: z.ZodEnum<{
|
|
8
|
+
cpp: "cpp";
|
|
9
|
+
swift: "swift";
|
|
10
|
+
}>;
|
|
11
|
+
implementationClassName: z.ZodString;
|
|
12
|
+
}, z.core.$strip>;
|
|
13
|
+
declare const autolinkingAndroidImplementationSchema: z.ZodObject<{
|
|
14
|
+
language: z.ZodEnum<{
|
|
15
|
+
cpp: "cpp";
|
|
16
|
+
kotlin: "kotlin";
|
|
17
|
+
}>;
|
|
18
|
+
implementationClassName: z.ZodString;
|
|
19
|
+
}, z.core.$strip>;
|
|
20
|
+
declare const autolinkingPlatformImplementationSchema: z.ZodObject<{
|
|
21
|
+
language: z.ZodEnum<{
|
|
22
|
+
cpp: "cpp";
|
|
23
|
+
swift: "swift";
|
|
24
|
+
kotlin: "kotlin";
|
|
25
|
+
}>;
|
|
26
|
+
implementationClassName: z.ZodString;
|
|
27
|
+
}, z.core.$strip>;
|
|
28
|
+
declare const autolinkingHybridObjectSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
29
|
+
all: z.ZodOptional<z.ZodObject<{
|
|
30
|
+
language: z.ZodLiteral<"cpp">;
|
|
31
|
+
implementationClassName: z.ZodString;
|
|
32
|
+
}, z.core.$strip>>;
|
|
33
|
+
ios: z.ZodOptional<z.ZodObject<{
|
|
34
|
+
language: z.ZodEnum<{
|
|
35
|
+
cpp: "cpp";
|
|
36
|
+
swift: "swift";
|
|
37
|
+
}>;
|
|
38
|
+
implementationClassName: z.ZodString;
|
|
39
|
+
}, z.core.$strip>>;
|
|
40
|
+
android: z.ZodOptional<z.ZodObject<{
|
|
41
|
+
language: z.ZodEnum<{
|
|
42
|
+
cpp: "cpp";
|
|
43
|
+
kotlin: "kotlin";
|
|
44
|
+
}>;
|
|
45
|
+
implementationClassName: z.ZodString;
|
|
46
|
+
}, z.core.$strip>>;
|
|
47
|
+
}, z.core.$catchall<z.ZodObject<{
|
|
48
|
+
language: z.ZodEnum<{
|
|
49
|
+
cpp: "cpp";
|
|
50
|
+
swift: "swift";
|
|
51
|
+
kotlin: "kotlin";
|
|
52
|
+
}>;
|
|
53
|
+
implementationClassName: z.ZodString;
|
|
54
|
+
}, z.core.$strip>>>, z.ZodPipe<z.ZodObject<{
|
|
55
|
+
cpp: z.ZodOptional<z.ZodString>;
|
|
56
|
+
swift: z.ZodOptional<z.ZodString>;
|
|
57
|
+
kotlin: z.ZodOptional<z.ZodString>;
|
|
58
|
+
}, z.core.$strict>, z.ZodTransform<{
|
|
59
|
+
[x: string]: {
|
|
60
|
+
language: "cpp" | "swift" | "kotlin";
|
|
61
|
+
implementationClassName: string;
|
|
62
|
+
};
|
|
63
|
+
all?: {
|
|
64
|
+
language: "cpp";
|
|
65
|
+
implementationClassName: string;
|
|
66
|
+
} | undefined;
|
|
67
|
+
ios?: {
|
|
68
|
+
language: "cpp" | "swift";
|
|
69
|
+
implementationClassName: string;
|
|
70
|
+
} | undefined;
|
|
71
|
+
android?: {
|
|
72
|
+
language: "cpp" | "kotlin";
|
|
73
|
+
implementationClassName: string;
|
|
74
|
+
} | undefined;
|
|
75
|
+
}, {
|
|
76
|
+
cpp?: string | undefined;
|
|
77
|
+
swift?: string | undefined;
|
|
78
|
+
kotlin?: string | undefined;
|
|
79
|
+
}>>]>;
|
|
80
|
+
export type AutolinkingAllImplementation = z.infer<typeof autolinkingAllImplementationSchema>;
|
|
81
|
+
export type AutolinkingIOSImplementation = z.infer<typeof autolinkingIOSImplementationSchema>;
|
|
82
|
+
export type AutolinkingAndroidImplementation = z.infer<typeof autolinkingAndroidImplementationSchema>;
|
|
83
|
+
export type AutolinkingPlatformImplementation = z.infer<typeof autolinkingPlatformImplementationSchema>;
|
|
84
|
+
export type AutolinkingHybridObject = z.infer<typeof autolinkingHybridObjectSchema>;
|
|
2
85
|
export declare const NitroUserConfigSchema: z.ZodObject<{
|
|
3
86
|
cxxNamespace: z.ZodArray<z.ZodString>;
|
|
4
87
|
ios: z.ZodObject<{
|
|
@@ -8,11 +91,58 @@ export declare const NitroUserConfigSchema: z.ZodObject<{
|
|
|
8
91
|
androidNamespace: z.ZodArray<z.ZodString>;
|
|
9
92
|
androidCxxLibName: z.ZodString;
|
|
10
93
|
}, z.core.$strip>;
|
|
11
|
-
autolinking: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
94
|
+
autolinking: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodObject<{
|
|
95
|
+
all: z.ZodOptional<z.ZodObject<{
|
|
96
|
+
language: z.ZodLiteral<"cpp">;
|
|
97
|
+
implementationClassName: z.ZodString;
|
|
98
|
+
}, z.core.$strip>>;
|
|
99
|
+
ios: z.ZodOptional<z.ZodObject<{
|
|
100
|
+
language: z.ZodEnum<{
|
|
101
|
+
cpp: "cpp";
|
|
102
|
+
swift: "swift";
|
|
103
|
+
}>;
|
|
104
|
+
implementationClassName: z.ZodString;
|
|
105
|
+
}, z.core.$strip>>;
|
|
106
|
+
android: z.ZodOptional<z.ZodObject<{
|
|
107
|
+
language: z.ZodEnum<{
|
|
108
|
+
cpp: "cpp";
|
|
109
|
+
kotlin: "kotlin";
|
|
110
|
+
}>;
|
|
111
|
+
implementationClassName: z.ZodString;
|
|
112
|
+
}, z.core.$strip>>;
|
|
113
|
+
}, z.core.$catchall<z.ZodObject<{
|
|
114
|
+
language: z.ZodEnum<{
|
|
115
|
+
cpp: "cpp";
|
|
116
|
+
swift: "swift";
|
|
117
|
+
kotlin: "kotlin";
|
|
118
|
+
}>;
|
|
119
|
+
implementationClassName: z.ZodString;
|
|
120
|
+
}, z.core.$strip>>>, z.ZodPipe<z.ZodObject<{
|
|
12
121
|
cpp: z.ZodOptional<z.ZodString>;
|
|
13
122
|
swift: z.ZodOptional<z.ZodString>;
|
|
14
123
|
kotlin: z.ZodOptional<z.ZodString>;
|
|
15
|
-
}, z.core.$
|
|
124
|
+
}, z.core.$strict>, z.ZodTransform<{
|
|
125
|
+
[x: string]: {
|
|
126
|
+
language: "cpp" | "swift" | "kotlin";
|
|
127
|
+
implementationClassName: string;
|
|
128
|
+
};
|
|
129
|
+
all?: {
|
|
130
|
+
language: "cpp";
|
|
131
|
+
implementationClassName: string;
|
|
132
|
+
} | undefined;
|
|
133
|
+
ios?: {
|
|
134
|
+
language: "cpp" | "swift";
|
|
135
|
+
implementationClassName: string;
|
|
136
|
+
} | undefined;
|
|
137
|
+
android?: {
|
|
138
|
+
language: "cpp" | "kotlin";
|
|
139
|
+
implementationClassName: string;
|
|
140
|
+
} | undefined;
|
|
141
|
+
}, {
|
|
142
|
+
cpp?: string | undefined;
|
|
143
|
+
swift?: string | undefined;
|
|
144
|
+
kotlin?: string | undefined;
|
|
145
|
+
}>>]>>;
|
|
16
146
|
ignorePaths: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
17
147
|
gitAttributesGeneratedFlag: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
18
148
|
}, z.core.$strip>;
|
|
@@ -20,3 +150,4 @@ export declare const NitroUserConfigSchema: z.ZodObject<{
|
|
|
20
150
|
* Represents the structure of a `nitro.json` config file.
|
|
21
151
|
*/
|
|
22
152
|
export type NitroUserConfig = z.infer<typeof NitroUserConfigSchema>;
|
|
153
|
+
export {};
|
|
@@ -5,6 +5,98 @@ const isNotReservedKeyword = (val) => !['core', 'nitro', 'NitroModules'].include
|
|
|
5
5
|
const isReservedKeywordError = {
|
|
6
6
|
message: `This value is reserved and cannot be used!`,
|
|
7
7
|
};
|
|
8
|
+
const autolinkingLanguageSchema = z.enum(['cpp', 'swift', 'kotlin']);
|
|
9
|
+
const autolinkingAllImplementationSchema = z.object({
|
|
10
|
+
language: z.literal('cpp'),
|
|
11
|
+
implementationClassName: z.string(),
|
|
12
|
+
});
|
|
13
|
+
const autolinkingIOSImplementationSchema = z.object({
|
|
14
|
+
language: z.enum(['cpp', 'swift']),
|
|
15
|
+
implementationClassName: z.string(),
|
|
16
|
+
});
|
|
17
|
+
const autolinkingAndroidImplementationSchema = z.object({
|
|
18
|
+
language: z.enum(['cpp', 'kotlin']),
|
|
19
|
+
implementationClassName: z.string(),
|
|
20
|
+
});
|
|
21
|
+
const autolinkingPlatformImplementationSchema = z.object({
|
|
22
|
+
language: autolinkingLanguageSchema,
|
|
23
|
+
implementationClassName: z.string(),
|
|
24
|
+
});
|
|
25
|
+
const autolinkingModernHybridObjectSchema = z
|
|
26
|
+
.object({
|
|
27
|
+
all: autolinkingAllImplementationSchema.optional(),
|
|
28
|
+
ios: autolinkingIOSImplementationSchema.optional(),
|
|
29
|
+
android: autolinkingAndroidImplementationSchema.optional(),
|
|
30
|
+
})
|
|
31
|
+
.catchall(autolinkingPlatformImplementationSchema)
|
|
32
|
+
.superRefine((value, ctx) => {
|
|
33
|
+
const hasAll = value.all != null;
|
|
34
|
+
const platformCount = Object.keys(value).filter((key) => key !== 'all').length;
|
|
35
|
+
if (hasAll && platformCount > 0) {
|
|
36
|
+
ctx.addIssue({
|
|
37
|
+
code: 'custom',
|
|
38
|
+
message: '"all" cannot be combined with platform-specific entries.',
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
if (!hasAll && platformCount === 0) {
|
|
42
|
+
ctx.addIssue({
|
|
43
|
+
code: 'custom',
|
|
44
|
+
message: 'Each autolinking entry must declare either "all" or at least one platform.',
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
const autolinkingLegacyHybridObjectSchema = z
|
|
49
|
+
.object({
|
|
50
|
+
cpp: z.string().optional(),
|
|
51
|
+
swift: z.string().optional(),
|
|
52
|
+
kotlin: z.string().optional(),
|
|
53
|
+
})
|
|
54
|
+
.strict()
|
|
55
|
+
.superRefine((value, ctx) => {
|
|
56
|
+
const hasCpp = value.cpp != null;
|
|
57
|
+
const hasSwift = value.swift != null;
|
|
58
|
+
const hasKotlin = value.kotlin != null;
|
|
59
|
+
if (!hasCpp && !hasSwift && !hasKotlin) {
|
|
60
|
+
ctx.addIssue({
|
|
61
|
+
code: z.ZodIssueCode.custom,
|
|
62
|
+
message: 'Each autolinking entry must declare at least one implementation.',
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
if (hasCpp && (hasSwift || hasKotlin)) {
|
|
66
|
+
ctx.addIssue({
|
|
67
|
+
code: z.ZodIssueCode.custom,
|
|
68
|
+
message: 'Legacy autolinking entries cannot mix "cpp" with "swift"/"kotlin".',
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
function normalizeLegacyAutolinkingHybridObject(value) {
|
|
73
|
+
if (value.cpp != null) {
|
|
74
|
+
return {
|
|
75
|
+
all: {
|
|
76
|
+
language: 'cpp',
|
|
77
|
+
implementationClassName: value.cpp,
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
const normalized = {};
|
|
82
|
+
if (value.swift != null) {
|
|
83
|
+
normalized.ios = {
|
|
84
|
+
language: 'swift',
|
|
85
|
+
implementationClassName: value.swift,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
if (value.kotlin != null) {
|
|
89
|
+
normalized.android = {
|
|
90
|
+
language: 'kotlin',
|
|
91
|
+
implementationClassName: value.kotlin,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
return normalized;
|
|
95
|
+
}
|
|
96
|
+
const autolinkingHybridObjectSchema = z.union([
|
|
97
|
+
autolinkingModernHybridObjectSchema,
|
|
98
|
+
autolinkingLegacyHybridObjectSchema.transform(normalizeLegacyAutolinkingHybridObject),
|
|
99
|
+
]);
|
|
8
100
|
export const NitroUserConfigSchema = z.object({
|
|
9
101
|
/**
|
|
10
102
|
* Represents the C++ namespace of the module that will be generated.
|
|
@@ -65,14 +157,10 @@ export const NitroUserConfigSchema = z.object({
|
|
|
65
157
|
* Configures the code that gets generated for autolinking (registering)
|
|
66
158
|
* Hybrid Object constructors.
|
|
67
159
|
*
|
|
68
|
-
* Each class listed here needs to have a default constructor/initializer
|
|
69
|
-
* zero arguments.
|
|
160
|
+
* Each class listed here needs to have a default constructor/initializer
|
|
161
|
+
* that takes zero arguments.
|
|
70
162
|
*/
|
|
71
|
-
autolinking: z.record(z.string(),
|
|
72
|
-
cpp: z.string().optional(),
|
|
73
|
-
swift: z.string().optional(),
|
|
74
|
-
kotlin: z.string().optional(),
|
|
75
|
-
})),
|
|
163
|
+
autolinking: z.record(z.string(), autolinkingHybridObjectSchema),
|
|
76
164
|
/**
|
|
77
165
|
* A list of paths relative to the project directory that should be ignored by nitrogen.
|
|
78
166
|
* Nitrogen will not look for `.nitro.ts` files in these directories.
|
package/lib/config/getConfig.js
CHANGED
|
@@ -2,6 +2,34 @@ import { ZodError } from 'zod';
|
|
|
2
2
|
import fs from 'fs';
|
|
3
3
|
import { NitroUserConfigSchema, } from './NitroUserConfig.js';
|
|
4
4
|
import chalk from 'chalk';
|
|
5
|
+
function isObject(value) {
|
|
6
|
+
return typeof value === 'object' && value != null && !Array.isArray(value);
|
|
7
|
+
}
|
|
8
|
+
function getLegacyAutolinkingEntries(config) {
|
|
9
|
+
if (!isObject(config))
|
|
10
|
+
return [];
|
|
11
|
+
const autolinking = config.autolinking;
|
|
12
|
+
if (!isObject(autolinking))
|
|
13
|
+
return [];
|
|
14
|
+
const entries = [];
|
|
15
|
+
for (const [hybridObjectName, entry] of Object.entries(autolinking)) {
|
|
16
|
+
if (!isObject(entry))
|
|
17
|
+
continue;
|
|
18
|
+
const hasLegacySyntax = 'cpp' in entry || 'swift' in entry || 'kotlin' in entry;
|
|
19
|
+
if (hasLegacySyntax) {
|
|
20
|
+
entries.push(hybridObjectName);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return entries;
|
|
24
|
+
}
|
|
25
|
+
// TODO: Remove this once users have migrated to new `nitro.json`
|
|
26
|
+
function logLegacyAutolinkingDeprecation(config) {
|
|
27
|
+
const legacyEntries = getLegacyAutolinkingEntries(config);
|
|
28
|
+
if (legacyEntries.length === 0)
|
|
29
|
+
return;
|
|
30
|
+
console.warn(chalk.yellow(`Warning: nitro.json uses deprecated autolinking syntax ("cpp"/"swift"/"kotlin") for [${legacyEntries.join(', ')}]. ` +
|
|
31
|
+
`Use "all"/"ios"/"android" entries with { language, implementationClassName } instead.`));
|
|
32
|
+
}
|
|
5
33
|
function readFile(configPath) {
|
|
6
34
|
try {
|
|
7
35
|
return fs.readFileSync(configPath, 'utf8');
|
|
@@ -52,6 +80,7 @@ function parseConfig(json) {
|
|
|
52
80
|
});
|
|
53
81
|
}
|
|
54
82
|
try {
|
|
83
|
+
logLegacyAutolinkingDeprecation(object);
|
|
55
84
|
return NitroUserConfigSchema.parse(object);
|
|
56
85
|
}
|
|
57
86
|
catch (error) {
|
|
@@ -84,11 +84,11 @@ namespace ${cxxNamespace} {
|
|
|
84
84
|
class ${name.JHybridTSpec}: public virtual ${name.HybridTSpec}, public virtual ${cppBaseClass} {
|
|
85
85
|
public:
|
|
86
86
|
struct JavaPart: public jni::JavaClass<JavaPart, ${javaPartBaseClass}> {
|
|
87
|
-
static auto
|
|
87
|
+
static constexpr auto kJavaDescriptor = "L${jniClassDescriptor};";
|
|
88
88
|
std::shared_ptr<${name.JHybridTSpec}> get${name.JHybridTSpec}();
|
|
89
89
|
};
|
|
90
90
|
struct CxxPart: public jni::HybridClass<CxxPart, ${cxxPartBaseClass}> {
|
|
91
|
-
static auto
|
|
91
|
+
static constexpr auto kJavaDescriptor = "L${cxxPartJniClassDescriptor};";
|
|
92
92
|
static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jhybridobject> jThis);
|
|
93
93
|
static void registerNatives();
|
|
94
94
|
using HybridBase::HybridBase;
|
|
@@ -8,8 +8,8 @@ export declare class KotlinCxxBridgedType implements BridgedType<'kotlin', 'c++'
|
|
|
8
8
|
get hasType(): boolean;
|
|
9
9
|
get canBePassedByReference(): boolean;
|
|
10
10
|
get needsSpecialHandling(): boolean;
|
|
11
|
-
getRequiredImports(language: Language): SourceImport[];
|
|
12
|
-
getExtraFiles(): SourceFile[];
|
|
11
|
+
getRequiredImports(language: Language, visited?: Set<Type>): SourceImport[];
|
|
12
|
+
getExtraFiles(visited?: Set<Type>): SourceFile[];
|
|
13
13
|
asJniReferenceType(referenceType?: 'alias' | 'local' | 'global'): string;
|
|
14
14
|
getTypeCode(language: 'kotlin' | 'c++', isBoxed?: boolean): string;
|
|
15
15
|
parse(parameterName: string, from: 'c++' | 'kotlin', to: 'kotlin' | 'c++', inLanguage: 'kotlin' | 'c++'): string;
|
|
@@ -56,7 +56,10 @@ export class KotlinCxxBridgedType {
|
|
|
56
56
|
// no special handling needed
|
|
57
57
|
return false;
|
|
58
58
|
}
|
|
59
|
-
getRequiredImports(language) {
|
|
59
|
+
getRequiredImports(language, visited = new Set()) {
|
|
60
|
+
if (visited.has(this.type))
|
|
61
|
+
return [];
|
|
62
|
+
visited.add(this.type);
|
|
60
63
|
const imports = this.type.getRequiredImports(language);
|
|
61
64
|
if (language === 'c++') {
|
|
62
65
|
// All C++ imports we need for the JNI bridge
|
|
@@ -168,17 +171,16 @@ export class KotlinCxxBridgedType {
|
|
|
168
171
|
}
|
|
169
172
|
// Recursively look into referenced types (e.g. the `T` of a `optional<T>`, or `T` of a `T[]`)
|
|
170
173
|
const referencedTypes = getReferencedTypes(this.type);
|
|
171
|
-
|
|
172
|
-
if (t === this.type) {
|
|
173
|
-
// break a recursion - we already know this type
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
174
|
+
for (const t of referencedTypes) {
|
|
176
175
|
const bridged = new KotlinCxxBridgedType(t);
|
|
177
|
-
imports.push(...bridged.getRequiredImports(language));
|
|
178
|
-
}
|
|
176
|
+
imports.push(...bridged.getRequiredImports(language, visited));
|
|
177
|
+
}
|
|
179
178
|
return imports;
|
|
180
179
|
}
|
|
181
|
-
getExtraFiles() {
|
|
180
|
+
getExtraFiles(visited = new Set()) {
|
|
181
|
+
if (visited.has(this.type))
|
|
182
|
+
return [];
|
|
183
|
+
visited.add(this.type);
|
|
182
184
|
const files = [];
|
|
183
185
|
switch (this.type.kind) {
|
|
184
186
|
case 'enum':
|
|
@@ -204,14 +206,10 @@ export class KotlinCxxBridgedType {
|
|
|
204
206
|
}
|
|
205
207
|
// Recursively look into referenced types (e.g. the `T` of a `optional<T>`, or `T` of a `T[]`)
|
|
206
208
|
const referencedTypes = getReferencedTypes(this.type);
|
|
207
|
-
|
|
208
|
-
if (t === this.type) {
|
|
209
|
-
// break a recursion - we already know this type
|
|
210
|
-
return;
|
|
211
|
-
}
|
|
209
|
+
for (const t of referencedTypes) {
|
|
212
210
|
const bridged = new KotlinCxxBridgedType(t);
|
|
213
|
-
files.push(...bridged.getExtraFiles());
|
|
214
|
-
}
|
|
211
|
+
files.push(...bridged.getExtraFiles(visited));
|
|
212
|
+
}
|
|
215
213
|
return files;
|
|
216
214
|
}
|
|
217
215
|
asJniReferenceType(referenceType = 'alias') {
|
|
@@ -44,7 +44,7 @@ namespace ${cxxNamespace} {
|
|
|
44
44
|
*/
|
|
45
45
|
struct J${enumType.enumName} final: public jni::JavaClass<J${enumType.enumName}> {
|
|
46
46
|
public:
|
|
47
|
-
static auto
|
|
47
|
+
static constexpr auto kJavaDescriptor = "L${jniClassDescriptor};";
|
|
48
48
|
|
|
49
49
|
public:
|
|
50
50
|
/**
|
|
@@ -180,7 +180,7 @@ namespace ${cxxNamespace} {
|
|
|
180
180
|
*/
|
|
181
181
|
struct J${name}: public jni::JavaClass<J${name}> {
|
|
182
182
|
public:
|
|
183
|
-
static auto
|
|
183
|
+
static constexpr auto kJavaDescriptor = "L${jniInterfaceDescriptor};";
|
|
184
184
|
|
|
185
185
|
public:
|
|
186
186
|
/**
|
|
@@ -215,7 +215,7 @@ namespace ${cxxNamespace} {
|
|
|
215
215
|
}
|
|
216
216
|
|
|
217
217
|
public:
|
|
218
|
-
static auto
|
|
218
|
+
static constexpr auto kJavaDescriptor = "L${jniClassDescriptor};";
|
|
219
219
|
static void registerNatives() {
|
|
220
220
|
registerHybrid({makeNativeMethod("invoke_cxx", J${name}_cxx::invoke_cxx)});
|
|
221
221
|
}
|
|
@@ -14,9 +14,9 @@ export function createJNIHybridObjectRegistration({ hybridObjectName, jniClassNa
|
|
|
14
14
|
],
|
|
15
15
|
cppDefinition: `
|
|
16
16
|
struct ${JHybridTSpec}Impl: public jni::JavaClass<${JHybridTSpec}Impl, ${JHybridTSpec}::JavaPart> {
|
|
17
|
-
static auto
|
|
17
|
+
static constexpr auto kJavaDescriptor = "L${jniNamespace};";
|
|
18
18
|
static std::shared_ptr<${JHybridTSpec}> create() {
|
|
19
|
-
static auto constructorFn = javaClassStatic()->getConstructor<${JHybridTSpec}Impl::javaobject()>();
|
|
19
|
+
static const auto constructorFn = javaClassStatic()->getConstructor<${JHybridTSpec}Impl::javaobject()>();
|
|
20
20
|
jni::local_ref<${JHybridTSpec}::JavaPart> javaPart = javaClassStatic()->newObject(constructorFn);
|
|
21
21
|
return javaPart->get${JHybridTSpec}();
|
|
22
22
|
}
|
|
@@ -96,7 +96,7 @@ namespace ${cxxNamespace} {
|
|
|
96
96
|
*/
|
|
97
97
|
struct J${structType.structName} final: public jni::JavaClass<J${structType.structName}> {
|
|
98
98
|
public:
|
|
99
|
-
static auto
|
|
99
|
+
static constexpr auto kJavaDescriptor = "L${jniClassDescriptor};";
|
|
100
100
|
|
|
101
101
|
public:
|
|
102
102
|
/**
|
|
@@ -127,7 +127,7 @@ if (isInstanceOf(${namespace}::${innerName}::javaClassStatic())) {
|
|
|
127
127
|
return `
|
|
128
128
|
class ${innerName} final: public jni::JavaClass<${innerName}, J${kotlinName}> {
|
|
129
129
|
public:
|
|
130
|
-
static auto
|
|
130
|
+
static constexpr auto kJavaDescriptor = "L${descriptor};";
|
|
131
131
|
|
|
132
132
|
[[nodiscard]] ${bridge.asJniReferenceType('local')} getValue() const {
|
|
133
133
|
static const auto field = javaClassStatic()->getField<${bridge.getTypeCode('c++')}>("value");
|
|
@@ -160,7 +160,7 @@ namespace ${cxxNamespace} {
|
|
|
160
160
|
*/
|
|
161
161
|
class J${kotlinName}: public jni::JavaClass<J${kotlinName}> {
|
|
162
162
|
public:
|
|
163
|
-
static auto
|
|
163
|
+
static constexpr auto kJavaDescriptor = "L${jniClassDescriptor};";
|
|
164
164
|
|
|
165
165
|
${indent(cppCreateFuncs.join('\n'), ' ')}
|
|
166
166
|
|
|
@@ -11,11 +11,11 @@ export function createKotlinHybridViewManager(spec) {
|
|
|
11
11
|
const { JHybridTSpec, HybridTSpec } = getHybridObjectName(spec.name);
|
|
12
12
|
const { manager, stateClassName, component, propsClassName, descriptorClassName, } = getViewComponentNames(spec);
|
|
13
13
|
const stateUpdaterName = `${stateClassName}Updater`;
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
throw new Error(`Cannot create Kotlin HybridView ViewManager for ${spec.name} - it is not autolinked in nitro.json!`);
|
|
14
|
+
const implementation = spec.config.getAndroidAutolinkedImplementation(spec.name);
|
|
15
|
+
if (implementation?.language !== 'kotlin') {
|
|
16
|
+
throw new Error(`Cannot create Kotlin HybridView ViewManager for ${spec.name} - it must be autolinked with a Kotlin Android implementation in nitro.json!`);
|
|
18
17
|
}
|
|
18
|
+
const viewImplementation = implementation.implementationClassName;
|
|
19
19
|
const viewManagerCode = `
|
|
20
20
|
${createFileMetadataString(`${manager}.kt`)}
|
|
21
21
|
|
|
@@ -11,10 +11,9 @@ export function createSwiftHybridViewManager(spec) {
|
|
|
11
11
|
const swiftNamespace = spec.config.getIosModuleName();
|
|
12
12
|
const { HybridTSpec, HybridTSpecSwift, HybridTSpecCxx } = getHybridObjectName(spec.name);
|
|
13
13
|
const { component, descriptorClassName, propsClassName } = getViewComponentNames(spec);
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
throw new Error(`Cannot create Swift HybridView ViewManager for ${spec.name} - it is not autolinked in nitro.json!`);
|
|
14
|
+
const implementation = spec.config.getIosAutolinkedImplementation(spec.name);
|
|
15
|
+
if (implementation?.language !== 'swift') {
|
|
16
|
+
throw new Error(`Cannot create Swift HybridView ViewManager for ${spec.name} - it must be autolinked with a Swift iOS implementation in nitro.json!`);
|
|
18
17
|
}
|
|
19
18
|
const propAssignments = spec.properties.map((p) => {
|
|
20
19
|
const name = escapeCppName(p.name);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nitrogen",
|
|
3
|
-
"version": "0.35.0",
|
|
3
|
+
"version": "0.35.1-beta.0",
|
|
4
4
|
"description": "The code-generator for react-native-nitro-modules.",
|
|
5
5
|
"main": "lib/index",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"chalk": "^5.3.0",
|
|
38
|
-
"react-native-nitro-modules": "^0.35.0",
|
|
38
|
+
"react-native-nitro-modules": "^0.35.1-beta.0",
|
|
39
39
|
"ts-morph": "^27.0.0",
|
|
40
40
|
"yargs": "^18.0.0",
|
|
41
41
|
"zod": "^4.0.5"
|
|
@@ -28,27 +28,39 @@ export function createHybridObjectIntializer(): SourceFile[] {
|
|
|
28
28
|
const cppRegistrations: string[] = []
|
|
29
29
|
const cppDefinitions: string[] = []
|
|
30
30
|
for (const hybridObjectName of Object.keys(autolinkedHybridObjects)) {
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
if (
|
|
34
|
-
|
|
35
|
-
const { cppCode, requiredImports } = createCppHybridObjectRegistration({
|
|
36
|
-
hybridObjectName: hybridObjectName,
|
|
37
|
-
cppClassName: config.cpp,
|
|
38
|
-
})
|
|
39
|
-
cppHybridObjectImports.push(...requiredImports)
|
|
40
|
-
cppRegistrations.push(cppCode)
|
|
31
|
+
const implementation =
|
|
32
|
+
NitroConfig.current.getAndroidAutolinkedImplementation(hybridObjectName)
|
|
33
|
+
if (implementation == null) {
|
|
34
|
+
continue
|
|
41
35
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
36
|
+
|
|
37
|
+
switch (implementation.language) {
|
|
38
|
+
case 'cpp': {
|
|
39
|
+
// Autolink a C++ HybridObject!
|
|
40
|
+
const { cppCode, requiredImports } = createCppHybridObjectRegistration({
|
|
46
41
|
hybridObjectName: hybridObjectName,
|
|
47
|
-
|
|
42
|
+
cppClassName: implementation.implementationClassName,
|
|
48
43
|
})
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
44
|
+
cppHybridObjectImports.push(...requiredImports)
|
|
45
|
+
cppRegistrations.push(cppCode)
|
|
46
|
+
break
|
|
47
|
+
}
|
|
48
|
+
case 'kotlin': {
|
|
49
|
+
// Autolink a Kotlin HybridObject through JNI/C++!
|
|
50
|
+
const { cppCode, cppDefinition, requiredImports } =
|
|
51
|
+
createJNIHybridObjectRegistration({
|
|
52
|
+
hybridObjectName: hybridObjectName,
|
|
53
|
+
jniClassName: implementation.implementationClassName,
|
|
54
|
+
})
|
|
55
|
+
cppHybridObjectImports.push(...requiredImports)
|
|
56
|
+
cppDefinitions.push(cppDefinition)
|
|
57
|
+
cppRegistrations.push(cppCode)
|
|
58
|
+
break
|
|
59
|
+
}
|
|
60
|
+
default:
|
|
61
|
+
throw new Error(
|
|
62
|
+
`The HybridObject "${hybridObjectName}" cannot be autolinked on Android - Language "${implementation.language}" is not supported on Android!`
|
|
63
|
+
)
|
|
52
64
|
}
|
|
53
65
|
}
|
|
54
66
|
|
|
@@ -23,28 +23,40 @@ export function createHybridObjectIntializer(): [ObjcFile, SwiftFile] | [] {
|
|
|
23
23
|
const cppImports: SourceImport[] = []
|
|
24
24
|
let containsSwiftObjects = false
|
|
25
25
|
for (const hybridObjectName of Object.keys(autolinkedHybridObjects)) {
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
if (
|
|
29
|
-
|
|
30
|
-
const { cppCode, requiredImports } = createCppHybridObjectRegistration({
|
|
31
|
-
hybridObjectName: hybridObjectName,
|
|
32
|
-
cppClassName: config.cpp,
|
|
33
|
-
})
|
|
34
|
-
cppImports.push(...requiredImports)
|
|
35
|
-
cppRegistrations.push(cppCode)
|
|
26
|
+
const implementation =
|
|
27
|
+
NitroConfig.current.getIosAutolinkedImplementation(hybridObjectName)
|
|
28
|
+
if (implementation == null) {
|
|
29
|
+
continue
|
|
36
30
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
31
|
+
|
|
32
|
+
switch (implementation.language) {
|
|
33
|
+
case 'cpp': {
|
|
34
|
+
// Autolink a C++ HybridObject!
|
|
35
|
+
const { cppCode, requiredImports } = createCppHybridObjectRegistration({
|
|
42
36
|
hybridObjectName: hybridObjectName,
|
|
43
|
-
|
|
37
|
+
cppClassName: implementation.implementationClassName,
|
|
44
38
|
})
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
39
|
+
cppImports.push(...requiredImports)
|
|
40
|
+
cppRegistrations.push(cppCode)
|
|
41
|
+
break
|
|
42
|
+
}
|
|
43
|
+
case 'swift': {
|
|
44
|
+
// Autolink a Swift HybridObject!
|
|
45
|
+
containsSwiftObjects = true
|
|
46
|
+
const { cppCode, requiredImports, swiftRegistrationMethods } =
|
|
47
|
+
createSwiftHybridObjectRegistration({
|
|
48
|
+
hybridObjectName: hybridObjectName,
|
|
49
|
+
swiftClassName: implementation.implementationClassName,
|
|
50
|
+
})
|
|
51
|
+
cppImports.push(...requiredImports)
|
|
52
|
+
cppRegistrations.push(cppCode)
|
|
53
|
+
swiftRegistrations.push(swiftRegistrationMethods)
|
|
54
|
+
break
|
|
55
|
+
}
|
|
56
|
+
default:
|
|
57
|
+
throw new Error(
|
|
58
|
+
`The HybridObject "${hybridObjectName}" cannot be autolinked on iOS - Language "${implementation.language}" is not supported on iOS!`
|
|
59
|
+
)
|
|
48
60
|
}
|
|
49
61
|
}
|
|
50
62
|
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import chalk from 'chalk'
|
|
2
2
|
import { readUserConfig } from './getConfig.js'
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
AutolinkingAndroidImplementation,
|
|
5
|
+
AutolinkingIOSImplementation,
|
|
6
|
+
AutolinkingPlatformImplementation,
|
|
7
|
+
NitroUserConfig,
|
|
8
|
+
} from './NitroUserConfig.js'
|
|
4
9
|
|
|
5
10
|
const CXX_BASE_NAMESPACE = ['margelo', 'nitro']
|
|
6
11
|
const ANDROID_BASE_NAMESPACE = ['com', 'margelo', 'nitro']
|
|
@@ -109,6 +114,49 @@ export class NitroConfig {
|
|
|
109
114
|
return this.config.autolinking
|
|
110
115
|
}
|
|
111
116
|
|
|
117
|
+
getPlatformAutolinkedImplementation(
|
|
118
|
+
hybridObjectName: string,
|
|
119
|
+
platform: string
|
|
120
|
+
): AutolinkingPlatformImplementation | undefined {
|
|
121
|
+
const objectConfig = this.config.autolinking[hybridObjectName]
|
|
122
|
+
if (objectConfig == null) {
|
|
123
|
+
return undefined
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// "all" applies to every platform and should automatically include
|
|
127
|
+
// future platforms without having to expand explicit key lists here.
|
|
128
|
+
if (objectConfig.all != null) {
|
|
129
|
+
return objectConfig.all
|
|
130
|
+
}
|
|
131
|
+
return objectConfig[platform]
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
getIosAutolinkedImplementation(
|
|
135
|
+
hybridObjectName: string
|
|
136
|
+
): AutolinkingIOSImplementation | undefined {
|
|
137
|
+
const objectConfig = this.config.autolinking[hybridObjectName]
|
|
138
|
+
if (objectConfig == null) {
|
|
139
|
+
return undefined
|
|
140
|
+
}
|
|
141
|
+
if (objectConfig.all != null) {
|
|
142
|
+
return objectConfig.all
|
|
143
|
+
}
|
|
144
|
+
return objectConfig.ios
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
getAndroidAutolinkedImplementation(
|
|
148
|
+
hybridObjectName: string
|
|
149
|
+
): AutolinkingAndroidImplementation | undefined {
|
|
150
|
+
const objectConfig = this.config.autolinking[hybridObjectName]
|
|
151
|
+
if (objectConfig == null) {
|
|
152
|
+
return undefined
|
|
153
|
+
}
|
|
154
|
+
if (objectConfig.all != null) {
|
|
155
|
+
return objectConfig.all
|
|
156
|
+
}
|
|
157
|
+
return objectConfig.android
|
|
158
|
+
}
|
|
159
|
+
|
|
112
160
|
/**
|
|
113
161
|
* Get the paths that will be ignored when loading the TypeScript project.
|
|
114
162
|
* In most cases, this just contains `node_modules/`.
|
|
@@ -9,6 +9,137 @@ const isReservedKeywordError = {
|
|
|
9
9
|
message: `This value is reserved and cannot be used!`,
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
const autolinkingLanguageSchema = z.enum(['cpp', 'swift', 'kotlin'])
|
|
13
|
+
|
|
14
|
+
const autolinkingAllImplementationSchema = z.object({
|
|
15
|
+
language: z.literal('cpp'),
|
|
16
|
+
implementationClassName: z.string(),
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
const autolinkingIOSImplementationSchema = z.object({
|
|
20
|
+
language: z.enum(['cpp', 'swift']),
|
|
21
|
+
implementationClassName: z.string(),
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
const autolinkingAndroidImplementationSchema = z.object({
|
|
25
|
+
language: z.enum(['cpp', 'kotlin']),
|
|
26
|
+
implementationClassName: z.string(),
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
const autolinkingPlatformImplementationSchema = z.object({
|
|
30
|
+
language: autolinkingLanguageSchema,
|
|
31
|
+
implementationClassName: z.string(),
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
const autolinkingModernHybridObjectSchema = z
|
|
35
|
+
.object({
|
|
36
|
+
all: autolinkingAllImplementationSchema.optional(),
|
|
37
|
+
ios: autolinkingIOSImplementationSchema.optional(),
|
|
38
|
+
android: autolinkingAndroidImplementationSchema.optional(),
|
|
39
|
+
})
|
|
40
|
+
.catchall(autolinkingPlatformImplementationSchema)
|
|
41
|
+
.superRefine((value, ctx) => {
|
|
42
|
+
const hasAll = value.all != null
|
|
43
|
+
const platformCount = Object.keys(value).filter(
|
|
44
|
+
(key) => key !== 'all'
|
|
45
|
+
).length
|
|
46
|
+
|
|
47
|
+
if (hasAll && platformCount > 0) {
|
|
48
|
+
ctx.addIssue({
|
|
49
|
+
code: 'custom',
|
|
50
|
+
message: '"all" cannot be combined with platform-specific entries.',
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (!hasAll && platformCount === 0) {
|
|
55
|
+
ctx.addIssue({
|
|
56
|
+
code: 'custom',
|
|
57
|
+
message:
|
|
58
|
+
'Each autolinking entry must declare either "all" or at least one platform.',
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
const autolinkingLegacyHybridObjectSchema = z
|
|
64
|
+
.object({
|
|
65
|
+
cpp: z.string().optional(),
|
|
66
|
+
swift: z.string().optional(),
|
|
67
|
+
kotlin: z.string().optional(),
|
|
68
|
+
})
|
|
69
|
+
.strict()
|
|
70
|
+
.superRefine((value, ctx) => {
|
|
71
|
+
const hasCpp = value.cpp != null
|
|
72
|
+
const hasSwift = value.swift != null
|
|
73
|
+
const hasKotlin = value.kotlin != null
|
|
74
|
+
|
|
75
|
+
if (!hasCpp && !hasSwift && !hasKotlin) {
|
|
76
|
+
ctx.addIssue({
|
|
77
|
+
code: z.ZodIssueCode.custom,
|
|
78
|
+
message:
|
|
79
|
+
'Each autolinking entry must declare at least one implementation.',
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (hasCpp && (hasSwift || hasKotlin)) {
|
|
84
|
+
ctx.addIssue({
|
|
85
|
+
code: z.ZodIssueCode.custom,
|
|
86
|
+
message:
|
|
87
|
+
'Legacy autolinking entries cannot mix "cpp" with "swift"/"kotlin".',
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
function normalizeLegacyAutolinkingHybridObject(
|
|
93
|
+
value: z.infer<typeof autolinkingLegacyHybridObjectSchema>
|
|
94
|
+
): z.infer<typeof autolinkingModernHybridObjectSchema> {
|
|
95
|
+
if (value.cpp != null) {
|
|
96
|
+
return {
|
|
97
|
+
all: {
|
|
98
|
+
language: 'cpp',
|
|
99
|
+
implementationClassName: value.cpp,
|
|
100
|
+
},
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const normalized: z.infer<typeof autolinkingModernHybridObjectSchema> = {}
|
|
105
|
+
if (value.swift != null) {
|
|
106
|
+
normalized.ios = {
|
|
107
|
+
language: 'swift',
|
|
108
|
+
implementationClassName: value.swift,
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (value.kotlin != null) {
|
|
112
|
+
normalized.android = {
|
|
113
|
+
language: 'kotlin',
|
|
114
|
+
implementationClassName: value.kotlin,
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return normalized
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const autolinkingHybridObjectSchema = z.union([
|
|
121
|
+
autolinkingModernHybridObjectSchema,
|
|
122
|
+
autolinkingLegacyHybridObjectSchema.transform(
|
|
123
|
+
normalizeLegacyAutolinkingHybridObject
|
|
124
|
+
),
|
|
125
|
+
])
|
|
126
|
+
|
|
127
|
+
export type AutolinkingAllImplementation = z.infer<
|
|
128
|
+
typeof autolinkingAllImplementationSchema
|
|
129
|
+
>
|
|
130
|
+
export type AutolinkingIOSImplementation = z.infer<
|
|
131
|
+
typeof autolinkingIOSImplementationSchema
|
|
132
|
+
>
|
|
133
|
+
export type AutolinkingAndroidImplementation = z.infer<
|
|
134
|
+
typeof autolinkingAndroidImplementationSchema
|
|
135
|
+
>
|
|
136
|
+
export type AutolinkingPlatformImplementation = z.infer<
|
|
137
|
+
typeof autolinkingPlatformImplementationSchema
|
|
138
|
+
>
|
|
139
|
+
export type AutolinkingHybridObject = z.infer<
|
|
140
|
+
typeof autolinkingHybridObjectSchema
|
|
141
|
+
>
|
|
142
|
+
|
|
12
143
|
export const NitroUserConfigSchema = z.object({
|
|
13
144
|
/**
|
|
14
145
|
* Represents the C++ namespace of the module that will be generated.
|
|
@@ -74,17 +205,10 @@ export const NitroUserConfigSchema = z.object({
|
|
|
74
205
|
* Configures the code that gets generated for autolinking (registering)
|
|
75
206
|
* Hybrid Object constructors.
|
|
76
207
|
*
|
|
77
|
-
* Each class listed here needs to have a default constructor/initializer
|
|
78
|
-
* zero arguments.
|
|
208
|
+
* Each class listed here needs to have a default constructor/initializer
|
|
209
|
+
* that takes zero arguments.
|
|
79
210
|
*/
|
|
80
|
-
autolinking: z.record(
|
|
81
|
-
z.string(),
|
|
82
|
-
z.object({
|
|
83
|
-
cpp: z.string().optional(),
|
|
84
|
-
swift: z.string().optional(),
|
|
85
|
-
kotlin: z.string().optional(),
|
|
86
|
-
})
|
|
87
|
-
),
|
|
211
|
+
autolinking: z.record(z.string(), autolinkingHybridObjectSchema),
|
|
88
212
|
/**
|
|
89
213
|
* A list of paths relative to the project directory that should be ignored by nitrogen.
|
|
90
214
|
* Nitrogen will not look for `.nitro.ts` files in these directories.
|
package/src/config/getConfig.ts
CHANGED
|
@@ -6,6 +6,43 @@ import {
|
|
|
6
6
|
} from './NitroUserConfig.js'
|
|
7
7
|
import chalk from 'chalk'
|
|
8
8
|
|
|
9
|
+
function isObject(value: unknown): value is Record<string, unknown> {
|
|
10
|
+
return typeof value === 'object' && value != null && !Array.isArray(value)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function getLegacyAutolinkingEntries(config: unknown): string[] {
|
|
14
|
+
if (!isObject(config)) return []
|
|
15
|
+
|
|
16
|
+
const autolinking = config.autolinking
|
|
17
|
+
if (!isObject(autolinking)) return []
|
|
18
|
+
|
|
19
|
+
const entries: string[] = []
|
|
20
|
+
for (const [hybridObjectName, entry] of Object.entries(autolinking)) {
|
|
21
|
+
if (!isObject(entry)) continue
|
|
22
|
+
|
|
23
|
+
const hasLegacySyntax =
|
|
24
|
+
'cpp' in entry || 'swift' in entry || 'kotlin' in entry
|
|
25
|
+
if (hasLegacySyntax) {
|
|
26
|
+
entries.push(hybridObjectName)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return entries
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// TODO: Remove this once users have migrated to new `nitro.json`
|
|
34
|
+
function logLegacyAutolinkingDeprecation(config: unknown): void {
|
|
35
|
+
const legacyEntries = getLegacyAutolinkingEntries(config)
|
|
36
|
+
if (legacyEntries.length === 0) return
|
|
37
|
+
|
|
38
|
+
console.warn(
|
|
39
|
+
chalk.yellow(
|
|
40
|
+
`Warning: nitro.json uses deprecated autolinking syntax ("cpp"/"swift"/"kotlin") for [${legacyEntries.join(', ')}]. ` +
|
|
41
|
+
`Use "all"/"ios"/"android" entries with { language, implementationClassName } instead.`
|
|
42
|
+
)
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
9
46
|
function readFile(configPath: string): string {
|
|
10
47
|
try {
|
|
11
48
|
return fs.readFileSync(configPath, 'utf8')
|
|
@@ -58,6 +95,7 @@ function parseConfig(json: string): NitroUserConfig {
|
|
|
58
95
|
}
|
|
59
96
|
|
|
60
97
|
try {
|
|
98
|
+
logLegacyAutolinkingDeprecation(object)
|
|
61
99
|
return NitroUserConfigSchema.parse(object)
|
|
62
100
|
} catch (error) {
|
|
63
101
|
if (error instanceof ZodError) {
|
|
@@ -103,11 +103,11 @@ namespace ${cxxNamespace} {
|
|
|
103
103
|
class ${name.JHybridTSpec}: public virtual ${name.HybridTSpec}, public virtual ${cppBaseClass} {
|
|
104
104
|
public:
|
|
105
105
|
struct JavaPart: public jni::JavaClass<JavaPart, ${javaPartBaseClass}> {
|
|
106
|
-
static auto
|
|
106
|
+
static constexpr auto kJavaDescriptor = "L${jniClassDescriptor};";
|
|
107
107
|
std::shared_ptr<${name.JHybridTSpec}> get${name.JHybridTSpec}();
|
|
108
108
|
};
|
|
109
109
|
struct CxxPart: public jni::HybridClass<CxxPart, ${cxxPartBaseClass}> {
|
|
110
|
-
static auto
|
|
110
|
+
static constexpr auto kJavaDescriptor = "L${cxxPartJniClassDescriptor};";
|
|
111
111
|
static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jhybridobject> jThis);
|
|
112
112
|
static void registerNatives();
|
|
113
113
|
using HybridBase::HybridBase;
|
|
@@ -66,7 +66,13 @@ export class KotlinCxxBridgedType implements BridgedType<'kotlin', 'c++'> {
|
|
|
66
66
|
return false
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
getRequiredImports(
|
|
69
|
+
getRequiredImports(
|
|
70
|
+
language: Language,
|
|
71
|
+
visited: Set<Type> = new Set()
|
|
72
|
+
): SourceImport[] {
|
|
73
|
+
if (visited.has(this.type)) return []
|
|
74
|
+
visited.add(this.type)
|
|
75
|
+
|
|
70
76
|
const imports = this.type.getRequiredImports(language)
|
|
71
77
|
|
|
72
78
|
if (language === 'c++') {
|
|
@@ -179,19 +185,17 @@ export class KotlinCxxBridgedType implements BridgedType<'kotlin', 'c++'> {
|
|
|
179
185
|
|
|
180
186
|
// Recursively look into referenced types (e.g. the `T` of a `optional<T>`, or `T` of a `T[]`)
|
|
181
187
|
const referencedTypes = getReferencedTypes(this.type)
|
|
182
|
-
|
|
183
|
-
if (t === this.type) {
|
|
184
|
-
// break a recursion - we already know this type
|
|
185
|
-
return
|
|
186
|
-
}
|
|
188
|
+
for (const t of referencedTypes) {
|
|
187
189
|
const bridged = new KotlinCxxBridgedType(t)
|
|
188
|
-
imports.push(...bridged.getRequiredImports(language))
|
|
189
|
-
}
|
|
190
|
+
imports.push(...bridged.getRequiredImports(language, visited))
|
|
191
|
+
}
|
|
190
192
|
|
|
191
193
|
return imports
|
|
192
194
|
}
|
|
193
195
|
|
|
194
|
-
getExtraFiles(): SourceFile[] {
|
|
196
|
+
getExtraFiles(visited: Set<Type> = new Set()): SourceFile[] {
|
|
197
|
+
if (visited.has(this.type)) return []
|
|
198
|
+
visited.add(this.type)
|
|
195
199
|
const files: SourceFile[] = []
|
|
196
200
|
|
|
197
201
|
switch (this.type.kind) {
|
|
@@ -219,14 +223,10 @@ export class KotlinCxxBridgedType implements BridgedType<'kotlin', 'c++'> {
|
|
|
219
223
|
|
|
220
224
|
// Recursively look into referenced types (e.g. the `T` of a `optional<T>`, or `T` of a `T[]`)
|
|
221
225
|
const referencedTypes = getReferencedTypes(this.type)
|
|
222
|
-
|
|
223
|
-
if (t === this.type) {
|
|
224
|
-
// break a recursion - we already know this type
|
|
225
|
-
return
|
|
226
|
-
}
|
|
226
|
+
for (const t of referencedTypes) {
|
|
227
227
|
const bridged = new KotlinCxxBridgedType(t)
|
|
228
|
-
files.push(...bridged.getExtraFiles())
|
|
229
|
-
}
|
|
228
|
+
files.push(...bridged.getExtraFiles(visited))
|
|
229
|
+
}
|
|
230
230
|
|
|
231
231
|
return files
|
|
232
232
|
}
|
|
@@ -53,7 +53,7 @@ namespace ${cxxNamespace} {
|
|
|
53
53
|
*/
|
|
54
54
|
struct J${enumType.enumName} final: public jni::JavaClass<J${enumType.enumName}> {
|
|
55
55
|
public:
|
|
56
|
-
static auto
|
|
56
|
+
static constexpr auto kJavaDescriptor = "L${jniClassDescriptor};";
|
|
57
57
|
|
|
58
58
|
public:
|
|
59
59
|
/**
|
|
@@ -201,7 +201,7 @@ namespace ${cxxNamespace} {
|
|
|
201
201
|
*/
|
|
202
202
|
struct J${name}: public jni::JavaClass<J${name}> {
|
|
203
203
|
public:
|
|
204
|
-
static auto
|
|
204
|
+
static constexpr auto kJavaDescriptor = "L${jniInterfaceDescriptor};";
|
|
205
205
|
|
|
206
206
|
public:
|
|
207
207
|
/**
|
|
@@ -236,7 +236,7 @@ namespace ${cxxNamespace} {
|
|
|
236
236
|
}
|
|
237
237
|
|
|
238
238
|
public:
|
|
239
|
-
static auto
|
|
239
|
+
static constexpr auto kJavaDescriptor = "L${jniClassDescriptor};";
|
|
240
240
|
static void registerNatives() {
|
|
241
241
|
registerHybrid({makeNativeMethod("invoke_cxx", J${name}_cxx::invoke_cxx)});
|
|
242
242
|
}
|
|
@@ -40,9 +40,9 @@ export function createJNIHybridObjectRegistration({
|
|
|
40
40
|
],
|
|
41
41
|
cppDefinition: `
|
|
42
42
|
struct ${JHybridTSpec}Impl: public jni::JavaClass<${JHybridTSpec}Impl, ${JHybridTSpec}::JavaPart> {
|
|
43
|
-
static auto
|
|
43
|
+
static constexpr auto kJavaDescriptor = "L${jniNamespace};";
|
|
44
44
|
static std::shared_ptr<${JHybridTSpec}> create() {
|
|
45
|
-
static auto constructorFn = javaClassStatic()->getConstructor<${JHybridTSpec}Impl::javaobject()>();
|
|
45
|
+
static const auto constructorFn = javaClassStatic()->getConstructor<${JHybridTSpec}Impl::javaobject()>();
|
|
46
46
|
jni::local_ref<${JHybridTSpec}::JavaPart> javaPart = javaClassStatic()->newObject(constructorFn);
|
|
47
47
|
return javaPart->get${JHybridTSpec}();
|
|
48
48
|
}
|
|
@@ -117,7 +117,7 @@ namespace ${cxxNamespace} {
|
|
|
117
117
|
*/
|
|
118
118
|
struct J${structType.structName} final: public jni::JavaClass<J${structType.structName}> {
|
|
119
119
|
public:
|
|
120
|
-
static auto
|
|
120
|
+
static constexpr auto kJavaDescriptor = "L${jniClassDescriptor};";
|
|
121
121
|
|
|
122
122
|
public:
|
|
123
123
|
/**
|
|
@@ -145,7 +145,7 @@ if (isInstanceOf(${namespace}::${innerName}::javaClassStatic())) {
|
|
|
145
145
|
return `
|
|
146
146
|
class ${innerName} final: public jni::JavaClass<${innerName}, J${kotlinName}> {
|
|
147
147
|
public:
|
|
148
|
-
static auto
|
|
148
|
+
static constexpr auto kJavaDescriptor = "L${descriptor};";
|
|
149
149
|
|
|
150
150
|
[[nodiscard]] ${bridge.asJniReferenceType('local')} getValue() const {
|
|
151
151
|
static const auto field = javaClassStatic()->getField<${bridge.getTypeCode('c++')}>("value");
|
|
@@ -180,7 +180,7 @@ namespace ${cxxNamespace} {
|
|
|
180
180
|
*/
|
|
181
181
|
class J${kotlinName}: public jni::JavaClass<J${kotlinName}> {
|
|
182
182
|
public:
|
|
183
|
-
static auto
|
|
183
|
+
static constexpr auto kJavaDescriptor = "L${jniClassDescriptor};";
|
|
184
184
|
|
|
185
185
|
${indent(cppCreateFuncs.join('\n'), ' ')}
|
|
186
186
|
|
|
@@ -28,13 +28,15 @@ export function createKotlinHybridViewManager(
|
|
|
28
28
|
descriptorClassName,
|
|
29
29
|
} = getViewComponentNames(spec)
|
|
30
30
|
const stateUpdaterName = `${stateClassName}Updater`
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
const implementation = spec.config.getAndroidAutolinkedImplementation(
|
|
32
|
+
spec.name
|
|
33
|
+
)
|
|
34
|
+
if (implementation?.language !== 'kotlin') {
|
|
34
35
|
throw new Error(
|
|
35
|
-
`Cannot create Kotlin HybridView ViewManager for ${spec.name} - it
|
|
36
|
+
`Cannot create Kotlin HybridView ViewManager for ${spec.name} - it must be autolinked with a Kotlin Android implementation in nitro.json!`
|
|
36
37
|
)
|
|
37
38
|
}
|
|
39
|
+
const viewImplementation = implementation.implementationClassName
|
|
38
40
|
|
|
39
41
|
const viewManagerCode = `
|
|
40
42
|
${createFileMetadataString(`${manager}.kt`)}
|
|
@@ -28,11 +28,10 @@ export function createSwiftHybridViewManager(
|
|
|
28
28
|
)
|
|
29
29
|
const { component, descriptorClassName, propsClassName } =
|
|
30
30
|
getViewComponentNames(spec)
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
if (viewImplementation == null) {
|
|
31
|
+
const implementation = spec.config.getIosAutolinkedImplementation(spec.name)
|
|
32
|
+
if (implementation?.language !== 'swift') {
|
|
34
33
|
throw new Error(
|
|
35
|
-
`Cannot create Swift HybridView ViewManager for ${spec.name} - it
|
|
34
|
+
`Cannot create Swift HybridView ViewManager for ${spec.name} - it must be autolinked with a Swift iOS implementation in nitro.json!`
|
|
36
35
|
)
|
|
37
36
|
}
|
|
38
37
|
|