@@ -5,13 +5,16 @@ import { createRequire } from "node:module";
55
66import { Command } from "@commander-js/extra-typings" ;
77
8- const XCFRAMEWORK_NAME = "node-api.xcframework" ;
8+ // Must be in all xcframeworks to be considered as Node-API modules
9+ const MAGIC_FILENAME = "react-native-node-api-module" ;
910
1011/**
1112 * Search upwards from a directory to find a package.json and
1213 * return a list of the resolved paths of all dependencies of that package.
1314 */
14- export function findPackageDependencyPaths ( from : string ) : string [ ] {
15+ export function findPackageDependencyPaths (
16+ from : string
17+ ) : Record < string , string > {
1518 const candidatePath = path . join ( from , "package.json" ) ;
1619 const parentDir = path . dirname ( from ) ;
1720 if ( fs . existsSync ( candidatePath ) ) {
@@ -28,19 +31,22 @@ export function findPackageDependencyPaths(from: string): string[] {
2831 typeof json . dependencies === "object" &&
2932 json . dependencies !== null
3033 ) {
31- return Object . keys ( json . dependencies )
32- . map ( ( dependencyName ) => {
33- try {
34- return path . dirname (
35- require . resolve ( `${ dependencyName } /package.json` )
36- ) ;
37- } catch {
38- return undefined ;
39- }
40- } )
41- . filter ( ( p ) => typeof p === "string" ) ;
34+ return Object . fromEntries (
35+ Object . keys ( json . dependencies )
36+ . map ( ( dependencyName ) => {
37+ try {
38+ return [
39+ dependencyName ,
40+ path . dirname ( require . resolve ( `${ dependencyName } /package.json` ) ) ,
41+ ] ;
42+ } catch {
43+ return undefined ;
44+ }
45+ } )
46+ . filter ( ( item ) => typeof item !== "undefined" )
47+ ) ;
4248 } else {
43- return [ ] ;
49+ return { } ;
4450 }
4551 } else if ( parentDir === from ) {
4652 throw new Error ( "package.json not found in any parent directory" ) ;
@@ -53,32 +59,73 @@ export function findXCFrameworkPaths(dependencyPath: string): string[] {
5359 return fs
5460 . readdirSync ( dependencyPath , { withFileTypes : true } )
5561 . flatMap ( ( file ) => {
56- if ( file . isDirectory ( ) ) {
57- if ( file . name === XCFRAMEWORK_NAME ) {
58- return [ path . join ( dependencyPath , file . name ) ] ;
59- } else {
60- // Traverse into the child directory
61- return findXCFrameworkPaths ( path . join ( dependencyPath , file . name ) ) ;
62- }
63- } else {
64- return [ ] ;
62+ if (
63+ file . isFile ( ) &&
64+ file . name === MAGIC_FILENAME &&
65+ path . extname ( dependencyPath ) === ".xcframework"
66+ ) {
67+ return [ dependencyPath ] ;
68+ } else if ( file . isDirectory ( ) ) {
69+ // Traverse into the child directory
70+ return findXCFrameworkPaths ( path . join ( dependencyPath , file . name ) ) ;
6571 }
72+ return [ ] ;
6673 } ) ;
6774}
6875
6976export const program = new Command ( "react-native-node-api-modules" ) ;
7077
7178program
72- . command ( "print -xcframework-paths" )
79+ . command ( "link -xcframework-paths" )
7380 . argument ( "<installation-root>" , "Parent directory of the Podfile" , ( p ) =>
7481 path . resolve ( process . cwd ( ) , p )
7582 )
7683 . action ( ( installationRoot : string ) => {
77- // Find the package.json of the app
78- const dependencyPaths = findPackageDependencyPaths ( installationRoot ) ;
79- const xcframeworkPaths = dependencyPaths . flatMap ( findXCFrameworkPaths ) ;
80- // Find all node-api.xcframeworks files in the dependencies
81- console . log ( JSON . stringify ( xcframeworkPaths , null , 2 ) ) ;
84+ // Find the location of each dependency
85+ const dependencyPathsByName = findPackageDependencyPaths ( installationRoot ) ;
86+ // Find all their xcframeworks
87+ const dependenciesByName = Object . fromEntries (
88+ Object . entries ( dependencyPathsByName )
89+ . map ( ( [ dependencyName , dependencyPath ] ) => {
90+ // Make all the xcframeworks relative to the dependency path
91+ const xcframeworkPaths = findXCFrameworkPaths ( dependencyPath ) . map (
92+ ( p ) => path . relative ( dependencyPath , p )
93+ ) ;
94+ return [
95+ dependencyName ,
96+ {
97+ path : dependencyPath ,
98+ xcframeworkPaths,
99+ } ,
100+ ] as const ;
101+ } )
102+ // Remove any dependencies without xcframeworks
103+ . filter ( ( [ , { xcframeworkPaths } ] ) => xcframeworkPaths . length > 0 )
104+ ) ;
105+ // To be able to reference the xcframeworks from the Podspec,
106+ // we need them as sub-directories of the Podspec parent directory.
107+ const outputPath = path . resolve (
108+ __dirname ,
109+ ".." ,
110+ ".." ,
111+ "vendored-xcframeworks"
112+ ) ;
113+ // Create or clean the output directory
114+ fs . rmSync ( outputPath , { recursive : true , force : true } ) ;
115+ fs . mkdirSync ( outputPath , { recursive : true } ) ;
116+ // Create symbolic links for each xcframework found in dependencies
117+ const linkedXcframeworkPaths = Object . entries ( dependenciesByName ) . flatMap (
118+ ( [ name , dependency ] ) => {
119+ return dependency . xcframeworkPaths . map ( ( xcframeworkPath ) => {
120+ const fromPath = path . join ( dependency . path , xcframeworkPath ) ;
121+ const linkedPath = path . join ( outputPath , name , xcframeworkPath ) ;
122+ fs . mkdirSync ( path . dirname ( linkedPath ) , { recursive : true } ) ;
123+ fs . symlinkSync ( fromPath , linkedPath , "dir" ) ;
124+ return linkedPath ;
125+ } ) ;
126+ }
127+ ) ;
128+ console . log ( JSON . stringify ( linkedXcframeworkPaths , null , 2 ) ) ;
82129 } ) ;
83130
84131export function run ( argv : string [ ] ) {
0 commit comments