Skip to content

Commit 42ce6b0

Browse files
authored
Merge pull request #11 from NativeScript/feat/napi
feat: napi support
2 parents 1fa5971 + 4e8da61 commit 42ce6b0

332 files changed

Lines changed: 233745 additions & 428 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/workflows/npm_release.yml‎

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
name: npm release
2+
3+
# Publishes the four engine variants — @nativescript/windows-{hermes,jsc,quickjs,v8} — to npm via
4+
# OIDC trusted publishing. npm matches a trusted publisher on repository + workflow FILENAME +
5+
# environment, so `npm_release.yml` and the `npm-publish` environment below are part of the
6+
# credential: renaming either breaks publishing for all four packages.
7+
#
8+
# NOTE: `paths` filters deliberately do NOT accompany the tag trigger — a tag push can have an
9+
# empty changed-file set, and paths+tags together silently skip the run.
10+
on:
11+
push:
12+
tags:
13+
- "v*" # -> all four variants at the tag's version
14+
workflow_dispatch:
15+
inputs:
16+
engine:
17+
description: "Which variant to release"
18+
type: choice
19+
required: true
20+
default: all
21+
options: [all, hermes, jsc, quickjs, v8]
22+
version:
23+
description: "Release version to cut, e.g. 0.1.0-alpha.2 (dist-tag follows the prerelease id; a plain version publishes 'latest'). Leave empty for a rolling 'next' build."
24+
required: false
25+
default: ""
26+
rebuild_framework:
27+
description: "Rebuild the shared framework (tools + dotnet-bridge) from source instead of shipping the copies committed under template/framework."
28+
type: boolean
29+
required: false
30+
default: false
31+
32+
# Minimal default token permissions for every job; the publish job declares id-token:write locally.
33+
permissions:
34+
contents: read
35+
36+
# A variant is the same framework as @nativescript/windows — the WinUI 3 app template,
37+
# dotnet-bridge and tools — with the engine's runtime DLL staged as framework/libs/x64/
38+
# nativescript.dll. `template/build.ps1 -Engine <engine>` copies that shared scaffolding out of
39+
# template/framework and builds the engine cdylib into it, so a release is:
40+
#
41+
# setup (resolve version + engine matrix) -> build (one Windows job per engine, npm pack)
42+
# -> publish (npm, OIDC)
43+
#
44+
# Everything the shared framework needs is committed under template/framework, so the default build
45+
# only compiles the engine. `rebuild_framework` regenerates that scaffolding from source instead.
46+
jobs:
47+
setup:
48+
name: Resolve version
49+
runs-on: ubuntu-latest
50+
outputs:
51+
npm_version: ${{ steps.out.outputs.NPM_VERSION }}
52+
npm_tag: ${{ steps.out.outputs.NPM_TAG }}
53+
build_matrix: ${{ steps.out.outputs.BUILD_MATRIX }}
54+
steps:
55+
- name: Harden the runner (Audit all outbound calls)
56+
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
57+
with:
58+
egress-policy: audit
59+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
60+
with:
61+
persist-credentials: false
62+
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
63+
with:
64+
node-version: 22
65+
# setup-node v6 enables dependency caching by default when it detects a package manager —
66+
# a cache-poisoning vector, worst in the jobs that publish. Disabled on every setup-node
67+
# step in this workflow (also keeps pre-v6 behavior).
68+
package-manager-cache: false
69+
- name: Compute version, dist-tag and engine matrix
70+
id: out
71+
env:
72+
# env indirection keeps the dispatch inputs out of shell interpolation
73+
INPUT_VERSION: ${{ inputs.version }}
74+
INPUT_ENGINE: ${{ inputs.engine || 'all' }}
75+
run: node ./scripts/resolve-release.mjs --version "$INPUT_VERSION" --engine "$INPUT_ENGINE"
76+
77+
build:
78+
name: Build ${{ matrix.engine }}
79+
runs-on: windows-latest
80+
needs: setup
81+
strategy:
82+
fail-fast: false
83+
# { include: [{ engine: hermes }, ...] } — the engines selected in `setup`.
84+
matrix: ${{ fromJSON(needs.setup.outputs.build_matrix) }}
85+
env:
86+
NPM_VERSION: ${{ needs.setup.outputs.npm_version }}
87+
ENGINE: ${{ matrix.engine }}
88+
steps:
89+
- name: Harden the runner (Audit all outbound calls)
90+
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
91+
with:
92+
egress-policy: audit
93+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
94+
with:
95+
persist-credentials: false # disable to prevent leaking credentials to build scripts
96+
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
97+
with:
98+
node-version: 22
99+
package-manager-cache: false
100+
- uses: dtolnay/rust-toolchain@4cda84d5c5c54efe2404f9d843567869ab1699d4 # stable
101+
with:
102+
toolchain: stable
103+
- uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
104+
with:
105+
# The engine packages are excluded from the root workspace (their C builds / prebuilt
106+
# engine links must not run on a plain `cargo build`), so each has its own target dir.
107+
workspaces: |
108+
. -> target
109+
packages/windows-${{ matrix.engine }} -> target
110+
key: ${{ matrix.engine }}
111+
# quickjs' napi shim uses clang/GCC C extensions that MSVC `cl` rejects; build.rs compiles it
112+
# with clang-cl from this exact path and silently falls back to `cl` (which then fails deep in
113+
# the C build) when it is absent. Fail early with the real reason instead.
114+
- name: Ensure clang-cl (quickjs shim)
115+
if: ${{ matrix.engine == 'quickjs' }}
116+
shell: pwsh
117+
run: |
118+
$ErrorActionPreference = 'Stop'
119+
$clangCl = 'C:\Program Files\LLVM\bin\clang-cl.exe'
120+
if (-not (Test-Path $clangCl)) {
121+
Write-Host "clang-cl not found at $clangCl — installing LLVM."
122+
choco install llvm --no-progress -y
123+
}
124+
if (-not (Test-Path $clangCl)) {
125+
throw "clang-cl is required to compile the quickjs napi shim and is not at $clangCl."
126+
}
127+
& $clangCl --version
128+
- name: Setup .NET (framework rebuild)
129+
if: ${{ inputs.rebuild_framework }}
130+
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
131+
with:
132+
# dotnet-typings-gen targets net10.0; ManifestMerger is netstandard2.0 and builds on it.
133+
dotnet-version: "10.x"
134+
- name: Rebuild shared framework
135+
if: ${{ inputs.rebuild_framework }}
136+
shell: pwsh
137+
# -SkipRelease -SkipDevtools: the classic V8 nativescript.dll is not staged into a variant
138+
# (the engine cdylib takes its place), so building it here would be thrown away.
139+
# -SkipArm64: the variants are x64-only (`cpu: ["x64"]` in their package.json).
140+
run: |
141+
$ErrorActionPreference = 'Stop'
142+
./template/build.ps1 -SkipArm64 -SkipRelease -SkipDevtools
143+
- name: Set package version
144+
shell: pwsh
145+
working-directory: packages/windows-${{ matrix.engine }}
146+
run: npm version $env:NPM_VERSION --no-git-tag-version --allow-same-version
147+
- name: Build ${{ matrix.engine }} framework
148+
shell: pwsh
149+
run: |
150+
$ErrorActionPreference = 'Stop'
151+
# build.ps1 reports through Write-Host, so the log has to be teed off the information
152+
# stream (`*>&1`) rather than stdout for the WARN check below to see anything.
153+
$logPath = Join-Path $env:RUNNER_TEMP "build-$env:ENGINE.log"
154+
./template/build.ps1 -Engine $env:ENGINE *>&1 | Tee-Object -FilePath $logPath
155+
# A missing engine runtime DLL (hermes.dll, JavaScriptCore.dll, …) beside the cdylib is
156+
# only a warning there, but it ships a framework that fails to load at app start.
157+
if (Select-String -Path $logPath -Pattern 'expected engine DLL not found' -Quiet) {
158+
throw "An engine runtime DLL was not staged — see the WARN above."
159+
}
160+
$dll = "packages/windows-$env:ENGINE/framework/libs/x64/nativescript.dll"
161+
if (-not (Test-Path $dll)) { throw "Runtime DLL missing: $dll" }
162+
- name: Pack
163+
shell: pwsh
164+
working-directory: packages/windows-${{ matrix.engine }}
165+
run: |
166+
$ErrorActionPreference = 'Stop'
167+
# --pack-destination does not create the directory.
168+
New-Item -ItemType Directory -Force -Path ../../dist | Out-Null
169+
npm pack --pack-destination ../../dist
170+
- name: Upload npm package artifact
171+
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
172+
with:
173+
name: npm-package-${{ matrix.engine }}
174+
path: dist/nativescript-windows-${{ matrix.engine }}-${{ needs.setup.outputs.npm_version }}.tgz
175+
if-no-files-found: error
176+
177+
publish:
178+
name: Publish ${{ matrix.engine }}
179+
runs-on: ubuntu-latest
180+
# Part of the npm trusted-publisher match — see the header note before renaming.
181+
environment: npm-publish
182+
needs:
183+
- setup
184+
- build
185+
permissions:
186+
contents: read
187+
id-token: write # OIDC trusted publishing + provenance
188+
strategy:
189+
fail-fast: false
190+
matrix: ${{ fromJSON(needs.setup.outputs.build_matrix) }}
191+
env:
192+
NPM_VERSION: ${{ needs.setup.outputs.npm_version }}
193+
NPM_TAG: ${{ needs.setup.outputs.npm_tag }}
194+
ENGINE: ${{ matrix.engine }}
195+
steps:
196+
- name: Harden the runner (Audit all outbound calls)
197+
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
198+
with:
199+
egress-policy: audit
200+
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
201+
with:
202+
node-version: 22
203+
package-manager-cache: false
204+
registry-url: "https://registry.npmjs.org"
205+
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
206+
with:
207+
name: npm-package-${{ matrix.engine }}
208+
path: dist
209+
- name: Update npm (required for OIDC trusted publishing)
210+
run: |
211+
corepack enable npm
212+
corepack install -g npm@11.5.1
213+
test "$(npm --version)" = "11.5.1"
214+
test "$(npx --version)" = "11.5.1"
215+
- name: Publish package (OIDC trusted publishing)
216+
if: ${{ vars.USE_NPM_TOKEN != 'true' }}
217+
run: |
218+
echo "Publishing @nativescript/windows-$ENGINE@$NPM_VERSION to NPM with tag $NPM_TAG via OIDC trusted publishing..."
219+
unset NODE_AUTH_TOKEN
220+
if [ -n "${NPM_CONFIG_USERCONFIG:-}" ]; then
221+
rm -f "$NPM_CONFIG_USERCONFIG"
222+
fi
223+
npm publish "./dist/nativescript-windows-${ENGINE}-${NPM_VERSION}.tgz" --tag "$NPM_TAG" --access public --provenance
224+
env:
225+
NODE_AUTH_TOKEN: ""
226+
- name: Publish package (granular token)
227+
if: ${{ vars.USE_NPM_TOKEN == 'true' }}
228+
run: |
229+
echo "Publishing @nativescript/windows-$ENGINE@$NPM_VERSION to NPM with tag $NPM_TAG via granular token..."
230+
npm publish "./dist/nativescript-windows-${ENGINE}-${NPM_VERSION}.tgz" --tag "$NPM_TAG" --access public --provenance
231+
env:
232+
NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}

‎.github/workflows/windows-napi.yml‎

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
name: windows-napi
2+
3+
# NOTE: `paths` filters deliberately do NOT accompany the tag trigger — a tag push can have an
4+
# empty changed-file set, and paths+tags together silently skip the run.
5+
on:
6+
push:
7+
tags:
8+
- 'windows-napi-v*'
9+
pull_request:
10+
paths:
11+
- 'windows-napi/**'
12+
- 'runtime/**'
13+
- 'metadata/**'
14+
- '.github/workflows/windows-napi.yml'
15+
workflow_dispatch:
16+
17+
env:
18+
CARGO_TERM_COLOR: always
19+
20+
jobs:
21+
build:
22+
runs-on: windows-latest
23+
strategy:
24+
fail-fast: false
25+
matrix:
26+
settings:
27+
- target: x86_64-pc-windows-msvc
28+
- target: aarch64-pc-windows-msvc
29+
name: build ${{ matrix.settings.target }}
30+
steps:
31+
- uses: actions/checkout@v4
32+
- uses: actions/setup-node@v4
33+
with:
34+
node-version: 20
35+
- uses: dtolnay/rust-toolchain@stable
36+
with:
37+
targets: ${{ matrix.settings.target }}
38+
- uses: Swatinem/rust-cache@v2
39+
with:
40+
key: ${{ matrix.settings.target }}
41+
- name: Install npm dependencies
42+
working-directory: windows-napi
43+
run: npm ci
44+
- name: Build .node
45+
working-directory: windows-napi
46+
run: npx napi build --platform --release --target ${{ matrix.settings.target }}
47+
- name: Run test suite (x64 only — the runner cannot execute arm64 binaries)
48+
if: matrix.settings.target == 'x86_64-pc-windows-msvc'
49+
working-directory: windows-napi
50+
# Server runner SKUs can lack optional feature packs some suites touch (e.g. Media
51+
# Foundation for the MediaPlayer event test), so failures here warn instead of block;
52+
# the authoritative run is the local one on a client SKU.
53+
continue-on-error: true
54+
run: npm test
55+
- uses: actions/upload-artifact@v4
56+
with:
57+
name: bindings-${{ matrix.settings.target }}
58+
path: windows-napi/windows.*.node
59+
if-no-files-found: error
60+
61+
publish:
62+
name: publish to npm
63+
if: startsWith(github.ref, 'refs/tags/windows-napi-v')
64+
needs: build
65+
runs-on: windows-latest
66+
steps:
67+
- uses: actions/checkout@v4
68+
- uses: actions/setup-node@v4
69+
with:
70+
node-version: 20
71+
registry-url: https://registry.npmjs.org
72+
- name: Install npm dependencies
73+
working-directory: windows-napi
74+
run: npm ci
75+
- name: Download all binding artifacts
76+
uses: actions/download-artifact@v4
77+
with:
78+
path: windows-napi/artifacts
79+
- name: Distribute artifacts into npm/<triple> sub-packages
80+
working-directory: windows-napi
81+
run: npx napi artifacts
82+
- name: Publish
83+
working-directory: windows-napi
84+
# `napi prepublish` (prepublishOnly script) publishes each npm/<triple> sub-package,
85+
# then the root package publishes with them as optionalDependencies.
86+
run: npm publish --access public
87+
env:
88+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

‎.gitignore‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
1+
# Rust build artifacts (root + nested workspace/package crates)
12
/target
3+
target/
24
/**/*.rs.bk
35
/Cargo.lock
6+
7+
# Node
8+
node_modules/
9+
10+
# Logs
11+
*.log
12+
413
.DS_Store
514
.vs
615

‎Cargo.toml‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
[workspace]
22
resolver = "2"
3-
members = ["metadata", "playground", "runtime", "runtime-binding-gen", "sbg", "nativescript", "typings-generator", "integration-tests", "runtime-devtools", "metadata-generator","tools/dotnet-tool"]
3+
members = ["metadata", "playground", "runtime", "runtime-binding-gen", "sbg", "nativescript", "typings-generator", "integration-tests", "runtime-devtools", "metadata-generator","tools/dotnet-tool", "windows-napi"]
4+
# Excluded so their C builds / prebuilt-engine links don't run on normal `cargo` invocations.
5+
exclude = ["packages/common", "packages/demo", "packages/windows-quickjs", "packages/windows-hermes", "packages/windows-jsc", "packages/windows-v8"]
46

57
[workspace.dependencies]
68
windows = "0.62.2"

0 commit comments

Comments
 (0)