Skip to content

Commit 2a2fc26

Browse files
authored
feat: Add new autolinking syntax to nitro.json - allow mixed C++ and Swift/Kotlin autolinked objects per platform (#1255)
* feat: Allow mixing C++ and Swift/Kotlin autolinked objects per platform * Add backwards compatibility for old `nitro.json` * chore: Warn on deprecated nitro.json syntax * chore: Use new log * chore: Lint
1 parent 02f645c commit 2a2fc26

10 files changed

Lines changed: 441 additions & 82 deletions

File tree

docs/static/nitro.schema.json

Lines changed: 90 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -62,26 +62,105 @@
6262
"type": "object",
6363
"oneOf": [
6464
{
65+
"required": [
66+
"all"
67+
],
6568
"properties": {
66-
"cpp": {
67-
"type": "string"
69+
"all": {
70+
"type": "object",
71+
"required": [
72+
"language",
73+
"implementationClassName"
74+
],
75+
"properties": {
76+
"language": {
77+
"type": "string",
78+
"enum": [
79+
"cpp"
80+
]
81+
},
82+
"implementationClassName": {
83+
"type": "string"
84+
}
85+
},
86+
"additionalProperties": false
6887
}
6988
},
70-
"additionalProperties": false,
71-
"required": [
72-
"cpp"
73-
]
89+
"additionalProperties": false
7490
},
7591
{
92+
"type": "object",
93+
"not": {
94+
"required": [
95+
"all"
96+
]
97+
},
98+
"minProperties": 1,
7699
"properties": {
77-
"swift": {
78-
"type": "string"
100+
"ios": {
101+
"type": "object",
102+
"required": [
103+
"language",
104+
"implementationClassName"
105+
],
106+
"properties": {
107+
"language": {
108+
"type": "string",
109+
"enum": [
110+
"cpp",
111+
"swift"
112+
]
113+
},
114+
"implementationClassName": {
115+
"type": "string"
116+
}
117+
},
118+
"additionalProperties": false
79119
},
80-
"kotlin": {
81-
"type": "string"
120+
"android": {
121+
"type": "object",
122+
"required": [
123+
"language",
124+
"implementationClassName"
125+
],
126+
"properties": {
127+
"language": {
128+
"type": "string",
129+
"enum": [
130+
"cpp",
131+
"kotlin"
132+
]
133+
},
134+
"implementationClassName": {
135+
"type": "string"
136+
}
137+
},
138+
"additionalProperties": false
139+
}
140+
},
141+
"patternProperties": {
142+
"^(?!all$|ios$|android$).+$": {
143+
"type": "object",
144+
"required": [
145+
"language",
146+
"implementationClassName"
147+
],
148+
"properties": {
149+
"language": {
150+
"type": "string",
151+
"enum": [
152+
"cpp",
153+
"swift",
154+
"kotlin"
155+
]
156+
},
157+
"implementationClassName": {
158+
"type": "string"
159+
}
160+
},
161+
"additionalProperties": false
82162
}
83163
},
84-
"minProperties": 1,
85164
"additionalProperties": false
86165
}
87166
]

packages/nitrogen/src/autolinking/android/createHybridObjectInitializer.ts

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -28,27 +28,39 @@ export function createHybridObjectIntializer(): SourceFile[] {
2828
const cppRegistrations: string[] = []
2929
const cppDefinitions: string[] = []
3030
for (const hybridObjectName of Object.keys(autolinkedHybridObjects)) {
31-
const config = autolinkedHybridObjects[hybridObjectName]
32-
33-
if (config?.cpp != null) {
34-
// Autolink a C++ HybridObject!
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
4135
}
42-
if (config?.kotlin != null) {
43-
// Autolink a Kotlin HybridObject through JNI/C++!
44-
const { cppCode, cppDefinition, requiredImports } =
45-
createJNIHybridObjectRegistration({
36+
37+
switch (implementation.language) {
38+
case 'cpp': {
39+
// Autolink a C++ HybridObject!
40+
const { cppCode, requiredImports } = createCppHybridObjectRegistration({
4641
hybridObjectName: hybridObjectName,
47-
jniClassName: config.kotlin,
42+
cppClassName: implementation.implementationClassName,
4843
})
49-
cppHybridObjectImports.push(...requiredImports)
50-
cppDefinitions.push(cppDefinition)
51-
cppRegistrations.push(cppCode)
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+
)
5264
}
5365
}
5466

packages/nitrogen/src/autolinking/ios/createHybridObjectInitializer.ts

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -23,28 +23,40 @@ export function createHybridObjectIntializer(): [ObjcFile, SwiftFile] | [] {
2323
const cppImports: SourceImport[] = []
2424
let containsSwiftObjects = false
2525
for (const hybridObjectName of Object.keys(autolinkedHybridObjects)) {
26-
const config = autolinkedHybridObjects[hybridObjectName]
27-
28-
if (config?.cpp != null) {
29-
// Autolink a C++ HybridObject!
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
3630
}
37-
if (config?.swift != null) {
38-
// Autolink a Swift HybridObject!
39-
containsSwiftObjects = true
40-
const { cppCode, requiredImports, swiftRegistrationMethods } =
41-
createSwiftHybridObjectRegistration({
31+
32+
switch (implementation.language) {
33+
case 'cpp': {
34+
// Autolink a C++ HybridObject!
35+
const { cppCode, requiredImports } = createCppHybridObjectRegistration({
4236
hybridObjectName: hybridObjectName,
43-
swiftClassName: config.swift,
37+
cppClassName: implementation.implementationClassName,
4438
})
45-
cppImports.push(...requiredImports)
46-
cppRegistrations.push(cppCode)
47-
swiftRegistrations.push(swiftRegistrationMethods)
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+
)
4860
}
4961
}
5062

packages/nitrogen/src/config/NitroConfig.ts

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import chalk from 'chalk'
22
import { readUserConfig } from './getConfig.js'
3-
import type { NitroUserConfig } from './NitroUserConfig.js'
3+
import type {
4+
AutolinkingAndroidImplementation,
5+
AutolinkingIOSImplementation,
6+
AutolinkingPlatformImplementation,
7+
NitroUserConfig,
8+
} from './NitroUserConfig.js'
49

510
const CXX_BASE_NAMESPACE = ['margelo', 'nitro']
611
const ANDROID_BASE_NAMESPACE = ['com', 'margelo', 'nitro']
@@ -109,6 +114,49 @@ export class NitroConfig {
109114
return this.config.autolinking
110115
}
111116

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+
112160
/**
113161
* Get the paths that will be ignored when loading the TypeScript project.
114162
* In most cases, this just contains `node_modules/`.

0 commit comments

Comments
 (0)