Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/many-candies-retire.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"react-native-node-api": patch
---

Fix auto-linking from Gradle builds on Windows
5 changes: 5 additions & 0 deletions .changeset/silver-suits-double.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"react-native-node-api": minor
---

Assert that REACT_NATIVE_OVERRIDE_HERMES_DIR is set when Android / Gradle projects depend on the host package
2 changes: 2 additions & 0 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ name: Check
env:
# Version here should match the one in React Native template and packages/cmake-rn/src/cli.ts
NDK_VERSION: 27.1.12297006
# Enabling the Gradle test on CI (disabled by default because it downloads a lot)
ENABLE_GRADLE_TESTS: true

on:
push:
Expand Down
25 changes: 22 additions & 3 deletions packages/host/android/build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import java.nio.file.Paths
import groovy.json.JsonSlurper
import org.gradle.internal.os.OperatingSystem

buildscript {
ext.getExtOrDefault = {name ->
Expand Down Expand Up @@ -134,12 +135,30 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
}

task checkHermesOverride {
doFirst {
if (!System.getenv("REACT_NATIVE_OVERRIDE_HERMES_DIR")) {
throw new GradleException([
"React Native Node-API needs a custom version of Hermes with Node-API enabled.",
"Run the following in your terminal, to clone Hermes and instruct React Native to use it:",
"",
"export REACT_NATIVE_OVERRIDE_HERMES_DIR=`npx react-native-node-api vendor-hermes --silent --force`",
"",
"And follow this guide to build React Native from source:",
"https://reactnative.dev/contributing/how-to-build-from-source#update-your-project-to-build-from-source"
].join('\n'))
}
}
}

def commandLinePrefix = OperatingSystem.current().isWindows() ? ["cmd", "/c", "node"] : []
def cliPath = file("../bin/react-native-node-api.mjs")

// Custom task to fetch jniLibs paths via CLI
task linkNodeApiModules {
doLast {
exec {
// TODO: Support --strip-path-suffix
commandLine 'npx', 'react-native-node-api', 'link', '--android', rootProject.rootDir.absolutePath
commandLine commandLinePrefix + [cliPath, 'link', '--android', rootProject.rootDir.absolutePath]
standardOutput = System.out
errorOutput = System.err
// Enable color output
Expand All @@ -150,5 +169,5 @@ task linkNodeApiModules {
}
}

preBuild.dependsOn linkNodeApiModules
preBuild.dependsOn checkHermesOverride, linkNodeApiModules

1 change: 1 addition & 0 deletions packages/host/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"build-weak-node-api": "cmake-rn --no-auto-link --no-weak-node-api-linkage --xcframework-extension --source ./weak-node-api --out ./weak-node-api",
"build-weak-node-api:all-triplets": "cmake-rn --android --apple --no-auto-link --no-weak-node-api-linkage --xcframework-extension --source ./weak-node-api --out ./weak-node-api",
"test": "tsx --test --test-reporter=@reporters/github --test-reporter-destination=stdout --test-reporter=spec --test-reporter-destination=stdout src/node/**/*.test.ts src/node/*.test.ts",
"test:gradle": "ENABLE_GRADLE_TESTS=true node --run test",
"bootstrap": "node --run copy-node-api-headers && node --run generate-weak-node-api-injector && node --run generate-weak-node-api && node --run build-weak-node-api",
"prerelease": "node --run copy-node-api-headers && node --run generate-weak-node-api-injector && node --run generate-weak-node-api && node --run build-weak-node-api:all-triplets"
},
Expand Down
57 changes: 57 additions & 0 deletions packages/host/src/node/cli/bin.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import assert from "node:assert/strict";
import { describe, it } from "node:test";
import cp from "node:child_process";
import path from "node:path";

const PACKAGE_ROOT = path.join(__dirname, "../../..");
const BIN_PATH = path.join(PACKAGE_ROOT, "bin/react-native-node-api.mjs");

describe("bin", () => {
describe("help command", () => {
it("should succeed with a mention of usage", () => {
const { status, stdout, stderr } = cp.spawnSync(
process.execPath,
[BIN_PATH, "help"],
{
cwd: PACKAGE_ROOT,
encoding: "utf8",
},
);

assert.equal(
status,
0,
`Expected success (got ${status}): ${stdout} ${stderr}`,
);
assert.match(
stdout,
/Usage: react-native-node-api/,
`Failed to find expected output (stdout: ${stdout} stderr: ${stderr})`,
);
});
});

describe("link command", () => {
it("should succeed with a mention of Node-API modules", () => {
const { status, stdout, stderr } = cp.spawnSync(
process.execPath,
[BIN_PATH, "link", "--android", "--apple"],
{
cwd: PACKAGE_ROOT,
encoding: "utf8",
},
);

assert.equal(
status,
0,
`Expected success (got ${status}): ${stdout} ${stderr}`,
);
assert.match(
stdout + stderr,
/Auto-linking Node-API modules/,
`Failed to find expected output (stdout: ${stdout} stderr: ${stderr})`,
);
});
});
});
66 changes: 66 additions & 0 deletions packages/host/src/node/gradle.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import assert from "node:assert/strict";
import { describe, it } from "node:test";
import cp from "node:child_process";
import path from "node:path";

const PACKAGE_ROOT = path.join(__dirname, "../..");
const MONOREPO_ROOT = path.join(PACKAGE_ROOT, "../..");
const TEST_APP_ANDROID_PATH = path.join(MONOREPO_ROOT, "apps/test-app/android");

describe(
"Gradle tasks",
// Skipping these tests by default, as they download a lot and takes a long time
{ skip: process.env.ENABLE_GRADLE_TESTS !== "true" },
() => {
describe("checkHermesOverride task", () => {
it("should fail if REACT_NATIVE_OVERRIDE_HERMES_DIR is not set", () => {
const { status, stdout, stderr } = cp.spawnSync(
"sh",
["gradlew", "react-native-node-api:checkHermesOverride"],
{
cwd: TEST_APP_ANDROID_PATH,
env: {
...process.env,
REACT_NATIVE_OVERRIDE_HERMES_DIR: undefined,
},
encoding: "utf-8",
},
);

assert.notEqual(status, 0, `Expected failure: ${stdout} ${stderr}`);
assert.match(
stderr,
/React Native Node-API needs a custom version of Hermes with Node-API enabled/,
);
assert.match(
stderr,
/Run the following in your terminal, to clone Hermes and instruct React Native to use it/,
);
assert.match(
stderr,
/export REACT_NATIVE_OVERRIDE_HERMES_DIR=`npx react-native-node-api vendor-hermes --silent --force`/,
);
assert.match(
stderr,
/And follow this guide to build React Native from source/,
);
});
});

describe("linkNodeApiModules task", () => {
it("should call the CLI to autolink", () => {
const { status, stdout, stderr } = cp.spawnSync(
"sh",
["gradlew", "react-native-node-api:linkNodeApiModules"],
{
cwd: TEST_APP_ANDROID_PATH,
encoding: "utf-8",
},
);

assert.equal(status, 0, `Expected failure: ${stdout} ${stderr}`);
assert.match(stdout, /Auto-linking Node-API modules/);
});
});
},
);
Loading