nitrogen 0.2.24 → 0.29.5

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.
Files changed (241) hide show
  1. package/README.md +18 -108
  2. package/lib/Logger.js +56 -0
  3. package/lib/autolinking/Autolinking.js +1 -0
  4. package/lib/autolinking/android/createCMakeExtension.js +109 -0
  5. package/lib/autolinking/android/createGradleExtension.js +36 -0
  6. package/lib/autolinking/android/createHybridObjectInitializer.js +159 -0
  7. package/lib/autolinking/createAndroidAutolinking.js +13 -0
  8. package/lib/autolinking/createIOSAutolinking.js +19 -0
  9. package/lib/autolinking/ios/createHybridObjectInitializer.js +97 -0
  10. package/lib/autolinking/ios/createPodspecRubyExtension.js +69 -0
  11. package/lib/autolinking/ios/createSwiftCxxBridge.js +117 -0
  12. package/lib/autolinking/ios/createSwiftUmbrellaHeader.js +74 -0
  13. package/lib/config/NitroConfig.js +112 -0
  14. package/lib/config/NitroUserConfig.js +88 -0
  15. package/lib/config/getConfig.js +84 -0
  16. package/lib/createGitAttributes.js +11 -0
  17. package/lib/createPlatformSpec.js +127 -0
  18. package/lib/getFiles.js +28 -0
  19. package/lib/getPlatformSpecs.js +153 -0
  20. package/lib/index.js +113 -10
  21. package/lib/init.js +123 -0
  22. package/lib/nitrogen.js +165 -0
  23. package/lib/prettifyDirectory.js +27 -0
  24. package/lib/syntax/BridgedType.js +1 -0
  25. package/lib/syntax/CodeNode.js +1 -0
  26. package/lib/syntax/HybridObjectSpec.js +1 -0
  27. package/lib/syntax/Method.js +108 -0
  28. package/lib/syntax/Parameter.js +65 -0
  29. package/lib/syntax/Property.js +147 -0
  30. package/lib/syntax/SourceFile.js +7 -0
  31. package/lib/syntax/c++/CppEnum.js +110 -0
  32. package/lib/syntax/c++/CppHybridObject.js +146 -0
  33. package/lib/syntax/c++/CppHybridObjectRegistration.js +18 -0
  34. package/lib/syntax/c++/CppStruct.js +108 -0
  35. package/lib/syntax/c++/CppUnion.js +88 -0
  36. package/lib/syntax/c++/getForwardDeclaration.js +14 -0
  37. package/lib/syntax/c++/includeNitroHeader.js +34 -0
  38. package/lib/syntax/createType.js +303 -0
  39. package/lib/syntax/getAllTypes.js +11 -0
  40. package/lib/syntax/getCustomTypeConfig.js +53 -0
  41. package/lib/syntax/getHybridObjectName.d.ts +36 -0
  42. package/lib/syntax/getHybridObjectName.js +10 -0
  43. package/lib/syntax/getInterfaceProperties.js +9 -0
  44. package/lib/syntax/getReferencedTypes.js +47 -0
  45. package/lib/syntax/helpers.js +53 -0
  46. package/lib/syntax/isCoreType.js +47 -0
  47. package/lib/syntax/kotlin/FbjniHybridObject.js +261 -0
  48. package/lib/syntax/kotlin/JNINativeRegistrations.js +7 -0
  49. package/lib/syntax/kotlin/KotlinBoxedPrimitive.js +17 -0
  50. package/lib/syntax/kotlin/KotlinCxxBridgedType.js +893 -0
  51. package/lib/syntax/kotlin/KotlinEnum.js +113 -0
  52. package/lib/syntax/kotlin/KotlinFunction.js +256 -0
  53. package/lib/syntax/kotlin/KotlinHybridObject.js +177 -0
  54. package/lib/syntax/kotlin/KotlinHybridObjectRegistration.js +26 -0
  55. package/lib/syntax/kotlin/KotlinStruct.js +172 -0
  56. package/lib/syntax/kotlin/KotlinVariant.js +191 -0
  57. package/lib/syntax/swift/SwiftCxxBridgedType.js +819 -0
  58. package/lib/syntax/swift/SwiftCxxTypeHelper.js +613 -0
  59. package/lib/syntax/swift/SwiftEnum.js +52 -0
  60. package/lib/syntax/swift/SwiftFunction.js +83 -0
  61. package/lib/syntax/swift/SwiftHybridObject.js +103 -0
  62. package/lib/syntax/swift/SwiftHybridObjectBridge.js +451 -0
  63. package/lib/syntax/swift/SwiftHybridObjectRegistration.js +42 -0
  64. package/lib/syntax/swift/SwiftStruct.js +75 -0
  65. package/lib/syntax/swift/SwiftVariant.js +58 -0
  66. package/lib/syntax/types/ArrayBufferType.js +37 -0
  67. package/lib/syntax/types/ArrayType.d.ts +12 -0
  68. package/lib/syntax/types/ArrayType.js +52 -0
  69. package/lib/syntax/types/BigIntType.js +27 -0
  70. package/lib/syntax/types/BooleanType.js +27 -0
  71. package/lib/syntax/types/CustomType.d.ts +14 -0
  72. package/lib/syntax/types/CustomType.js +36 -0
  73. package/lib/syntax/types/DateType.js +35 -0
  74. package/lib/syntax/types/EnumType.js +101 -0
  75. package/lib/syntax/types/ErrorType.js +37 -0
  76. package/lib/syntax/types/FunctionType.js +147 -0
  77. package/lib/syntax/types/HybridObjectBaseType.js +38 -0
  78. package/lib/syntax/types/HybridObjectType.js +131 -0
  79. package/lib/syntax/types/MapType.js +37 -0
  80. package/lib/syntax/types/NamedWrappingType.js +27 -0
  81. package/lib/syntax/types/NullType.js +23 -0
  82. package/lib/syntax/types/NumberType.js +27 -0
  83. package/lib/syntax/types/OptionalType.js +59 -0
  84. package/lib/syntax/types/PromiseType.js +62 -0
  85. package/lib/syntax/types/RecordType.js +47 -0
  86. package/lib/syntax/types/ResultWrappingType.js +44 -0
  87. package/lib/syntax/types/StringType.js +35 -0
  88. package/lib/syntax/types/StructType.js +61 -0
  89. package/lib/syntax/types/TupleType.js +39 -0
  90. package/lib/syntax/types/Type.js +1 -0
  91. package/lib/syntax/types/VariantType.js +75 -0
  92. package/lib/syntax/types/VoidType.js +27 -0
  93. package/lib/syntax/types/getTypeAs.js +12 -0
  94. package/lib/utils.js +126 -0
  95. package/lib/views/CppHybridViewComponent.js +256 -0
  96. package/lib/views/createHostComponentJs.js +27 -0
  97. package/lib/views/kotlin/KotlinHybridViewManager.js +229 -0
  98. package/lib/views/swift/SwiftHybridViewManager.js +131 -0
  99. package/lib/writeFile.js +19 -0
  100. package/package.json +58 -29
  101. package/src/Logger.ts +63 -0
  102. package/src/autolinking/Autolinking.ts +9 -0
  103. package/src/autolinking/android/createCMakeExtension.ts +126 -0
  104. package/src/autolinking/android/createGradleExtension.ts +43 -0
  105. package/src/autolinking/android/createHybridObjectInitializer.ts +174 -0
  106. package/src/autolinking/createAndroidAutolinking.ts +28 -0
  107. package/src/autolinking/createIOSAutolinking.ts +24 -0
  108. package/src/autolinking/ios/createHybridObjectInitializer.ts +112 -0
  109. package/src/autolinking/ios/createPodspecRubyExtension.ts +76 -0
  110. package/src/autolinking/ios/createSwiftCxxBridge.ts +137 -0
  111. package/src/autolinking/ios/createSwiftUmbrellaHeader.ts +90 -0
  112. package/src/config/NitroConfig.ts +139 -0
  113. package/src/config/NitroUserConfig.ts +105 -0
  114. package/src/config/getConfig.ts +91 -0
  115. package/src/createGitAttributes.ts +15 -0
  116. package/src/createPlatformSpec.ts +176 -0
  117. package/src/getFiles.ts +31 -0
  118. package/src/getPlatformSpecs.ts +202 -0
  119. package/src/index.ts +146 -0
  120. package/src/init.ts +186 -0
  121. package/src/nitrogen.ts +246 -0
  122. package/src/prettifyDirectory.ts +32 -0
  123. package/src/syntax/BridgedType.ts +59 -0
  124. package/src/syntax/CodeNode.ts +24 -0
  125. package/src/syntax/HybridObjectSpec.ts +14 -0
  126. package/src/syntax/Method.ts +154 -0
  127. package/src/syntax/Parameter.ts +81 -0
  128. package/src/syntax/Property.ts +203 -0
  129. package/src/syntax/SourceFile.ts +80 -0
  130. package/src/syntax/c++/CppEnum.ts +128 -0
  131. package/src/syntax/c++/CppHybridObject.ts +165 -0
  132. package/src/syntax/c++/CppHybridObjectRegistration.ts +39 -0
  133. package/src/syntax/c++/CppStruct.ts +129 -0
  134. package/src/syntax/c++/CppUnion.ts +105 -0
  135. package/src/syntax/c++/getForwardDeclaration.ts +19 -0
  136. package/src/syntax/c++/includeNitroHeader.ts +40 -0
  137. package/src/syntax/createType.ts +365 -0
  138. package/src/syntax/getAllTypes.ts +18 -0
  139. package/src/syntax/getCustomTypeConfig.ts +71 -0
  140. package/src/syntax/getHybridObjectName.ts +48 -0
  141. package/src/syntax/getInterfaceProperties.ts +21 -0
  142. package/src/syntax/getReferencedTypes.ts +57 -0
  143. package/src/syntax/helpers.ts +79 -0
  144. package/src/syntax/isCoreType.ts +60 -0
  145. package/src/syntax/kotlin/FbjniHybridObject.ts +313 -0
  146. package/src/syntax/kotlin/JNINativeRegistrations.ts +19 -0
  147. package/src/syntax/kotlin/KotlinBoxedPrimitive.ts +19 -0
  148. package/src/syntax/kotlin/KotlinCxxBridgedType.ts +942 -0
  149. package/src/syntax/kotlin/KotlinEnum.ts +130 -0
  150. package/src/syntax/kotlin/KotlinFunction.ts +277 -0
  151. package/src/syntax/kotlin/KotlinHybridObject.ts +205 -0
  152. package/src/syntax/kotlin/KotlinHybridObjectRegistration.ts +51 -0
  153. package/src/syntax/kotlin/KotlinStruct.ts +198 -0
  154. package/src/syntax/kotlin/KotlinVariant.ts +212 -0
  155. package/src/syntax/swift/SwiftCxxBridgedType.ts +874 -0
  156. package/src/syntax/swift/SwiftCxxTypeHelper.ts +674 -0
  157. package/src/syntax/swift/SwiftEnum.ts +65 -0
  158. package/src/syntax/swift/SwiftFunction.ts +91 -0
  159. package/src/syntax/swift/SwiftHybridObject.ts +121 -0
  160. package/src/syntax/swift/SwiftHybridObjectBridge.ts +522 -0
  161. package/src/syntax/swift/SwiftHybridObjectRegistration.ts +75 -0
  162. package/src/syntax/swift/SwiftStruct.ts +85 -0
  163. package/src/syntax/swift/SwiftVariant.ts +67 -0
  164. package/src/syntax/types/ArrayBufferType.ts +49 -0
  165. package/src/syntax/types/ArrayType.ts +62 -0
  166. package/src/syntax/types/BigIntType.ts +35 -0
  167. package/src/syntax/types/BooleanType.ts +35 -0
  168. package/src/syntax/types/CustomType.ts +47 -0
  169. package/src/syntax/types/DateType.ts +43 -0
  170. package/src/syntax/types/EnumType.ts +130 -0
  171. package/src/syntax/types/ErrorType.ts +44 -0
  172. package/src/syntax/types/FunctionType.ts +167 -0
  173. package/src/syntax/types/HybridObjectBaseType.ts +54 -0
  174. package/src/syntax/types/HybridObjectType.ts +198 -0
  175. package/src/syntax/types/MapType.ts +49 -0
  176. package/src/syntax/types/NamedWrappingType.ts +33 -0
  177. package/src/syntax/types/NullType.ts +30 -0
  178. package/src/syntax/types/NumberType.ts +34 -0
  179. package/src/syntax/types/OptionalType.ts +66 -0
  180. package/src/syntax/types/PromiseType.ts +72 -0
  181. package/src/syntax/types/RecordType.ts +56 -0
  182. package/src/syntax/types/ResultWrappingType.ts +53 -0
  183. package/src/syntax/types/StringType.ts +44 -0
  184. package/src/syntax/types/StructType.ts +83 -0
  185. package/src/syntax/types/TupleType.ts +53 -0
  186. package/src/syntax/types/Type.ts +82 -0
  187. package/src/syntax/types/VariantType.ts +92 -0
  188. package/src/syntax/types/VoidType.ts +34 -0
  189. package/src/syntax/types/getTypeAs.ts +15 -0
  190. package/src/utils.ts +162 -0
  191. package/src/views/CppHybridViewComponent.ts +304 -0
  192. package/src/views/createHostComponentJs.ts +34 -0
  193. package/src/views/kotlin/KotlinHybridViewManager.ts +258 -0
  194. package/src/views/swift/SwiftHybridViewManager.ts +153 -0
  195. package/src/writeFile.ts +27 -0
  196. package/.jshintignore +0 -6
  197. package/.jshintrc +0 -3
  198. package/.npmignore +0 -3
  199. package/.travis.yml +0 -13
  200. package/LICENSE +0 -13
  201. package/browser/nitrogen-min.js +0 -3
  202. package/browser/nitrogen.js +0 -6369
  203. package/lib/apiKey.js +0 -67
  204. package/lib/blob.js +0 -57
  205. package/lib/commandManager.js +0 -350
  206. package/lib/device.js +0 -19
  207. package/lib/memoryStore.js +0 -24
  208. package/lib/message.js +0 -298
  209. package/lib/permission.js +0 -121
  210. package/lib/principal.js +0 -330
  211. package/lib/service.js +0 -347
  212. package/lib/session.js +0 -494
  213. package/lib/user.js +0 -20
  214. package/publish +0 -2
  215. package/scripts/build-documentation +0 -4
  216. package/scripts/build-module +0 -27
  217. package/scripts/module.js +0 -12
  218. package/scripts/postamble.js +0 -1
  219. package/scripts/preamble.js +0 -2
  220. package/scripts/run-test-server +0 -9
  221. package/test/config.js +0 -12
  222. package/test/fixtures/images/image.jpg +0 -0
  223. package/test/fixtures/images/motion0.jpg +0 -0
  224. package/test/fixtures/images/motion1.jpg +0 -0
  225. package/test/fixtures/images/motion2.jpg +0 -0
  226. package/test/fixtures/index.js +0 -76
  227. package/test/main.js +0 -5
  228. package/test/memoryStore.js +0 -22
  229. package/test/mocha.opts +0 -3
  230. package/test/units/apiKey.js +0 -46
  231. package/test/units/blob.js +0 -35
  232. package/test/units/commandManager.js +0 -67
  233. package/test/units/device.js +0 -26
  234. package/test/units/heartbeat.js +0 -28
  235. package/test/units/message.js +0 -79
  236. package/test/units/permissions.js +0 -43
  237. package/test/units/principal.js +0 -116
  238. package/test/units/service.js +0 -92
  239. package/test/units/session.js +0 -97
  240. package/test/units/user.js +0 -48
  241. package/yuidoc.json +0 -8
@@ -0,0 +1,202 @@
1
+ import type { PlatformSpec } from 'react-native-nitro-modules'
2
+ import type { InterfaceDeclaration, Type, TypeAliasDeclaration } from 'ts-morph'
3
+ import { Node, Symbol } from 'ts-morph'
4
+ import { getBaseTypes } from './utils.js'
5
+
6
+ export type Platform = keyof Required<PlatformSpec>
7
+ export type Language = Required<PlatformSpec>[keyof PlatformSpec]
8
+
9
+ const platformLanguages: { [K in Platform]: Language[] } = {
10
+ ios: ['swift', 'c++'],
11
+ android: ['kotlin', 'c++'],
12
+ }
13
+ const allPlatforms = Object.keys(platformLanguages) as Platform[]
14
+ const allLanguages = Object.values(platformLanguages).flatMap((l) => l)
15
+
16
+ function isValidLanguage(language: string | undefined): language is Language {
17
+ if (language == null) {
18
+ return false
19
+ }
20
+ return allLanguages.includes(language as Language)
21
+ }
22
+
23
+ function isValidPlatform(platform: string): platform is Platform {
24
+ return allPlatforms.includes(platform as Platform)
25
+ }
26
+
27
+ function getLiteralValue(symbol: Symbol): string | undefined {
28
+ const value = symbol.getValueDeclaration()
29
+ if (value == null) {
30
+ return undefined
31
+ }
32
+ const type = value.getType()
33
+ const literal = type.getLiteralValue()
34
+ if (typeof literal === 'string') {
35
+ return literal
36
+ }
37
+ return undefined
38
+ }
39
+
40
+ // TODO: The type casting result here doesn't really work in TS.
41
+ function isValidLanguageForPlatform(
42
+ language: Language,
43
+ platform: Platform
44
+ ): language is Required<PlatformSpec>[typeof platform] {
45
+ return platformLanguages[platform].includes(language)
46
+ }
47
+
48
+ function getPlatformSpec(typeName: string, platformSpecs: Type): PlatformSpec {
49
+ const result: PlatformSpec = {}
50
+
51
+ // Properties (ios, android)
52
+ const properties = platformSpecs.getProperties()
53
+ for (const property of properties) {
54
+ // Property name (ios, android)
55
+ const platform = property.getName()
56
+ if (!isValidPlatform(platform)) {
57
+ console.warn(
58
+ ` ⚠️ ${typeName} does not properly extend HybridObject<T> - "${platform}" is not a valid Platform! ` +
59
+ `Valid platforms are: [${allPlatforms.join(', ')}]`
60
+ )
61
+ continue
62
+ }
63
+
64
+ // Value (swift, kotlin, c++)
65
+ const language = getLiteralValue(property)
66
+ if (!isValidLanguage(language)) {
67
+ console.warn(
68
+ ` ⚠️ ${typeName}: Language ${language} is not a valid language for ${platform}! ` +
69
+ `Valid languages are: [${platformLanguages[platform].join(', ')}]`
70
+ )
71
+ continue
72
+ }
73
+
74
+ // Double-check that language works on this platform (android: kotlin/c++, ios: swift/c++)
75
+ if (!isValidLanguageForPlatform(language, platform)) {
76
+ console.warn(
77
+ ` ⚠️ ${typeName}: Language ${language} is not a valid language for ${platform}! ` +
78
+ `Valid languages are: [${platformLanguages[platform].join(', ')}]`
79
+ )
80
+ continue
81
+ }
82
+
83
+ // @ts-expect-error because TypeScript isn't smart enough yet to correctly cast after the `isValidLanguageForPlatform` check.
84
+ result[platform] = language
85
+ }
86
+
87
+ return result
88
+ }
89
+
90
+ function isDirectlyType(type: Type, name: string): boolean {
91
+ const symbol = type.getSymbol() ?? type.getAliasSymbol()
92
+ if (symbol?.getName() === name) {
93
+ return true
94
+ }
95
+ return false
96
+ }
97
+
98
+ function extendsType(type: Type, name: string, recursive: boolean): boolean {
99
+ for (const base of getBaseTypes(type)) {
100
+ const isHybrid = isDirectlyType(base, name)
101
+ if (isHybrid) {
102
+ return true
103
+ }
104
+ if (recursive) {
105
+ const baseExtends = extendsType(base, name, recursive)
106
+ if (baseExtends) {
107
+ return true
108
+ }
109
+ }
110
+ }
111
+ return false
112
+ }
113
+
114
+ export function isDirectlyHybridObject(type: Type): boolean {
115
+ return isDirectlyType(type, 'HybridObject')
116
+ }
117
+
118
+ export function extendsHybridObject(type: Type, recursive: boolean): boolean {
119
+ return extendsType(type, 'HybridObject', recursive)
120
+ }
121
+
122
+ export function isHybridViewProps(type: Type): boolean {
123
+ return extendsType(type, 'HybridViewProps', true)
124
+ }
125
+ export function isHybridViewMethods(type: Type): boolean {
126
+ return extendsType(type, 'HybridViewMethods', true)
127
+ }
128
+
129
+ export function isHybridView(type: Type): boolean {
130
+ // HybridViews are type aliases for `HybridView`, and `Props & Methods` are just intersected together.
131
+ const unionTypes = type.getIntersectionTypes()
132
+ for (const union of unionTypes) {
133
+ const symbol = union.getSymbol()
134
+ if (symbol == null) return false
135
+ return symbol.getName() === 'HybridViewTag'
136
+ }
137
+ return false
138
+ }
139
+
140
+ export function isAnyHybridSubclass(type: Type): boolean {
141
+ if (isDirectlyHybridObject(type)) return false
142
+
143
+ if (isHybridView(type)) return true
144
+
145
+ if (extendsHybridObject(type, true)) return true
146
+
147
+ return false
148
+ }
149
+
150
+ /**
151
+ * If the given interface ({@linkcode declaration}) extends `HybridObject`,
152
+ * this method returns the platforms it exists on.
153
+ * If it doesn't extend `HybridObject`, this returns `undefined`.
154
+ */
155
+ export function getHybridObjectPlatforms(
156
+ declaration: InterfaceDeclaration | TypeAliasDeclaration
157
+ ): PlatformSpec | undefined {
158
+ const base = getBaseTypes(declaration.getType()).find((t) =>
159
+ isDirectlyHybridObject(t)
160
+ )
161
+ if (base == null) {
162
+ // this type does not extend `HybridObject`.
163
+ throw new Error(
164
+ `Couldn't find HybridObject<..> base for ${declaration.getName()}! (${declaration.getText()})`
165
+ )
166
+ }
167
+
168
+ const genericArguments = base.getTypeArguments()
169
+ const platformSpecsArgument = genericArguments[0]
170
+ if (platformSpecsArgument == null) {
171
+ // it uses `HybridObject` without generic arguments. This defaults to C++
172
+ return { android: 'c++', ios: 'c++' }
173
+ }
174
+
175
+ return getPlatformSpec(declaration.getName(), platformSpecsArgument)
176
+ }
177
+
178
+ export function getHybridViewPlatforms(
179
+ view: InterfaceDeclaration | TypeAliasDeclaration
180
+ ): PlatformSpec | undefined {
181
+ if (Node.isTypeAliasDeclaration(view)) {
182
+ const hybridViewTypeNode = view.getTypeNode()
183
+
184
+ const isHybridViewType =
185
+ Node.isTypeReference(hybridViewTypeNode) &&
186
+ hybridViewTypeNode.getTypeName().getText() === 'HybridView'
187
+
188
+ if (!isHybridViewType) {
189
+ return
190
+ }
191
+
192
+ const genericArguments = hybridViewTypeNode.getTypeArguments()
193
+
194
+ const platformSpecArg = genericArguments[2]
195
+ if (platformSpecArg != null) {
196
+ return getPlatformSpec(view.getName(), platformSpecArg.getType())
197
+ }
198
+ }
199
+
200
+ // it uses `HybridObject` without generic arguments. This defaults to platform native languages
201
+ return { ios: 'swift', android: 'kotlin' }
202
+ }
package/src/index.ts ADDED
@@ -0,0 +1,146 @@
1
+ #!/usr/bin/env node
2
+
3
+ import yargs from 'yargs/yargs'
4
+ import { hideBin } from 'yargs/helpers'
5
+ import chalk from 'chalk'
6
+ import { prettifyDirectory } from './prettifyDirectory.js'
7
+ import { getFiles } from './getFiles.js'
8
+ import { runNitrogen } from './nitrogen.js'
9
+ import { promises as fs } from 'fs'
10
+ import { isValidLogLevel, setLogLevel } from './Logger.js'
11
+ import { initNewNitroModule } from './init.js'
12
+ import { NITROGEN_VERSION } from './utils.js'
13
+
14
+ const commandName = 'nitrogen'
15
+
16
+ // Maximum of 100 col width
17
+ const cliWidth = Math.min(process.stdout.columns * 0.9, 100)
18
+
19
+ // Set up yargs CLI app
20
+ await yargs(hideBin(process.argv))
21
+ .option('log-level', {
22
+ type: 'string',
23
+ description: 'Configures the log-level of nitrogen.',
24
+ default: 'info',
25
+ choices: ['debug', 'info', 'warning', 'error'],
26
+ })
27
+ .middleware((args) => {
28
+ if (!isValidLogLevel(args.logLevel)) {
29
+ throw new Error(`Invalid log-level ("${args.logLevel}")!`)
30
+ }
31
+ setLogLevel(args.logLevel)
32
+ })
33
+ // 🔥 nitrogen [path]
34
+ .command(
35
+ '$0 [basePath]',
36
+ `Usage: ${chalk.bold(`${commandName} <basePath> [options]`)}\n` +
37
+ `Run the nitro code-generator on all ${chalk.underline('**/*.nitro.ts')} files found ` +
38
+ `in the current directory and generate C++, Swift or Kotlin outputs in ${chalk.underline('./nitrogen/generated')}.`,
39
+ (y) =>
40
+ y
41
+ .positional('basePath', {
42
+ type: 'string',
43
+ description: `The base path of where Nitrogen will start looking for ${chalk.underline('**/*.nitro.ts')} specs.`,
44
+ default: process.cwd(),
45
+ })
46
+ .option('out', {
47
+ type: 'string',
48
+ description:
49
+ 'Configures the output path of the generated C++, Swift or Kotlin files.',
50
+ default: './nitrogen/generated',
51
+ })
52
+ .option('config', {
53
+ type: 'string',
54
+ description: `A custom path to a ${chalk.underline('nitro.json')} config file.`,
55
+ default: './nitro.json',
56
+ }),
57
+ async (argv) => {
58
+ const basePath = argv.basePath
59
+ const outputDirectory = argv.out
60
+ await runNitrogenCommand(basePath, outputDirectory)
61
+ }
62
+ )
63
+ // 🔥 nitrogen init <moduleName>
64
+ .command(
65
+ 'init <moduleName>',
66
+ `Usage: ${chalk.bold(`${commandName} init <moduleName> [options]`)}\n` +
67
+ `Create a new Nitro Module.`,
68
+ (y) =>
69
+ y
70
+ .positional('moduleName', {
71
+ type: 'string',
72
+ description: 'The name of the Nitro Module that will be created.',
73
+ demandOption: true,
74
+ })
75
+ .option('path', {
76
+ type: 'string',
77
+ description: `A custom path to create the new Nitro Module in - instead of the current working directory.`,
78
+ default: process.cwd(),
79
+ }),
80
+ async (argv) => {
81
+ await initNewNitroModule(argv.path, argv.moduleName)
82
+ }
83
+ )
84
+ .usage(
85
+ `Usage: ${chalk.bold('$0 [options]')}\n` +
86
+ `$0 is a code-generater for Nitro Modules (${chalk.underline('https://github.com/mrousavy/nitro')})\n` +
87
+ `It converts all TypeScript specs found in ${chalk.underline('**/*.nitro.ts')} to C++, Swift or Kotlin specs.\n` +
88
+ `Each library/module must have a ${chalk.underline('nitro.json')} configuration file in it's root directory.\n` +
89
+ `$Nitrogen Version: ${chalk.bold(NITROGEN_VERSION)}`
90
+ )
91
+ .help()
92
+ .strict()
93
+ .wrap(cliWidth).argv
94
+
95
+ async function runNitrogenCommand(
96
+ baseDirectory: string,
97
+ outputDirectory: string
98
+ ): Promise<void> {
99
+ // 1. Prepare output folders
100
+ const filesBefore = await getFiles(outputDirectory)
101
+
102
+ const start = performance.now()
103
+
104
+ // 2. Run Nitrogen
105
+ const { generatedFiles, generatedSpecsCount, targetSpecsCount } =
106
+ await runNitrogen({
107
+ baseDirectory: baseDirectory,
108
+ outputDirectory: outputDirectory,
109
+ })
110
+
111
+ const end = performance.now()
112
+ const timeS = ((end - start) / 1000).toFixed(1)
113
+ console.log(
114
+ `🎉 Generated ${generatedSpecsCount}/${targetSpecsCount} HybridObject${generatedSpecsCount === 1 ? '' : 's'} in ${timeS}s!`
115
+ )
116
+ console.log(
117
+ `💡 Your code is in ${chalk.underline(prettifyDirectory(outputDirectory))}`
118
+ )
119
+
120
+ // 3. Delete all old dangling files
121
+ const addedFiles = generatedFiles.filter((f) => !filesBefore.includes(f))
122
+ const removedFiles = filesBefore.filter((f) => !generatedFiles.includes(f))
123
+ if (addedFiles.length > 0 || removedFiles.length > 0) {
124
+ let text = ''
125
+ const as = addedFiles.length > 1 ? 's' : ''
126
+ const rs = removedFiles.length > 1 ? 's' : ''
127
+ if (addedFiles.length > 0 && removedFiles.length === 0) {
128
+ text = `Added ${addedFiles.length} file${as}`
129
+ } else if (addedFiles.length === 0 && removedFiles.length > 0) {
130
+ text = `Removed ${removedFiles.length} file${rs}`
131
+ } else {
132
+ text = `Added ${addedFiles.length} file${as} and removed ${removedFiles.length} file${rs}`
133
+ }
134
+
135
+ console.log(
136
+ `‼️ ${text} - ${chalk.bold('you need to run `pod install`/sync gradle to update files!')}`
137
+ )
138
+ }
139
+ const promises = removedFiles.map(async (file) => {
140
+ const stat = await fs.stat(file)
141
+ if (stat.isFile()) {
142
+ await fs.rm(file)
143
+ }
144
+ })
145
+ await Promise.all(promises)
146
+ }
package/src/init.ts ADDED
@@ -0,0 +1,186 @@
1
+ import path from 'path'
2
+ import { prettifyDirectory } from './prettifyDirectory.js'
3
+ import fs from 'fs/promises'
4
+ import { existsSync } from 'fs'
5
+ import { Logger } from './Logger.js'
6
+ import chalk from 'chalk'
7
+ import { execSync } from 'child_process'
8
+ import { randomUUID } from 'crypto'
9
+
10
+ export async function initNewNitroModule(
11
+ baseDirectory: string,
12
+ moduleName: string,
13
+ ref: string = 'main'
14
+ ): Promise<void> {
15
+ Logger.info(
16
+ `⚙️ Creating new Nitro Module "${chalk.bold(moduleName)}" in ${chalk.underline(prettifyDirectory(baseDirectory))}...`
17
+ )
18
+
19
+ const directory = path.join(baseDirectory, moduleName)
20
+ if (existsSync(directory)) {
21
+ Logger.error(
22
+ `❌ A folder named "${chalk.underline(moduleName)}" already exists in the directory ${chalk.underline(prettifyDirectory(baseDirectory))}!`
23
+ )
24
+ process.exit(1)
25
+ }
26
+
27
+ await fs.mkdir(directory)
28
+
29
+ const modulePath = await downloadGitHubFolder(
30
+ 'mrousavy',
31
+ 'nitro',
32
+ ref,
33
+ 'packages/template',
34
+ directory
35
+ )
36
+ Logger.info(`🏗️ Constructing template...`)
37
+
38
+ const cleanLibraryName = moduleName.replace('react-native-', '')
39
+ const cxxNamespace = cleanLibraryName.replaceAll('-', '')
40
+ let camelCaseName = cleanLibraryName
41
+ .split('-')
42
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
43
+ .join('')
44
+ if (!camelCaseName.startsWith('Nitro')) {
45
+ camelCaseName = 'Nitro' + camelCaseName
46
+ }
47
+
48
+ await replaceTemplate(modulePath, `cxxNamespace`, cxxNamespace)
49
+ await replaceTemplate(modulePath, `androidNamespace`, cxxNamespace)
50
+ await replaceTemplate(modulePath, `androidCxxLibName`, camelCaseName)
51
+ await replaceTemplate(modulePath, `iosModuleName`, camelCaseName)
52
+ await replaceTemplate(modulePath, `packageName`, moduleName)
53
+ await replaceTemplate(modulePath, `packageDescription`, moduleName)
54
+
55
+ Logger.info(
56
+ `🎉 Created Nitro Module "${chalk.bold(moduleName)}" in ${chalk.underline(prettifyDirectory(directory))}!`
57
+ )
58
+ Logger.info(
59
+ `👉 To install dependencies, use your package manager of choice (e.g. ${chalk.bold('npm install')})`
60
+ )
61
+ Logger.info(
62
+ `👉 To create your first Hybrid Object, add a ${chalk.underline(`*.nitro.ts`)} file, declare a TypeScript interface that extends ${chalk.bold('HybridObject<...>')}, and run ${chalk.bold('npx nitrogen')}.`
63
+ )
64
+ Logger.info(
65
+ `👉 To test your module in an app, create a new React Native app somewhere (e.g. in ${chalk.underline(`./${moduleName}/example`)}) and add ${chalk.bold(moduleName)} as a local dependency.`
66
+ )
67
+ }
68
+
69
+ async function isGitInstalled(): Promise<boolean> {
70
+ try {
71
+ execSync('git --version')
72
+ return true
73
+ } catch (error) {
74
+ return false
75
+ }
76
+ }
77
+
78
+ async function downloadGitHubFolder(
79
+ owner: string,
80
+ repo: string,
81
+ branch: string,
82
+ folder: string,
83
+ outputPath: string
84
+ ): Promise<string> {
85
+ if (!isGitInstalled()) {
86
+ console.error(
87
+ `❌ ${chalk.bold('git')} is not installed or available in the current path! Make sure to install ${chalk.bold('git')} and try again.`
88
+ )
89
+ process.exit(1)
90
+ }
91
+
92
+ const repoUrl = `https://github.com/${owner}/${repo}`
93
+ Logger.info(`⏳ Cloning ${chalk.underline(repoUrl)}...`)
94
+ const tempPath = randomUUID()
95
+ const tempDir = path.join(outputPath, tempPath)
96
+ execSync(
97
+ `git clone --depth 1 --filter=blob:none -b ${branch} --quiet --sparse ${repoUrl} ${tempDir}`
98
+ )
99
+ const prettyOutputPath = prettifyDirectory(outputPath)
100
+
101
+ const initialDir = process.cwd()
102
+ try {
103
+ process.chdir(tempDir)
104
+
105
+ Logger.debug(`⏳ Checking out ${chalk.underline(folder)}...`)
106
+ execSync(`git sparse-checkout set ${folder}`)
107
+
108
+ Logger.debug(
109
+ `📁 Copying files from ${chalk.underline(`${prettyOutputPath}/${tempPath}`)} to ${chalk.underline(prettyOutputPath)}...`
110
+ )
111
+ const sourcePath = path.join(tempDir, folder)
112
+ await fs.mkdir(outputPath, { recursive: true })
113
+ await copyFolder(sourcePath, outputPath)
114
+
115
+ return outputPath
116
+ } finally {
117
+ // change dir back to original
118
+ process.chdir(initialDir)
119
+
120
+ Logger.debug(
121
+ `🗑️ Removing temporary folder ${chalk.underline(prettifyDirectory(tempDir))}...`
122
+ )
123
+ await fs.rm(tempDir, { recursive: true, force: true })
124
+ }
125
+ }
126
+
127
+ async function copyFolder(src: string, dest: string) {
128
+ const entries = await fs.readdir(src, { withFileTypes: true })
129
+ for (const entry of entries) {
130
+ const srcPath = path.join(src, entry.name)
131
+ const destPath = path.join(dest, entry.name)
132
+ if (entry.isDirectory()) {
133
+ await fs.mkdir(destPath, { recursive: true })
134
+ await copyFolder(srcPath, destPath)
135
+ } else {
136
+ await fs.copyFile(srcPath, destPath)
137
+ }
138
+ }
139
+ }
140
+
141
+ async function replaceTemplate(
142
+ dir: string,
143
+ templateName: string,
144
+ replacementValue: string
145
+ ): Promise<void> {
146
+ const replaceInFile = async (filePath: string) => {
147
+ const content = await fs.readFile(filePath, 'utf8')
148
+ const updatedContent = content.replaceAll(
149
+ `$$${templateName}$$`,
150
+ replacementValue
151
+ )
152
+ if (content !== updatedContent) {
153
+ await fs.writeFile(filePath, updatedContent, 'utf8')
154
+ }
155
+ }
156
+
157
+ const replaceInName = (name: string) => {
158
+ return name.replaceAll(`$$${templateName}$$`, replacementValue)
159
+ }
160
+
161
+ const processDirectory = async (currentDir: string) => {
162
+ const entries = await fs.readdir(currentDir, { withFileTypes: true })
163
+
164
+ for (const entry of entries) {
165
+ const oldPath = path.join(currentDir, entry.name)
166
+ const updatedName = replaceInName(entry.name)
167
+ const newPath = path.join(currentDir, updatedName)
168
+
169
+ if (oldPath !== newPath) {
170
+ await fs.rename(oldPath, newPath)
171
+ }
172
+
173
+ if (entry.isDirectory()) {
174
+ await processDirectory(newPath)
175
+ } else if (entry.isFile()) {
176
+ await replaceInFile(newPath)
177
+ }
178
+ }
179
+ }
180
+
181
+ if (!existsSync(dir)) {
182
+ throw new Error(`The directory "${dir}" does not exist.`)
183
+ }
184
+
185
+ await processDirectory(dir)
186
+ }