diff --git a/.changeset/many-pens-join.md b/.changeset/many-pens-join.md
new file mode 100644
index 00000000..1aa8aa8e
--- /dev/null
+++ b/.changeset/many-pens-join.md
@@ -0,0 +1,8 @@
+---
+"ferric-cli": minor
+"@react-native-node-api/test-app": minor
+---
+
+- Created `init` command to generate / scaffold the project.
+- Replacing `ferric-example` with `app-test`, scaffolding the project there.
+- Added `Cargo.toml` optimizations for size.
diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml
index 52aa06c7..a7a53aac 100644
--- a/.github/workflows/check.yml
+++ b/.github/workflows/check.yml
@@ -41,7 +41,6 @@ jobs:
# Bootstrap host package to get weak-node-api and ferric-example to get types
# TODO: Solve this by adding an option to ferric to build only types or by committing the types into the repo as a fixture for an "init" command
- run: npm run bootstrap --workspace react-native-node-api
- - run: npm run bootstrap --workspace @react-native-node-api/ferric-example
- run: npm run lint
env:
DEBUG: eslint:eslint
@@ -152,9 +151,9 @@ jobs:
- name: Build weak-node-api for all architectures
run: npm run build-weak-node-api -- --android
working-directory: packages/host
- - name: Build ferric-example for all architectures
+ - name: Build lib_in_rust for android architectures
run: npm run build -- --android
- working-directory: packages/ferric-example
+ working-directory: apps/test-app/lib_in_rust
- name: Run tests (Android)
timeout-minutes: 75
uses: reactivecircus/android-emulator-runner@v2
diff --git a/apps/test-app/App.tsx b/apps/test-app/App.tsx
index 62c59944..3efcc002 100644
--- a/apps/test-app/App.tsx
+++ b/apps/test-app/App.tsx
@@ -23,14 +23,12 @@ type Context = {
allTests?: boolean;
nodeAddonExamples?: boolean;
nodeTests?: boolean;
- ferricExample?: boolean;
};
function loadTests({
allTests = false,
nodeAddonExamples = allTests,
nodeTests = allTests,
- ferricExample = allTests,
}: Context) {
describeIf(nodeAddonExamples, "Node Addon Examples", () => {
for (const [suiteName, examples] of Object.entries(
@@ -67,18 +65,6 @@ function loadTests({
registerTestSuite(nodeTestsSuites);
});
-
- describeIf(ferricExample, "ferric-example", () => {
- it("exports a callable sum function", () => {
- const exampleAddon =
- /* eslint-disable-next-line @typescript-eslint/no-require-imports -- TODO: Determine why a dynamic import doesn't work on Android */
- require("ferric-example") as typeof import("ferric-example");
- const result = exampleAddon.sum(1, 3);
- if (result !== 4) {
- throw new Error(`Expected 1 + 3 to equal 4, but got ${result}`);
- }
- });
- });
}
export default function App() {
diff --git a/apps/test-app/lib_in_rust/.gitignore b/apps/test-app/lib_in_rust/.gitignore
new file mode 100644
index 00000000..24a51298
--- /dev/null
+++ b/apps/test-app/lib_in_rust/.gitignore
@@ -0,0 +1,8 @@
+target
+Cargo.lock
+
+*.xcframework/
+*.apple.node/
+*.android.node/
+
+dist
diff --git a/packages/ferric-example/Cargo.toml b/apps/test-app/lib_in_rust/Cargo.toml
similarity index 87%
rename from packages/ferric-example/Cargo.toml
rename to apps/test-app/lib_in_rust/Cargo.toml
index 2524a0e8..a743d2d6 100644
--- a/packages/ferric-example/Cargo.toml
+++ b/apps/test-app/lib_in_rust/Cargo.toml
@@ -1,5 +1,5 @@
[package]
-name = "ferric-example"
+name = "lib_in_rust"
version = "1.0.0"
edition = "2021"
license = "MIT"
@@ -23,3 +23,5 @@ napi-build = "2"
[profile.release]
lto = true
codegen-units = 1
+strip = "symbols"
+opt-level = "z"
diff --git a/packages/ferric-example/build.rs b/apps/test-app/lib_in_rust/build.rs
similarity index 100%
rename from packages/ferric-example/build.rs
rename to apps/test-app/lib_in_rust/build.rs
diff --git a/apps/test-app/lib_in_rust/package.json b/apps/test-app/lib_in_rust/package.json
new file mode 100644
index 00000000..bcf6bc14
--- /dev/null
+++ b/apps/test-app/lib_in_rust/package.json
@@ -0,0 +1,14 @@
+{
+ "name": "lib_in_rust",
+ "private": true,
+ "version": "0.1.0",
+ "main": "dist/lib_in_rust.js",
+ "types": "dist/lib_in_rust.d.ts",
+ "scripts": {
+ "build": "ferric build",
+ "build:release": "npm run build -- --configuration release"
+ },
+ "devDependencies": {
+ "ferric-cli": "latest"
+ }
+}
diff --git a/packages/ferric-example/src/lib.rs b/apps/test-app/lib_in_rust/src/lib.rs
similarity index 87%
rename from packages/ferric-example/src/lib.rs
rename to apps/test-app/lib_in_rust/src/lib.rs
index ecfd7ab1..1064d5bf 100644
--- a/packages/ferric-example/src/lib.rs
+++ b/apps/test-app/lib_in_rust/src/lib.rs
@@ -2,5 +2,5 @@ use napi_derive::napi;
#[napi]
pub fn sum(a: i32, b: i32) -> i32 {
- a + b
+ a + b
}
diff --git a/apps/test-app/package.json b/apps/test-app/package.json
index f77dabf5..5d7edd24 100644
--- a/apps/test-app/package.json
+++ b/apps/test-app/package.json
@@ -15,8 +15,10 @@
"test:ios": "mocha-remote --exit-on-error -- concurrently --passthrough-arguments --kill-others-on-fail npm:metro 'npm:ios -- {@}' --",
"test:ios:allTests": "MOCHA_REMOTE_CONTEXT=allTests npm run test:ios -- ",
"test:ios:nodeAddonExamples": "MOCHA_REMOTE_CONTEXT=nodeAddonExamples npm run test:ios -- ",
- "test:ios:nodeTests": "MOCHA_REMOTE_CONTEXT=nodeTests npm run test:ios -- ",
- "test:ios:ferricExample": "MOCHA_REMOTE_CONTEXT=ferricExample npm run test:ios -- "
+ "gen:rs": "ferric init lib_in_rust",
+ "build:rs": "ferric build --cwd lib_in_rust",
+ "build:rs:release": "npm run build:rs -- --configuration release",
+ "test:ios:nodeTests": "MOCHA_REMOTE_CONTEXT=nodeTests npm run test:ios -- "
},
"dependencies": {
"@babel/core": "^7.26.10",
@@ -34,7 +36,7 @@
"@types/mocha": "^10.0.10",
"@types/react": "^19.0.0",
"concurrently": "^9.1.2",
- "ferric-example": "^0.1.0",
+ "ferric-cli": "*",
"mocha": "^11.6.0",
"mocha-remote-cli": "^1.13.2",
"mocha-remote-react-native": "^1.13.2",
diff --git a/eslint.config.js b/eslint.config.js
index c0af837e..33a3bb74 100644
--- a/eslint.config.js
+++ b/eslint.config.js
@@ -13,9 +13,6 @@ export default tseslint.config(
"apps/test-app/ios/**",
"packages/host/hermes/**",
"packages/node-addon-examples/examples/**",
- "packages/ferric-example/ferric_example.js",
- "packages/ferric-example/ferric_example.d.ts",
- "packages/ferric-example/target/**",
"packages/node-tests/node/**",
"packages/node-tests/tests/**",
"packages/node-tests/*.generated.js",
diff --git a/package-lock.json b/package-lock.json
index 051c13fb..22d3cfcd 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -13,8 +13,7 @@
"packages/ferric",
"packages/host",
"packages/node-addon-examples",
- "packages/node-tests",
- "packages/ferric-example"
+ "packages/node-tests"
],
"devDependencies": {
"@changesets/cli": "^2.29.5",
@@ -30,7 +29,7 @@
"prettier": "^3.6.2",
"react-native": "0.79.5",
"tsx": "^4.20.3",
- "typescript": "^5.8",
+ "typescript": "^5.8.0",
"typescript-eslint": "^8.38.0"
}
},
@@ -53,7 +52,7 @@
"@types/mocha": "^10.0.10",
"@types/react": "^19.0.0",
"concurrently": "^9.1.2",
- "ferric-example": "^0.1.0",
+ "ferric-cli": "*",
"mocha": "^11.6.0",
"mocha-remote-cli": "^1.13.2",
"mocha-remote-react-native": "^1.13.2",
@@ -4077,9 +4076,9 @@
}
},
"node_modules/@napi-rs/cli": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/@napi-rs/cli/-/cli-3.0.3.tgz",
- "integrity": "sha512-5PxtOcax9PoOriYWYf8BFNgOzUnsogYUDRM8p2leT2VtG/NTMZCyoRQMzSMB0qEaw+Durkefc5jXCpEF6wrWbA==",
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@napi-rs/cli/-/cli-3.1.1.tgz",
+ "integrity": "sha512-fwg/YgV0caxXwZjgq+jOadmb0R5apmlQ9XkU4r1StmgHs+1CXsiGJshVetOnbBMpJ7wdvFGfbFKkXGgTPBMMDg==",
"license": "MIT",
"dependencies": {
"@inquirer/prompts": "^7.4.0",
@@ -4094,8 +4093,7 @@
"js-yaml": "^4.1.0",
"lodash-es": "^4.17.21",
"semver": "^7.7.1",
- "typanion": "^3.14.0",
- "wasm-sjlj": "^1.0.6"
+ "typanion": "^3.14.0"
},
"bin": {
"napi": "dist/cli.js",
@@ -5892,10 +5890,6 @@
"node": "^12.20.0 || >=14"
}
},
- "node_modules/@react-native-node-api/ferric-example": {
- "resolved": "packages/ferric-example",
- "link": true
- },
"node_modules/@react-native-node-api/node-addon-examples": {
"resolved": "packages/node-addon-examples",
"link": true
@@ -8202,6 +8196,67 @@
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
"license": "MIT"
},
+ "node_modules/copyfiles": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/copyfiles/-/copyfiles-2.4.1.tgz",
+ "integrity": "sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "glob": "^7.0.5",
+ "minimatch": "^3.0.3",
+ "mkdirp": "^1.0.4",
+ "noms": "0.0.0",
+ "through2": "^2.0.1",
+ "untildify": "^4.0.0",
+ "yargs": "^16.1.0"
+ },
+ "bin": {
+ "copyfiles": "copyfiles",
+ "copyup": "copyfiles"
+ }
+ },
+ "node_modules/copyfiles/node_modules/cliui": {
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
+ "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^7.0.0"
+ }
+ },
+ "node_modules/copyfiles/node_modules/yargs": {
+ "version": "16.2.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
+ "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cliui": "^7.0.2",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.0",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^20.2.2"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/copyfiles/node_modules/yargs-parser": {
+ "version": "20.2.9",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
+ "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/core-js-compat": {
"version": "3.41.0",
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.41.0.tgz",
@@ -8215,6 +8270,13 @@
"url": "https://opencollective.com/core-js"
}
},
+ "node_modules/core-util-is": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
+ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/cosmiconfig": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz",
@@ -9083,10 +9145,6 @@
"resolved": "packages/ferric",
"link": true
},
- "node_modules/ferric-example": {
- "resolved": "packages/ferric-example",
- "link": true
- },
"node_modules/file-entry-cache": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
@@ -10143,6 +10201,13 @@
"node": ">=8"
}
},
+ "node_modules/isarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+ "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
@@ -11630,6 +11695,37 @@
"url": "https://github.com/sponsors/antelle"
}
},
+ "node_modules/noms": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/noms/-/noms-0.0.0.tgz",
+ "integrity": "sha512-lNDU9VJaOPxUmXcLb+HQFeUgQQPtMI24Gt6hgfuMHRJgMRHMF/qZ4HJD3GDru4sSw9IQl2jPjAYnQrdIeLbwow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "inherits": "^2.0.1",
+ "readable-stream": "~1.0.31"
+ }
+ },
+ "node_modules/noms/node_modules/readable-stream": {
+ "version": "1.0.34",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
+ "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "0.0.1",
+ "string_decoder": "~0.10.x"
+ }
+ },
+ "node_modules/noms/node_modules/string_decoder": {
+ "version": "0.10.31",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+ "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/normalize-package-data": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz",
@@ -12228,6 +12324,13 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
+ "node_modules/process-nextick-args": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/promise": {
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz",
@@ -13650,6 +13753,57 @@
"integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==",
"license": "MIT"
},
+ "node_modules/through2": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
+ "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "readable-stream": "~2.3.6",
+ "xtend": "~4.0.1"
+ }
+ },
+ "node_modules/through2/node_modules/isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/through2/node_modules/readable-stream": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
+ "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "node_modules/through2/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/through2/node_modules/string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
"node_modules/tmp": {
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
@@ -13933,6 +14087,16 @@
"node": ">= 0.8"
}
},
+ "node_modules/untildify": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz",
+ "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/update-browserslist-db": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
@@ -14054,12 +14218,6 @@
"makeerror": "1.0.12"
}
},
- "node_modules/wasm-sjlj": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/wasm-sjlj/-/wasm-sjlj-1.0.6.tgz",
- "integrity": "sha512-pjaKtLJejlWm6+okPV2X1A6nIsRDD4qeK97eCh8DP8KXi3Nzn/HY01vpHhZHlhDri12eZqipjm8HhdTVw+ATxw==",
- "license": "MIT"
- },
"node_modules/wcwidth": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
@@ -14205,6 +14363,16 @@
"async-limiter": "~1.0.0"
}
},
+ "node_modules/xtend": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4"
+ }
+ },
"node_modules/y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
@@ -14341,7 +14509,7 @@
"cmake-js": "^7.3.1",
"commander": "^13.1.0",
"ora": "^8.2.0",
- "react-native-node-api": "0.3.2"
+ "react-native-node-api": "*"
},
"bin": {
"cmake-rn": "bin/cmake-rn.js"
@@ -14569,7 +14737,7 @@
"version": "0.2.3",
"dependencies": {
"@commander-js/extra-typings": "^13.1.0",
- "@napi-rs/cli": "~3.0.3",
+ "@napi-rs/cli": "^3.1",
"bufout": "^0.3.2",
"chalk": "^5.4.1",
"commander": "^13.1.0",
@@ -14578,13 +14746,9 @@
},
"bin": {
"ferric": "bin/ferric.js"
- }
- },
- "packages/ferric-example": {
- "name": "@react-native-node-api/ferric-example",
- "version": "0.1.0",
+ },
"devDependencies": {
- "ferric-cli": "*"
+ "copyfiles": "^2.4.1"
}
},
"packages/ferric/node_modules/@commander-js/extra-typings": {
diff --git a/package.json b/package.json
index 677d8c50..51652bfd 100644
--- a/package.json
+++ b/package.json
@@ -10,8 +10,7 @@
"packages/ferric",
"packages/host",
"packages/node-addon-examples",
- "packages/node-tests",
- "packages/ferric-example"
+ "packages/node-tests"
],
"homepage": "https://github.com/callstackincubator/react-native-node-api#readme",
"scripts": {
diff --git a/packages/cmake-rn/package.json b/packages/cmake-rn/package.json
index 4f56012f..b272bddf 100644
--- a/packages/cmake-rn/package.json
+++ b/packages/cmake-rn/package.json
@@ -28,7 +28,7 @@
"cmake-js": "^7.3.1",
"commander": "^13.1.0",
"ora": "^8.2.0",
- "react-native-node-api": "0.3.2"
+ "react-native-node-api": "*"
},
"peerDependencies": {
"node-addon-api": "^8.3.1",
diff --git a/packages/ferric-example/.gitignore b/packages/ferric-example/.gitignore
deleted file mode 100644
index 31c8570c..00000000
--- a/packages/ferric-example/.gitignore
+++ /dev/null
@@ -1,10 +0,0 @@
-/target
-Cargo.lock
-
-/*.xcframework/
-/*.apple.node/
-/*.android.node/
-
-# Generated files
-/ferric_example.d.ts
-/ferric_example.js
diff --git a/packages/ferric-example/package.json b/packages/ferric-example/package.json
deleted file mode 100644
index 2365f4e5..00000000
--- a/packages/ferric-example/package.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
- "name": "@react-native-node-api/ferric-example",
- "private": true,
- "type": "commonjs",
- "version": "0.1.0",
- "homepage": "https://github.com/callstackincubator/react-native-node-api",
- "repository": {
- "type": "git",
- "url": "git+https://github.com/callstackincubator/react-native-node-api.git",
- "directory": "packages/ferric-example"
- },
- "main": "ferric_example.js",
- "types": "ferric_example.d.ts",
- "scripts": {
- "build": "ferric build",
- "bootstrap": "npm run build"
- },
- "devDependencies": {
- "ferric-cli": "*"
- }
-}
diff --git a/packages/ferric/README.md b/packages/ferric/README.md
index c61bdb9a..6bde1021 100644
--- a/packages/ferric/README.md
+++ b/packages/ferric/README.md
@@ -1,3 +1,62 @@
# `ferric`
A wrapper around Cargo making it easier to produce prebuilt binaries targeting iOS and Android matching the [the prebuilt binary specification](https://github.com/callstackincubator/react-native-node-api/blob/main/docs/PREBUILDS.md) as well as [napi.rs](https://napi.rs/) to generate bindings from annotated Rust code.
+
+## Install the project
+
+
+ npm
+
+```
+ npm install --save-dev ferric-cli
+```
+
+
+
+
+ pnpm
+
+```
+ pnpm install -D ferric-cli
+```
+
+
+
+
+ yarn
+
+```
+ yarn add -D ferric-cli
+```
+
+
+
+## Add scripts
+
+```json
+"scripts": {
+ "gen:rs": "ferric init lib_name",
+ "build": "ferric build --cwd lib_name",
+ "build:release": "npm run build -- --configuration release"
+}
+```
+
+### Project Structure
+
+After generating the project, a folder would be created with the following file structure for the lib.
+
+```markdown
+lib_name
+│ Cargo.toml
+│ build.rs
+│ .gitignore
+└───src
+│ │ lib.rs
+└───dist (after build)
+│ │ index.js
+│ │ index.d.ts
+│ │ native_android_folder
+│ │ native_ios_folder
+```
+
+> Example project seen in `apps/test-app/lib_in_rust`.
diff --git a/packages/ferric/package.json b/packages/ferric/package.json
index d862a1a9..c0803a69 100644
--- a/packages/ferric/package.json
+++ b/packages/ferric/package.json
@@ -13,15 +13,21 @@
"ferric": "./bin/ferric.js"
},
"scripts": {
- "start": "tsx src/run.ts"
+ "start": "tsx src/run.ts",
+ "build": "tsc --build && npm run copy-templates",
+ "test": "tsx --test --test-reporter=@reporters/github --test-reporter-destination=stdout --test-reporter=spec --test-reporter-destination=stdout src/**/*.test.ts",
+ "copy-templates": "copyfiles -u 1 src/templates/**/* dist"
},
"dependencies": {
- "@napi-rs/cli": "~3.0.3",
"@commander-js/extra-typings": "^13.1.0",
+ "@napi-rs/cli": "^3.1",
"bufout": "^0.3.2",
"chalk": "^5.4.1",
"commander": "^13.1.0",
- "react-native-node-api": "0.3.2",
- "ora": "^8.2.0"
+ "ora": "^8.2.0",
+ "react-native-node-api": "0.3.2"
+ },
+ "devDependencies": {
+ "copyfiles": "^2.4.1"
}
}
diff --git a/packages/ferric/src/init.ts b/packages/ferric/src/init.ts
new file mode 100644
index 00000000..c0420538
--- /dev/null
+++ b/packages/ferric/src/init.ts
@@ -0,0 +1,70 @@
+import { Command } from "@commander-js/extra-typings";
+import fs from "node:fs";
+import fsPromise from "node:fs/promises";
+import path from "node:path";
+import { oraPromise } from "ora";
+import { prettyPath } from "react-native-node-api";
+
+export const initCommand = new Command("init")
+ .description("Generate the project scaffold")
+ .argument("", "Type the project name")
+ .action(async (str) => {
+ const projectName = str && str.length > 0 ? str : "ferric_project";
+ const generatePath = path.join(process.cwd(), projectName);
+ await oraPromise(
+ generateProject({ outputPath: generatePath, projectName }),
+ {
+ text: "Generating project",
+ successText: `Generated project ${prettyPath(generatePath)}`,
+ failText: (error) => `Failed to generate the project: ${error.message}`,
+ },
+ );
+ });
+
+async function replaceStrInFile(
+ filePath: string,
+ oldStr: string,
+ newStr: string,
+) {
+ const content = await fsPromise.readFile(filePath, "utf8");
+ const updatedContent = content.replaceAll(oldStr, newStr);
+ await fsPromise.writeFile(filePath, updatedContent, "utf8");
+}
+
+function createFolder(path: string) {
+ if (!fs.existsSync(path)) {
+ fs.mkdirSync(path);
+ }
+}
+
+async function copyAllTemplateFiles(outputFilePath: string) {
+ const templateDir = path.join(import.meta.dirname, "templates");
+ await fsPromise.cp(templateDir, outputFilePath, {
+ recursive: true,
+ });
+ await moveFile(`${outputFilePath}/lib.rs`, `${outputFilePath}/src/lib.rs`);
+ await moveFile(`${outputFilePath}/gitignore`, `${outputFilePath}/.gitignore`);
+}
+
+async function moveFile(currentPath: string, newPath: string) {
+ await fsPromise.rename(currentPath, newPath);
+}
+
+async function generateProject({
+ outputPath,
+ projectName,
+}: {
+ outputPath: string;
+ projectName: string;
+}) {
+ createFolder(outputPath);
+ createFolder(`${outputPath}/src`);
+ await copyAllTemplateFiles(outputPath);
+ const strToReplace = "project_name";
+ await replaceStrInFile(
+ `${outputPath}/package.json`,
+ strToReplace,
+ projectName,
+ );
+ await replaceStrInFile(`${outputPath}/Cargo.toml`, strToReplace, projectName);
+}
diff --git a/packages/ferric/src/program.ts b/packages/ferric/src/program.ts
index f8059c48..0474fde2 100644
--- a/packages/ferric/src/program.ts
+++ b/packages/ferric/src/program.ts
@@ -2,8 +2,10 @@ import { Command } from "@commander-js/extra-typings";
import { printBanner } from "./banner.js";
import { buildCommand } from "./build.js";
+import { initCommand } from "./init.js";
export const program = new Command("ferric")
.hook("preAction", () => printBanner())
.description("Rust Node-API Modules for React Native")
- .addCommand(buildCommand);
+ .addCommand(buildCommand)
+ .addCommand(initCommand);
diff --git a/packages/ferric/src/templates/Cargo.toml b/packages/ferric/src/templates/Cargo.toml
new file mode 100644
index 00000000..06d60062
--- /dev/null
+++ b/packages/ferric/src/templates/Cargo.toml
@@ -0,0 +1,27 @@
+[package]
+name = "project_name"
+version = "1.0.0"
+edition = "2021"
+license = "MIT"
+
+[lib]
+crate-type = ["cdylib"]
+
+[dependencies.napi]
+version = "3"
+default-features = false
+# see https://nodejs.org/api/n-api.html#node-api-version-matrix
+features = ["napi3"]
+
+[dependencies.napi-derive]
+version = "3"
+features = ["type-def"]
+
+[build-dependencies]
+napi-build = "2"
+
+[profile.release]
+lto = true
+codegen-units = 1
+strip = "symbols"
+opt-level = "z"
diff --git a/packages/ferric/src/templates/build.rs b/packages/ferric/src/templates/build.rs
new file mode 100644
index 00000000..bbfc9e4b
--- /dev/null
+++ b/packages/ferric/src/templates/build.rs
@@ -0,0 +1,3 @@
+fn main() {
+ napi_build::setup();
+}
diff --git a/packages/ferric/src/templates/gitignore b/packages/ferric/src/templates/gitignore
new file mode 100644
index 00000000..24a51298
--- /dev/null
+++ b/packages/ferric/src/templates/gitignore
@@ -0,0 +1,8 @@
+target
+Cargo.lock
+
+*.xcframework/
+*.apple.node/
+*.android.node/
+
+dist
diff --git a/packages/ferric/src/templates/lib.rs b/packages/ferric/src/templates/lib.rs
new file mode 100644
index 00000000..1064d5bf
--- /dev/null
+++ b/packages/ferric/src/templates/lib.rs
@@ -0,0 +1,6 @@
+use napi_derive::napi;
+
+#[napi]
+pub fn sum(a: i32, b: i32) -> i32 {
+ a + b
+}
diff --git a/packages/ferric/src/templates/package.json b/packages/ferric/src/templates/package.json
new file mode 100644
index 00000000..516e41cc
--- /dev/null
+++ b/packages/ferric/src/templates/package.json
@@ -0,0 +1,14 @@
+{
+ "name": "project_name",
+ "private": true,
+ "version": "0.1.0",
+ "main": "dist/project_name.js",
+ "types": "dist/project_name.d.ts",
+ "scripts": {
+ "build": "ferric build",
+ "build:release": "npm run build -- --configuration release"
+ },
+ "devDependencies": {
+ "ferric-cli": "latest"
+ }
+}
diff --git a/packages/ferric/tsconfig.json b/packages/ferric/tsconfig.json
index a678ba53..02cf7169 100644
--- a/packages/ferric/tsconfig.json
+++ b/packages/ferric/tsconfig.json
@@ -5,6 +5,7 @@
"declarationMap": true,
"outDir": "dist",
"rootDir": "src",
+ "esModuleInterop": true,
"types": ["node"]
},
"include": ["src/**/*.ts"],