@napi-rs/cli 3.9.0 → 3.9.1
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.
- package/dist/cli.js +9087 -8769
- package/dist/index.cjs +9078 -8760
- package/dist/index.d.cts +16 -16
- package/dist/index.d.ts +16 -16
- package/dist/index.js +9087 -8769
- package/package.json +4 -5
- package/src/api/__tests__/__snapshots__/templates.spec.ts.md +9 -13
- package/src/api/__tests__/__snapshots__/templates.spec.ts.snap +0 -0
- package/src/api/__tests__/build.spec.ts +194 -0
- package/src/api/__tests__/create-npm-dirs.spec.ts +4 -3
- package/src/api/__tests__/templates.spec.ts +62 -1
- package/src/api/build.ts +124 -4
- package/src/api/create-npm-dirs.ts +2 -1
- package/src/api/new.ts +13 -18
- package/src/api/pre-publish.ts +5 -10
- package/src/api/rename.ts +10 -21
- package/src/api/templates/js-binding.ts +7 -8
- package/src/api/templates/load-wasi-template.ts +20 -17
- package/src/api/templates/wasi-worker-template.ts +36 -31
- package/src/commands/build.ts +1 -1
- package/src/utils/__tests__/__snapshots__/typegen.spec.ts.md +18 -28
- package/src/utils/__tests__/__snapshots__/typegen.spec.ts.snap +0 -0
- package/src/utils/__tests__/misc.spec.ts +4 -0
- package/src/utils/__tests__/serialize.spec.ts +55 -0
- package/src/utils/__tests__/target.spec.ts +221 -0
- package/src/utils/__tests__/typegen.spec.ts +115 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/misc.ts +4 -5
- package/src/utils/serialize.ts +47 -0
- package/src/utils/target.ts +150 -1
- package/src/utils/typegen.ts +241 -30
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@napi-rs/cli",
|
|
3
|
-
"version": "3.9.
|
|
3
|
+
"version": "3.9.1",
|
|
4
4
|
"description": "Cli tools for napi-rs",
|
|
5
5
|
"author": "LongYinan <lynweklm@gmail.com>",
|
|
6
6
|
"homepage": "https://napi.rs/",
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"clipanion": "^4.0.0-rc.4",
|
|
69
69
|
"colorette": "^2.0.20",
|
|
70
70
|
"es-toolkit": "^1.47.0",
|
|
71
|
-
"js-yaml": "^
|
|
71
|
+
"js-yaml": "^5.0.0",
|
|
72
72
|
"obug": "^2.1.2",
|
|
73
73
|
"semver": "^7.8.2",
|
|
74
74
|
"typanion": "^3.14.0",
|
|
@@ -87,9 +87,8 @@
|
|
|
87
87
|
"emnapi": "^2.0.0-alpha.4",
|
|
88
88
|
"empathic": "^2.0.1",
|
|
89
89
|
"env-paths": "^4.0.0",
|
|
90
|
-
"oxc-parser": "^0.
|
|
91
|
-
"
|
|
92
|
-
"tsdown": "^0.22.2",
|
|
90
|
+
"oxc-parser": "^0.149.0",
|
|
91
|
+
"tsdown": "^0.23.0",
|
|
93
92
|
"tslib": "^2.8.1"
|
|
94
93
|
},
|
|
95
94
|
"peerDependencies": {
|
|
@@ -61,11 +61,11 @@ Generated by [AVA](https://avajs.dev).
|
|
|
61
61
|
let __emnapiWasmEnvCleanupDrainPromise␊
|
|
62
62
|
let __wasiDisposed = false␊
|
|
63
63
|
let __wasiDisposePromise␊
|
|
64
|
-
let __completeWasiDisposal = function() {}␊
|
|
64
|
+
let __completeWasiDisposal = function () {}␊
|
|
65
65
|
// Overridden by loader flavors that have a last-resort reclaim for a rollback␊
|
|
66
66
|
// that stopped short of destroying the context. See␊
|
|
67
67
|
// \`__rollbackWasiInitialization\`.␊
|
|
68
|
-
let __retainWasiRollbackForRetry = function() {}␊
|
|
68
|
+
let __retainWasiRollbackForRetry = function () {}␊
|
|
69
69
|
␊
|
|
70
70
|
function __isThenable(value) {␊
|
|
71
71
|
return (␊
|
|
@@ -669,11 +669,11 @@ Generated by [AVA](https://avajs.dev).
|
|
|
669
669
|
let __emnapiWasmEnvCleanupDrainPromise␊
|
|
670
670
|
let __wasiDisposed = false␊
|
|
671
671
|
let __wasiDisposePromise␊
|
|
672
|
-
let __completeWasiDisposal = function() {}␊
|
|
672
|
+
let __completeWasiDisposal = function () {}␊
|
|
673
673
|
// Overridden by loader flavors that have a last-resort reclaim for a rollback␊
|
|
674
674
|
// that stopped short of destroying the context. See␊
|
|
675
675
|
// \`__rollbackWasiInitialization\`.␊
|
|
676
|
-
let __retainWasiRollbackForRetry = function() {}␊
|
|
676
|
+
let __retainWasiRollbackForRetry = function () {}␊
|
|
677
677
|
␊
|
|
678
678
|
function __isThenable(value) {␊
|
|
679
679
|
return (␊
|
|
@@ -1296,11 +1296,11 @@ Generated by [AVA](https://avajs.dev).
|
|
|
1296
1296
|
let __emnapiWasmEnvCleanupDrainPromise␊
|
|
1297
1297
|
let __wasiDisposed = false␊
|
|
1298
1298
|
let __wasiDisposePromise␊
|
|
1299
|
-
let __completeWasiDisposal = function() {}␊
|
|
1299
|
+
let __completeWasiDisposal = function () {}␊
|
|
1300
1300
|
// Overridden by loader flavors that have a last-resort reclaim for a rollback␊
|
|
1301
1301
|
// that stopped short of destroying the context. See␊
|
|
1302
1302
|
// \`__rollbackWasiInitialization\`.␊
|
|
1303
|
-
let __retainWasiRollbackForRetry = function() {}␊
|
|
1303
|
+
let __retainWasiRollbackForRetry = function () {}␊
|
|
1304
1304
|
␊
|
|
1305
1305
|
function __isThenable(value) {␊
|
|
1306
1306
|
return (␊
|
|
@@ -1880,10 +1880,9 @@ Generated by [AVA](https://avajs.dev).
|
|
|
1880
1880
|
// eslint-disable-next-line no-console␊
|
|
1881
1881
|
console.log.apply(console, arguments)␊
|
|
1882
1882
|
},␊
|
|
1883
|
-
printErr: function() {␊
|
|
1883
|
+
printErr: function () {␊
|
|
1884
1884
|
// eslint-disable-next-line no-console␊
|
|
1885
1885
|
console.error.apply(console, arguments)␊
|
|
1886
|
-
␊
|
|
1887
1886
|
},␊
|
|
1888
1887
|
})␊
|
|
1889
1888
|
return instantiateNapiModuleSync(wasmModule, {␊
|
|
@@ -1904,7 +1903,6 @@ Generated by [AVA](https://avajs.dev).
|
|
|
1904
1903
|
},␊
|
|
1905
1904
|
})␊
|
|
1906
1905
|
},␊
|
|
1907
|
-
␊
|
|
1908
1906
|
})␊
|
|
1909
1907
|
␊
|
|
1910
1908
|
globalThis.onmessage = function (e) {␊
|
|
@@ -1933,10 +1931,9 @@ Generated by [AVA](https://avajs.dev).
|
|
|
1933
1931
|
// eslint-disable-next-line no-console␊
|
|
1934
1932
|
console.log.apply(console, arguments)␊
|
|
1935
1933
|
},␊
|
|
1936
|
-
printErr: function() {␊
|
|
1934
|
+
printErr: function () {␊
|
|
1937
1935
|
// eslint-disable-next-line no-console␊
|
|
1938
1936
|
console.error.apply(console, arguments)␊
|
|
1939
|
-
␊
|
|
1940
1937
|
errorOutputs.push([...arguments])␊
|
|
1941
1938
|
},␊
|
|
1942
1939
|
})␊
|
|
@@ -1998,10 +1995,9 @@ Generated by [AVA](https://avajs.dev).
|
|
|
1998
1995
|
// eslint-disable-next-line no-console␊
|
|
1999
1996
|
console.log.apply(console, arguments)␊
|
|
2000
1997
|
},␊
|
|
2001
|
-
printErr: function() {␊
|
|
1998
|
+
printErr: function () {␊
|
|
2002
1999
|
// eslint-disable-next-line no-console␊
|
|
2003
2000
|
console.error.apply(console, arguments)␊
|
|
2004
|
-
␊
|
|
2005
2001
|
errorOutputs.push([...arguments])␊
|
|
2006
2002
|
},␊
|
|
2007
2003
|
})␊
|
|
Binary file
|
|
@@ -25,9 +25,11 @@ import ava, { type ExecutionContext, type TestFn } from 'ava'
|
|
|
25
25
|
|
|
26
26
|
import {
|
|
27
27
|
buildProject,
|
|
28
|
+
EMNAPI_WASI_SDK_34_LINK_DIR,
|
|
28
29
|
generateTypeDef,
|
|
29
30
|
napiCrossToolchainEnvs,
|
|
30
31
|
resolveBuildFormat,
|
|
32
|
+
selectEmnapiLinkDir,
|
|
31
33
|
validateCrossCompileFlags,
|
|
32
34
|
validateNapiCrossSupport,
|
|
33
35
|
writeJsBinding,
|
|
@@ -1108,3 +1110,195 @@ const isNapiCrossUnsupportedHost =
|
|
|
1108
1110
|
)
|
|
1109
1111
|
},
|
|
1110
1112
|
)
|
|
1113
|
+
|
|
1114
|
+
async function createEmnapiLibDir(root: string, dirNames: string[]) {
|
|
1115
|
+
const emnapiLibDir = join(root, 'emnapi', 'lib')
|
|
1116
|
+
for (const dirName of dirNames) {
|
|
1117
|
+
await mkdir(join(emnapiLibDir, dirName), { recursive: true })
|
|
1118
|
+
}
|
|
1119
|
+
return emnapiLibDir
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
async function createWasiSdkDir(root: string, version: string) {
|
|
1123
|
+
const wasiSdkPath = join(root, `wasi-sdk-${version}`)
|
|
1124
|
+
await mkdir(wasiSdkPath, { recursive: true })
|
|
1125
|
+
await writeFile(join(wasiSdkPath, 'VERSION'), `${version}\n`)
|
|
1126
|
+
return wasiSdkPath
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
async function createWasiLibcArchive(
|
|
1130
|
+
projectDir: string,
|
|
1131
|
+
abi: 'legacy' | 'new',
|
|
1132
|
+
) {
|
|
1133
|
+
const libcPath = join(projectDir, `libc-${abi}.a`)
|
|
1134
|
+
// Only the archive member names matter: wasi-libc moved the futex helpers
|
|
1135
|
+
// into `futex.c` when it dropped the `int op` parameter, and kept `__wait.c`
|
|
1136
|
+
// for other symbols.
|
|
1137
|
+
const members =
|
|
1138
|
+
abi === 'new'
|
|
1139
|
+
? ['__wait.c.obj', 'futex.c.obj']
|
|
1140
|
+
: ['__wait.c.obj', '__wasilibc_busywait.c.obj']
|
|
1141
|
+
await writeFile(libcPath, `!<arch>\n${members.join('\n')}\n`)
|
|
1142
|
+
return libcPath
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
test('selects the wasi-sdk 34 emnapi archives for wasi-sdk >= 34', async (t) => {
|
|
1146
|
+
const { projectDir } = t.context
|
|
1147
|
+
const emnapiLibDir = await createEmnapiLibDir(projectDir, [
|
|
1148
|
+
'wasm32-wasip1',
|
|
1149
|
+
'wasm32-wasip1-threads',
|
|
1150
|
+
EMNAPI_WASI_SDK_34_LINK_DIR,
|
|
1151
|
+
])
|
|
1152
|
+
const wasiSdkPath = await createWasiSdkDir(projectDir, '34.0')
|
|
1153
|
+
|
|
1154
|
+
t.deepEqual(
|
|
1155
|
+
selectEmnapiLinkDir(
|
|
1156
|
+
emnapiLibDir,
|
|
1157
|
+
'wasm32-wasip1-threads',
|
|
1158
|
+
true,
|
|
1159
|
+
wasiSdkPath,
|
|
1160
|
+
),
|
|
1161
|
+
{
|
|
1162
|
+
linkDirName: EMNAPI_WASI_SDK_34_LINK_DIR,
|
|
1163
|
+
wasiSdkMajor: 34,
|
|
1164
|
+
needsWasiSdk34: true,
|
|
1165
|
+
},
|
|
1166
|
+
)
|
|
1167
|
+
})
|
|
1168
|
+
|
|
1169
|
+
test('keeps the legacy emnapi archives for wasi-sdk <= 33', async (t) => {
|
|
1170
|
+
const { projectDir } = t.context
|
|
1171
|
+
const emnapiLibDir = await createEmnapiLibDir(projectDir, [
|
|
1172
|
+
'wasm32-wasip1-threads',
|
|
1173
|
+
EMNAPI_WASI_SDK_34_LINK_DIR,
|
|
1174
|
+
])
|
|
1175
|
+
const wasiSdkPath = await createWasiSdkDir(projectDir, '33.0')
|
|
1176
|
+
|
|
1177
|
+
t.deepEqual(
|
|
1178
|
+
selectEmnapiLinkDir(
|
|
1179
|
+
emnapiLibDir,
|
|
1180
|
+
'wasm32-wasip1-threads',
|
|
1181
|
+
true,
|
|
1182
|
+
wasiSdkPath,
|
|
1183
|
+
),
|
|
1184
|
+
{
|
|
1185
|
+
linkDirName: 'wasm32-wasip1-threads',
|
|
1186
|
+
wasiSdkMajor: 33,
|
|
1187
|
+
needsWasiSdk34: false,
|
|
1188
|
+
},
|
|
1189
|
+
)
|
|
1190
|
+
})
|
|
1191
|
+
|
|
1192
|
+
test('keeps the legacy emnapi archives when the wasi-libc ABI is unknown', async (t) => {
|
|
1193
|
+
const { projectDir } = t.context
|
|
1194
|
+
const emnapiLibDir = await createEmnapiLibDir(projectDir, [
|
|
1195
|
+
'wasm32-wasip1-threads',
|
|
1196
|
+
EMNAPI_WASI_SDK_34_LINK_DIR,
|
|
1197
|
+
])
|
|
1198
|
+
|
|
1199
|
+
t.deepEqual(
|
|
1200
|
+
// `null` stands in for a Rust sysroot that cannot be probed.
|
|
1201
|
+
selectEmnapiLinkDir(
|
|
1202
|
+
emnapiLibDir,
|
|
1203
|
+
'wasm32-wasip1-threads',
|
|
1204
|
+
true,
|
|
1205
|
+
undefined,
|
|
1206
|
+
{ rustWasiLibc: null },
|
|
1207
|
+
),
|
|
1208
|
+
{
|
|
1209
|
+
linkDirName: 'wasm32-wasip1-threads',
|
|
1210
|
+
wasiSdkMajor: null,
|
|
1211
|
+
needsWasiSdk34: false,
|
|
1212
|
+
},
|
|
1213
|
+
)
|
|
1214
|
+
})
|
|
1215
|
+
|
|
1216
|
+
test('keeps the legacy emnapi archives for a pre wasi-sdk 34 Rust toolchain', async (t) => {
|
|
1217
|
+
const { projectDir } = t.context
|
|
1218
|
+
const emnapiLibDir = await createEmnapiLibDir(projectDir, [
|
|
1219
|
+
'wasm32-wasip1-threads',
|
|
1220
|
+
EMNAPI_WASI_SDK_34_LINK_DIR,
|
|
1221
|
+
])
|
|
1222
|
+
const rustWasiLibc = await createWasiLibcArchive(projectDir, 'legacy')
|
|
1223
|
+
|
|
1224
|
+
t.deepEqual(
|
|
1225
|
+
selectEmnapiLinkDir(
|
|
1226
|
+
emnapiLibDir,
|
|
1227
|
+
'wasm32-wasip1-threads',
|
|
1228
|
+
true,
|
|
1229
|
+
undefined,
|
|
1230
|
+
{ rustWasiLibc },
|
|
1231
|
+
),
|
|
1232
|
+
{
|
|
1233
|
+
linkDirName: 'wasm32-wasip1-threads',
|
|
1234
|
+
wasiSdkMajor: null,
|
|
1235
|
+
needsWasiSdk34: false,
|
|
1236
|
+
},
|
|
1237
|
+
)
|
|
1238
|
+
})
|
|
1239
|
+
|
|
1240
|
+
test('selects the wasi-sdk 34 archives for a Rust toolchain that bundles the new wasi-libc', async (t) => {
|
|
1241
|
+
const { projectDir } = t.context
|
|
1242
|
+
const emnapiLibDir = await createEmnapiLibDir(projectDir, [
|
|
1243
|
+
'wasm32-wasip1-threads',
|
|
1244
|
+
EMNAPI_WASI_SDK_34_LINK_DIR,
|
|
1245
|
+
])
|
|
1246
|
+
const rustWasiLibc = await createWasiLibcArchive(projectDir, 'new')
|
|
1247
|
+
|
|
1248
|
+
t.deepEqual(
|
|
1249
|
+
// No wasi-sdk is configured: cargo links Rust's bundled wasi-libc, and
|
|
1250
|
+
// Rust picked up wasi-sdk 34 in rust-lang/rust#161773.
|
|
1251
|
+
selectEmnapiLinkDir(
|
|
1252
|
+
emnapiLibDir,
|
|
1253
|
+
'wasm32-wasip1-threads',
|
|
1254
|
+
true,
|
|
1255
|
+
undefined,
|
|
1256
|
+
{ rustWasiLibc },
|
|
1257
|
+
),
|
|
1258
|
+
{
|
|
1259
|
+
linkDirName: EMNAPI_WASI_SDK_34_LINK_DIR,
|
|
1260
|
+
wasiSdkMajor: null,
|
|
1261
|
+
needsWasiSdk34: true,
|
|
1262
|
+
},
|
|
1263
|
+
)
|
|
1264
|
+
})
|
|
1265
|
+
|
|
1266
|
+
test('never selects the wasi-sdk 34 archives for the threadless target', async (t) => {
|
|
1267
|
+
const { projectDir } = t.context
|
|
1268
|
+
const emnapiLibDir = await createEmnapiLibDir(projectDir, [
|
|
1269
|
+
'wasm32-wasip1',
|
|
1270
|
+
EMNAPI_WASI_SDK_34_LINK_DIR,
|
|
1271
|
+
])
|
|
1272
|
+
const wasiSdkPath = await createWasiSdkDir(projectDir, '34.0')
|
|
1273
|
+
|
|
1274
|
+
t.deepEqual(
|
|
1275
|
+
selectEmnapiLinkDir(emnapiLibDir, 'wasm32-wasip1', false, wasiSdkPath),
|
|
1276
|
+
{
|
|
1277
|
+
linkDirName: 'wasm32-wasip1',
|
|
1278
|
+
wasiSdkMajor: 34,
|
|
1279
|
+
needsWasiSdk34: false,
|
|
1280
|
+
},
|
|
1281
|
+
)
|
|
1282
|
+
})
|
|
1283
|
+
|
|
1284
|
+
test('falls back to the legacy archives when emnapi has no wasi-sdk 34 directory', async (t) => {
|
|
1285
|
+
const { projectDir } = t.context
|
|
1286
|
+
const emnapiLibDir = await createEmnapiLibDir(projectDir, [
|
|
1287
|
+
'wasm32-wasip1-threads',
|
|
1288
|
+
])
|
|
1289
|
+
const wasiSdkPath = await createWasiSdkDir(projectDir, '34.0')
|
|
1290
|
+
|
|
1291
|
+
t.deepEqual(
|
|
1292
|
+
selectEmnapiLinkDir(
|
|
1293
|
+
emnapiLibDir,
|
|
1294
|
+
'wasm32-wasip1-threads',
|
|
1295
|
+
true,
|
|
1296
|
+
wasiSdkPath,
|
|
1297
|
+
),
|
|
1298
|
+
{
|
|
1299
|
+
linkDirName: 'wasm32-wasip1-threads',
|
|
1300
|
+
wasiSdkMajor: 34,
|
|
1301
|
+
needsWasiSdk34: true,
|
|
1302
|
+
},
|
|
1303
|
+
)
|
|
1304
|
+
})
|
|
@@ -131,9 +131,10 @@ test('should omit exports fields from publishConfig in scoped packages', async (
|
|
|
131
131
|
const scopedPackageJsonPath = join(scopedDir, 'package.json')
|
|
132
132
|
t.true(existsSync(scopedPackageJsonPath))
|
|
133
133
|
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
)
|
|
134
|
+
const scopedPackageJsonSource = await readFile(scopedPackageJsonPath, 'utf-8')
|
|
135
|
+
t.true(scopedPackageJsonSource.endsWith('\n'))
|
|
136
|
+
t.false(scopedPackageJsonSource.endsWith('\n\n'))
|
|
137
|
+
const scopedPackageJson = JSON.parse(scopedPackageJsonSource)
|
|
137
138
|
|
|
138
139
|
// Verify that publishConfig only contains registry and access, not exports
|
|
139
140
|
t.truthy(scopedPackageJson.publishConfig)
|
|
@@ -7,7 +7,10 @@ import {
|
|
|
7
7
|
createWasiBrowserBinding,
|
|
8
8
|
createWasiDeferredBrowserBinding,
|
|
9
9
|
} from '../templates/load-wasi-template.js'
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
createWasiBrowserWorkerBinding,
|
|
12
|
+
WASI_WORKER_TEMPLATE,
|
|
13
|
+
} from '../templates/wasi-worker-template.js'
|
|
11
14
|
|
|
12
15
|
const test = ava
|
|
13
16
|
|
|
@@ -228,6 +231,36 @@ const wasiLoaderCases: Array<{ name: string; code: string }> = [
|
|
|
228
231
|
{ name: 'deferred/workerd', code: createWasiDeferredBrowserBinding('test') },
|
|
229
232
|
]
|
|
230
233
|
|
|
234
|
+
test('Node WASI loader uses an accessible host root on Android', (t) => {
|
|
235
|
+
const code = createWasiBinding('test', '@scope/test')
|
|
236
|
+
assertValidJS(t, code, 'Node WASI loader')
|
|
237
|
+
t.true(code.includes('const __cwd = process.cwd()'))
|
|
238
|
+
t.true(code.includes("process.platform === 'android' ? __cwd : __rootDir"))
|
|
239
|
+
t.true(code.includes('[__rootDir]: __hostRoot'))
|
|
240
|
+
t.true(code.includes('[__hostRoot]: __hostRoot'))
|
|
241
|
+
t.true(
|
|
242
|
+
code.includes('workerData: { hostRoot: __hostRoot, rootDir: __rootDir }'),
|
|
243
|
+
)
|
|
244
|
+
t.false(code.includes('[__rootDir]: __rootDir'))
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
test('Node WASI worker uses an accessible host root on Android', (t) => {
|
|
248
|
+
assertValidJS(t, WASI_WORKER_TEMPLATE, 'Node WASI worker')
|
|
249
|
+
t.true(
|
|
250
|
+
WASI_WORKER_TEMPLATE.includes(
|
|
251
|
+
"workerData && typeof workerData.rootDir === 'string' && workerData.rootDir",
|
|
252
|
+
),
|
|
253
|
+
)
|
|
254
|
+
t.true(
|
|
255
|
+
WASI_WORKER_TEMPLATE.includes(
|
|
256
|
+
"workerData && typeof workerData.hostRoot === 'string' && workerData.hostRoot",
|
|
257
|
+
),
|
|
258
|
+
)
|
|
259
|
+
t.true(WASI_WORKER_TEMPLATE.includes('[__rootDir]: __hostRoot'))
|
|
260
|
+
t.true(WASI_WORKER_TEMPLATE.includes('[__hostRoot]: __hostRoot'))
|
|
261
|
+
t.false(WASI_WORKER_TEMPLATE.includes('[__rootDir]: __rootDir'))
|
|
262
|
+
})
|
|
263
|
+
|
|
231
264
|
for (const { name, code } of wasiLoaderCases) {
|
|
232
265
|
test(`WASI loader waits for queued settlements before destroy: ${name}`, (t) => {
|
|
233
266
|
t.true(
|
|
@@ -284,6 +317,34 @@ function initializationRollbackBody(code: string): string {
|
|
|
284
317
|
return code.slice(deferredStart, code.indexOf('throw error', deferredStart))
|
|
285
318
|
}
|
|
286
319
|
|
|
320
|
+
test('createCjsBinding uses one statement dialect', (t) => {
|
|
321
|
+
const code = createCjsBinding('test', '@scope/test', ['sum'], '1.0.0', [
|
|
322
|
+
'wasm32-wasi',
|
|
323
|
+
])
|
|
324
|
+
t.false(
|
|
325
|
+
code.includes('NAPI_RS_NATIVE_LIBRARY_PATH);'),
|
|
326
|
+
'native library require must not use a leftover semicolon',
|
|
327
|
+
)
|
|
328
|
+
t.true(code.includes("const __napiWasiFlavors = ['wasm32-wasi']"))
|
|
329
|
+
t.false(code.includes('"wasm32-wasi"'))
|
|
330
|
+
t.true(code.includes("return require('./test.win32-x64-gnu.node')"))
|
|
331
|
+
const win32Gnu = code.slice(
|
|
332
|
+
code.indexOf("process.arch === 'x64'"),
|
|
333
|
+
code.indexOf("process.arch === 'ia32'"),
|
|
334
|
+
)
|
|
335
|
+
t.true(
|
|
336
|
+
win32Gnu.includes(" return require('./test.win32-x64-gnu.node')"),
|
|
337
|
+
'win32-x64 gnu local require must be indented inside try',
|
|
338
|
+
)
|
|
339
|
+
})
|
|
340
|
+
|
|
341
|
+
test('WASI worker template matches the CJS/ESM quote and semicolon dialect', (t) => {
|
|
342
|
+
t.true(WASI_WORKER_TEMPLATE.includes("import fs from 'node:fs'"))
|
|
343
|
+
t.false(WASI_WORKER_TEMPLATE.includes('from "node:fs"'))
|
|
344
|
+
t.false(WASI_WORKER_TEMPLATE.includes("from 'node:fs';"))
|
|
345
|
+
t.true(WASI_WORKER_TEMPLATE.includes('memory: wasmMemory,'))
|
|
346
|
+
})
|
|
347
|
+
|
|
287
348
|
test('createEsmBinding is Node 12 compatible', (t) => {
|
|
288
349
|
const code = createEsmBinding('test', '@scope/test', ['sum'])
|
|
289
350
|
assertValidJS(t, code, 'esm')
|
package/src/api/build.ts
CHANGED
|
@@ -45,7 +45,10 @@ import {
|
|
|
45
45
|
targetToEnvVar,
|
|
46
46
|
tryInstallCargoBinary,
|
|
47
47
|
unlinkAsync,
|
|
48
|
+
rustBundledWasiLibc,
|
|
49
|
+
wasiLibcHasNewFutexAbi,
|
|
48
50
|
wasiLoaderSuffix,
|
|
51
|
+
wasiSdkMajorVersion,
|
|
49
52
|
wasiTargetHasThreads,
|
|
50
53
|
writeFileAtomic,
|
|
51
54
|
withFileSystemReconciliation,
|
|
@@ -223,6 +226,83 @@ export function getTypeDefCacheFolder(options: {
|
|
|
223
226
|
return join(options.targetDir, 'napi-rs', `${options.crateName}-${hash}`)
|
|
224
227
|
}
|
|
225
228
|
|
|
229
|
+
/**
|
|
230
|
+
* Name of the emnapi archive directory built against the wasi-sdk 34 ABI.
|
|
231
|
+
* Only the threaded target has one: the signature change it carries lives in
|
|
232
|
+
* the threads-only futex code.
|
|
233
|
+
*/
|
|
234
|
+
export const EMNAPI_WASI_SDK_34_LINK_DIR = 'wasm32-wasip1-threads-wasi-sdk-34'
|
|
235
|
+
|
|
236
|
+
export interface EmnapiLinkDirSelection {
|
|
237
|
+
/** Directory name under `emnapi/lib` whose archives should be linked. */
|
|
238
|
+
linkDirName: string
|
|
239
|
+
/** Detected wasi-sdk major version, `null` when no wasi-sdk is configured. */
|
|
240
|
+
wasiSdkMajor: number | null
|
|
241
|
+
/** `true` when the toolchain needs the wasi-sdk 34 archives. */
|
|
242
|
+
needsWasiSdk34: boolean
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Pick the emnapi archive directory for a WASI build.
|
|
247
|
+
*
|
|
248
|
+
* wasi-libc removed the unused `int op` parameter from
|
|
249
|
+
* `__wasilibc_futex_wait_atomic_wait` / `__wasilibc_futex_wait_maybe_busy`,
|
|
250
|
+
* and wasi-sdk 34 is the first release shipping the 3-argument signature.
|
|
251
|
+
* Linking the legacy 4-argument archives against a wasi-sdk >= 34 sysroot
|
|
252
|
+
* makes `wasm-ld` report `function signature mismatch` and emit an invalid
|
|
253
|
+
* wasm module, so emnapi publishes a second archive set for the new ABI and
|
|
254
|
+
* the directory has to be chosen by wasi-sdk version, not by target triple.
|
|
255
|
+
*
|
|
256
|
+
* Without `WASI_SDK_PATH`, cargo links through `rust-lld` against the
|
|
257
|
+
* wasi-libc bundled with the Rust standard library, so that archive is
|
|
258
|
+
* probed instead. Rust picked up wasi-sdk 34 in rust-lang/rust#161773, which
|
|
259
|
+
* lands in 1.100, and a toolchain carrying it needs the new archives even
|
|
260
|
+
* though no wasi-sdk is configured.
|
|
261
|
+
*
|
|
262
|
+
* The legacy directory stays the default whenever the ABI cannot be
|
|
263
|
+
* determined, and older emnapi releases ship it alone.
|
|
264
|
+
*/
|
|
265
|
+
export function selectEmnapiLinkDir(
|
|
266
|
+
emnapiLibDir: string,
|
|
267
|
+
wasiTarget: string,
|
|
268
|
+
hasThreads: boolean,
|
|
269
|
+
wasiSdkPath: string | undefined,
|
|
270
|
+
{
|
|
271
|
+
cwd,
|
|
272
|
+
rustWasiLibc,
|
|
273
|
+
}: {
|
|
274
|
+
// The directory Cargo is spawned in. It selects the toolchain, so the
|
|
275
|
+
// sysroot probe has to use it too.
|
|
276
|
+
cwd?: string
|
|
277
|
+
// Omit to resolve the Rust sysroot lazily, so a configured wasi-sdk never
|
|
278
|
+
// pays for a `rustc` invocation. Pass `null` to skip the probe.
|
|
279
|
+
rustWasiLibc?: string | null
|
|
280
|
+
} = {},
|
|
281
|
+
): EmnapiLinkDirSelection {
|
|
282
|
+
const usingWasiSdk = Boolean(wasiSdkPath) && existsSync(wasiSdkPath!)
|
|
283
|
+
const wasiSdkMajor = usingWasiSdk ? wasiSdkMajorVersion(wasiSdkPath!) : null
|
|
284
|
+
// Whichever wasi-libc actually gets linked decides the ABI: the wasi-sdk
|
|
285
|
+
// sysroot when one is configured, otherwise the copy bundled with the Rust
|
|
286
|
+
// standard library. Only the threaded target has a second archive set, so
|
|
287
|
+
// `hasThreads` gates the probe as well as the result — a threadless build
|
|
288
|
+
// must not pay for a `rustc` invocation and an archive read it cannot use.
|
|
289
|
+
const needsWasiSdk34 =
|
|
290
|
+
hasThreads &&
|
|
291
|
+
(usingWasiSdk
|
|
292
|
+
? wasiSdkMajor !== null && wasiSdkMajor >= 34
|
|
293
|
+
: wasiLibcHasNewFutexAbi(
|
|
294
|
+
rustWasiLibc === undefined
|
|
295
|
+
? rustBundledWasiLibc(wasiTarget, cwd)
|
|
296
|
+
: rustWasiLibc,
|
|
297
|
+
) === true)
|
|
298
|
+
const linkDirName =
|
|
299
|
+
needsWasiSdk34 &&
|
|
300
|
+
existsSync(join(emnapiLibDir, EMNAPI_WASI_SDK_34_LINK_DIR))
|
|
301
|
+
? EMNAPI_WASI_SDK_34_LINK_DIR
|
|
302
|
+
: wasiTarget
|
|
303
|
+
return { linkDirName, wasiSdkMajor, needsWasiSdk34 }
|
|
304
|
+
}
|
|
305
|
+
|
|
226
306
|
export function createWasiCompilerFlags(
|
|
227
307
|
wasiSdkPath: string,
|
|
228
308
|
wasiTarget: string,
|
|
@@ -1461,16 +1541,40 @@ class Builder {
|
|
|
1461
1541
|
private setWasiEnv() {
|
|
1462
1542
|
const hasThreads = wasiTargetHasThreads(this.target)
|
|
1463
1543
|
const wasiTarget = hasThreads ? 'wasm32-wasip1-threads' : 'wasm32-wasip1'
|
|
1464
|
-
const
|
|
1544
|
+
const emnapiLibDir = join(require.resolve('emnapi'), '..', 'lib')
|
|
1465
1545
|
const emnapiVersion = require('emnapi/package.json').version
|
|
1546
|
+
const { WASI_SDK_PATH } = process.env
|
|
1547
|
+
// `wasiTarget` stays the real target triple, because it is what the clang
|
|
1548
|
+
// driver is told to build for below. The emnapi archive directory is a
|
|
1549
|
+
// separate choice: emnapi ships two archive sets for the threaded target,
|
|
1550
|
+
// one per wasi-libc futex ABI. See `selectEmnapiLinkDir`.
|
|
1551
|
+
const { linkDirName, wasiSdkMajor, needsWasiSdk34 } = selectEmnapiLinkDir(
|
|
1552
|
+
emnapiLibDir,
|
|
1553
|
+
wasiTarget,
|
|
1554
|
+
hasThreads,
|
|
1555
|
+
WASI_SDK_PATH,
|
|
1556
|
+
{ cwd: this.options.cwd },
|
|
1557
|
+
)
|
|
1558
|
+
const emnapi = join(emnapiLibDir, linkDirName)
|
|
1466
1559
|
// Keep this in sync with `emnapi_link_library` in `crates/build/src/wasi.rs`.
|
|
1467
1560
|
const emnapiArchive = join(
|
|
1468
1561
|
emnapi,
|
|
1469
1562
|
hasThreads ? 'libemnapi-napi-rs-mt.a' : 'libemnapi-basic-napi-rs.a',
|
|
1470
1563
|
)
|
|
1564
|
+
const fellBackToLegacy =
|
|
1565
|
+
needsWasiSdk34 && linkDirName !== EMNAPI_WASI_SDK_34_LINK_DIR
|
|
1471
1566
|
if (!existsSync(emnapiArchive)) {
|
|
1472
1567
|
throw new Error(
|
|
1473
|
-
|
|
1568
|
+
fellBackToLegacy
|
|
1569
|
+
? `emnapi@${emnapiVersion} does not ship the ${EMNAPI_WASI_SDK_34_LINK_DIR} archives that wasi-sdk ${wasiSdkMajor} requires, and the ${linkDirName} archive it fell back to is missing too at ${emnapiArchive}. Upgrade emnapi to a version that ships the wasi-sdk 34 archives.`
|
|
1570
|
+
: needsWasiSdk34
|
|
1571
|
+
? `emnapi@${emnapiVersion} is missing the ${linkDirName} archive required by napi-rs at ${emnapiArchive}. wasi-sdk ${wasiSdkMajor} needs those archives, so upgrade emnapi to a version that ships them.`
|
|
1572
|
+
: `emnapi@${emnapiVersion} is missing the ${linkDirName} archive required by napi-rs at ${emnapiArchive}. Install emnapi v2 with support for this target.`,
|
|
1573
|
+
)
|
|
1574
|
+
}
|
|
1575
|
+
if (fellBackToLegacy) {
|
|
1576
|
+
debug.warn(
|
|
1577
|
+
`emnapi@${emnapiVersion} does not ship the ${EMNAPI_WASI_SDK_34_LINK_DIR} archives that wasi-sdk ${wasiSdkMajor} requires. Falling back to ${linkDirName}, which links the legacy wasi-libc futex ABI and may fail with \`wasm-ld: function signature mismatch\`. Upgrade emnapi to fix this.`,
|
|
1474
1578
|
)
|
|
1475
1579
|
}
|
|
1476
1580
|
this.envs.EMNAPI_LINK_DIR = emnapi
|
|
@@ -1499,7 +1603,6 @@ class Builder {
|
|
|
1499
1603
|
`emnapi version mismatch: emnapi@${emnapiVersion}, @emnapi/core@${emnapiCoreVersion}, @emnapi/runtime@${emnapiRuntimeVersion}. Please ensure all emnapi packages are the same version.`,
|
|
1500
1604
|
)
|
|
1501
1605
|
}
|
|
1502
|
-
const { WASI_SDK_PATH } = process.env
|
|
1503
1606
|
|
|
1504
1607
|
if (WASI_SDK_PATH && existsSync(WASI_SDK_PATH)) {
|
|
1505
1608
|
this.envs.CARGO_TARGET_WASM32_WASI_PREVIEW1_THREADS_LINKER = join(
|
|
@@ -2560,10 +2663,27 @@ export declare class ExternalObject<T> {
|
|
|
2560
2663
|
|
|
2561
2664
|
if (dts.indexOf('TypedArray') > -1) {
|
|
2562
2665
|
header += `
|
|
2563
|
-
export type TypedArray =
|
|
2666
|
+
export type TypedArray =
|
|
2667
|
+
| Int8Array
|
|
2668
|
+
| Uint8Array
|
|
2669
|
+
| Uint8ClampedArray
|
|
2670
|
+
| Int16Array
|
|
2671
|
+
| Uint16Array
|
|
2672
|
+
| Int32Array
|
|
2673
|
+
| Uint32Array
|
|
2674
|
+
| Float32Array
|
|
2675
|
+
| Float64Array
|
|
2676
|
+
| BigInt64Array
|
|
2677
|
+
| BigUint64Array
|
|
2564
2678
|
`
|
|
2565
2679
|
}
|
|
2566
2680
|
|
|
2681
|
+
if (header && !header.endsWith('\n')) {
|
|
2682
|
+
header += '\n'
|
|
2683
|
+
}
|
|
2684
|
+
if (header && !header.endsWith('\n\n')) {
|
|
2685
|
+
header += '\n'
|
|
2686
|
+
}
|
|
2567
2687
|
dts = header + dts
|
|
2568
2688
|
const dtsWithTypeImports = rewriteTypeImportReferences(
|
|
2569
2689
|
header + dtsWithTypeImportMarkers,
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
pick,
|
|
29
29
|
resolvePackageReconciliationPaths,
|
|
30
30
|
restrictWasiNodeEngine,
|
|
31
|
+
serializeJson,
|
|
31
32
|
wasiLoaderSuffix,
|
|
32
33
|
wasiTargetHasThreads,
|
|
33
34
|
withFileSystemReconciliation,
|
|
@@ -574,7 +575,7 @@ async function createNpmDirsUnlocked(
|
|
|
574
575
|
|
|
575
576
|
const targetPackageJson = join(targetDir, 'package.json')
|
|
576
577
|
pendingWrites.push({
|
|
577
|
-
content:
|
|
578
|
+
content: serializeJson(scopedPackageJson),
|
|
578
579
|
destination: targetPackageJson,
|
|
579
580
|
})
|
|
580
581
|
if (wasmModuleTypeDef) {
|