@napi-rs/cli 3.7.3 → 3.8.0
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/README.md +33 -0
- package/cli.mjs +9 -12
- package/dist/cli.js +9277 -1313
- package/dist/index.cjs +9266 -1302
- package/dist/index.d.cts +176 -158
- package/dist/index.d.ts +176 -158
- package/dist/index.js +9277 -1313
- package/docs/artifacts.md +33 -0
- package/docs/build.md +58 -0
- package/docs/create-npm-dirs.md +32 -0
- package/docs/new.md +39 -0
- package/docs/pre-publish.md +37 -0
- package/docs/rename.md +37 -0
- package/docs/universalize.md +31 -0
- package/docs/version.md +31 -0
- package/docs/wasi.md +161 -0
- package/package.json +11 -10
- package/src/api/__tests__/__snapshots__/templates.spec.ts.md +1019 -114
- package/src/api/__tests__/__snapshots__/templates.spec.ts.snap +0 -0
- package/src/api/__tests__/artifacts.spec.ts +17 -0
- package/src/api/__tests__/create-npm-dirs.spec.ts +14 -15
- package/src/api/__tests__/new.spec.ts +41 -5
- package/src/api/artifacts.ts +934 -108
- package/src/api/build.ts +1325 -194
- package/src/api/create-npm-dirs.ts +472 -101
- package/src/api/new.ts +354 -35
- package/src/api/pre-publish.ts +2845 -99
- package/src/api/rename.ts +1638 -124
- package/src/api/templates/js-binding.ts +216 -38
- package/src/api/templates/load-wasi-template.ts +1641 -111
- package/src/api/templates/wasi-worker-template.ts +32 -3
- package/src/commands/rename.ts +8 -3
- package/src/def/artifacts.ts +2 -2
- package/src/utils/__tests__/__fixtures__/optional-napi-derive/Cargo.toml +3 -0
- package/src/utils/__tests__/__fixtures__/optional-napi-derive/main-crate/Cargo.toml +8 -0
- package/src/utils/__tests__/__fixtures__/optional-napi-derive/main-crate/src/lib.rs +1 -0
- package/src/utils/__tests__/__fixtures__/optional-napi-derive/napi-derive/Cargo.toml +5 -0
- package/src/utils/__tests__/__fixtures__/optional-napi-derive/napi-derive/src/lib.rs +1 -0
- package/src/utils/__tests__/__fixtures__/optional-napi-derive/with-optional-derive/Cargo.toml +11 -0
- package/src/utils/__tests__/__fixtures__/optional-napi-derive/with-optional-derive/src/lib.rs +1 -0
- package/src/utils/__tests__/__snapshots__/target.spec.ts.md +2 -2
- package/src/utils/__tests__/__snapshots__/target.spec.ts.snap +0 -0
- package/src/utils/__tests__/metadata.spec.ts +43 -2
- package/src/utils/__tests__/misc.spec.ts +124 -3
- package/src/utils/__tests__/version.spec.ts +47 -1
- package/src/utils/config.ts +15 -2
- package/src/utils/metadata.ts +206 -7
- package/src/utils/misc.ts +6079 -0
- package/src/utils/target.ts +66 -8
- package/src/utils/typegen.ts +1223 -63
- package/src/utils/version.ts +96 -0
|
@@ -1,11 +1,139 @@
|
|
|
1
|
+
import { wasiLoaderSuffix } from '../../utils/index.js'
|
|
2
|
+
|
|
3
|
+
function resolveWasiFlavors(wasiFlavors?: string[]): string[] {
|
|
4
|
+
return wasiFlavors && wasiFlavors.length > 0 ? wasiFlavors : ['wasm32-wasi']
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Generate the WASI fallback `require` chain for `binding.cjs`/`binding.js`.
|
|
9
|
+
*
|
|
10
|
+
* Local candidates are tried before installed package fallbacks. Within each
|
|
11
|
+
* group, candidates retain the declared flavor order (threaded flavors are
|
|
12
|
+
* expected first), and the chain stops at the FIRST successfully loaded
|
|
13
|
+
* binding.
|
|
14
|
+
*/
|
|
15
|
+
function createWasiFallbackChain(
|
|
16
|
+
localName: string,
|
|
17
|
+
pkgName: string,
|
|
18
|
+
flavors: string[],
|
|
19
|
+
packageVersion?: string,
|
|
20
|
+
localWasiName = `./${localName}`,
|
|
21
|
+
): string {
|
|
22
|
+
const candidates = [
|
|
23
|
+
...flavors.map((flavor) => ({
|
|
24
|
+
flavor,
|
|
25
|
+
specifier: `${localWasiName}.${wasiLoaderSuffix(flavor)}.cjs`,
|
|
26
|
+
isPackage: false,
|
|
27
|
+
localArtifacts: [
|
|
28
|
+
`${localWasiName}.${flavor}.debug.wasm`,
|
|
29
|
+
`${localWasiName}.${flavor}.wasm`,
|
|
30
|
+
],
|
|
31
|
+
})),
|
|
32
|
+
...flavors.map((flavor) => ({
|
|
33
|
+
flavor,
|
|
34
|
+
specifier: `${pkgName}-${flavor}`,
|
|
35
|
+
isPackage: true,
|
|
36
|
+
localArtifacts: undefined,
|
|
37
|
+
})),
|
|
38
|
+
]
|
|
39
|
+
const chain = candidates
|
|
40
|
+
.map(
|
|
41
|
+
({
|
|
42
|
+
flavor,
|
|
43
|
+
specifier,
|
|
44
|
+
isPackage,
|
|
45
|
+
localArtifacts,
|
|
46
|
+
}) => ` if (!wasiBindingLoaded && (!__napiWasiFlavorRequested || __napiWasiFlavor === ${JSON.stringify(flavor)})) {
|
|
47
|
+
let candidateError = null
|
|
48
|
+
let candidateFailed = false
|
|
49
|
+
try {
|
|
50
|
+
candidateError = __napiWasiResolveCandidate('${specifier}', ${isPackage}, ${JSON.stringify(localArtifacts)})
|
|
51
|
+
candidateFailed = candidateError !== null
|
|
52
|
+
if (!candidateFailed) {${
|
|
53
|
+
isPackage && packageVersion
|
|
54
|
+
? `
|
|
55
|
+
if (process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
|
56
|
+
const bindingPackageVersion = require('${specifier}/package.json').version
|
|
57
|
+
if (bindingPackageVersion !== '${packageVersion}') {
|
|
58
|
+
throw new Error(\`WASI binding package version mismatch, expected ${packageVersion} but got \${bindingPackageVersion}. You can reinstall dependencies to fix this issue.\`)
|
|
59
|
+
}
|
|
60
|
+
}`
|
|
61
|
+
: ''
|
|
62
|
+
}
|
|
63
|
+
wasiBinding = require('${specifier}')
|
|
64
|
+
nativeBinding = wasiBinding
|
|
65
|
+
wasiBindingLoaded = true
|
|
66
|
+
}
|
|
67
|
+
} catch (err) {
|
|
68
|
+
candidateError = err
|
|
69
|
+
candidateFailed = true
|
|
70
|
+
}
|
|
71
|
+
if (candidateFailed) {
|
|
72
|
+
wasiBindingErrors.push(candidateError)
|
|
73
|
+
loadErrors.push(candidateError)
|
|
74
|
+
}
|
|
75
|
+
}`,
|
|
76
|
+
)
|
|
77
|
+
.join('\n')
|
|
78
|
+
return ` const __napiWasiResolveCandidate = (specifier, isPackage, localArtifacts) => {
|
|
79
|
+
try {
|
|
80
|
+
require.resolve(specifier)
|
|
81
|
+
} catch (resolveError) {
|
|
82
|
+
if (!resolveError || resolveError.code !== 'MODULE_NOT_FOUND') {
|
|
83
|
+
throw resolveError
|
|
84
|
+
}
|
|
85
|
+
if (isPackage) {
|
|
86
|
+
try {
|
|
87
|
+
require.resolve(specifier + '/package.json')
|
|
88
|
+
} catch (packageError) {
|
|
89
|
+
if (packageError && packageError.code === 'MODULE_NOT_FOUND') {
|
|
90
|
+
return resolveError
|
|
91
|
+
}
|
|
92
|
+
// An exports restriction proves the package exists even when its
|
|
93
|
+
// package.json is not public. Preserve the root resolution failure.
|
|
94
|
+
throw resolveError
|
|
95
|
+
}
|
|
96
|
+
// The package exists but its main/export target is broken.
|
|
97
|
+
throw resolveError
|
|
98
|
+
}
|
|
99
|
+
return resolveError
|
|
100
|
+
}
|
|
101
|
+
if (localArtifacts) {
|
|
102
|
+
let artifactError = null
|
|
103
|
+
for (let i = 0; i < localArtifacts.length; i++) {
|
|
104
|
+
try {
|
|
105
|
+
require.resolve(localArtifacts[i])
|
|
106
|
+
return null
|
|
107
|
+
} catch (resolveError) {
|
|
108
|
+
if (!resolveError || resolveError.code !== 'MODULE_NOT_FOUND') {
|
|
109
|
+
throw resolveError
|
|
110
|
+
}
|
|
111
|
+
artifactError = resolveError
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return artifactError
|
|
115
|
+
}
|
|
116
|
+
return null
|
|
117
|
+
}
|
|
118
|
+
${chain}`
|
|
119
|
+
}
|
|
120
|
+
|
|
1
121
|
export function createCjsBinding(
|
|
2
122
|
localName: string,
|
|
3
123
|
pkgName: string,
|
|
4
124
|
idents: string[],
|
|
5
125
|
packageVersion?: string,
|
|
126
|
+
wasiFlavors?: string[],
|
|
127
|
+
localWasiName?: string,
|
|
6
128
|
): string {
|
|
7
129
|
return `${bindingHeader}
|
|
8
|
-
${createCommonBinding(
|
|
130
|
+
${createCommonBinding(
|
|
131
|
+
localName,
|
|
132
|
+
pkgName,
|
|
133
|
+
packageVersion,
|
|
134
|
+
wasiFlavors,
|
|
135
|
+
localWasiName,
|
|
136
|
+
)}
|
|
9
137
|
module.exports = nativeBinding
|
|
10
138
|
${idents
|
|
11
139
|
.map((ident) => `module.exports.${ident} = nativeBinding.${ident}`)
|
|
@@ -18,15 +146,27 @@ export function createEsmBinding(
|
|
|
18
146
|
pkgName: string,
|
|
19
147
|
idents: string[],
|
|
20
148
|
packageVersion?: string,
|
|
149
|
+
wasiFlavors?: string[],
|
|
150
|
+
localWasiName?: string,
|
|
21
151
|
): string {
|
|
152
|
+
const exportsCode =
|
|
153
|
+
idents.length > 0
|
|
154
|
+
? `const { ${idents.join(', ')} } = nativeBinding
|
|
155
|
+
${idents.map((ident) => `export { ${ident} }`).join('\n')}`
|
|
156
|
+
: 'export default nativeBinding'
|
|
22
157
|
return `${bindingHeader}
|
|
23
158
|
import { createRequire } from 'module'
|
|
24
159
|
const require = createRequire(import.meta.url)
|
|
25
160
|
const __dirname = new URL('.', import.meta.url).pathname
|
|
26
161
|
|
|
27
|
-
${createCommonBinding(
|
|
28
|
-
|
|
29
|
-
|
|
162
|
+
${createCommonBinding(
|
|
163
|
+
localName,
|
|
164
|
+
pkgName,
|
|
165
|
+
packageVersion,
|
|
166
|
+
wasiFlavors,
|
|
167
|
+
localWasiName,
|
|
168
|
+
)}
|
|
169
|
+
${exportsCode}
|
|
30
170
|
`
|
|
31
171
|
}
|
|
32
172
|
|
|
@@ -40,6 +180,8 @@ function createCommonBinding(
|
|
|
40
180
|
localName: string,
|
|
41
181
|
pkgName: string,
|
|
42
182
|
packageVersion?: string,
|
|
183
|
+
wasiFlavors?: string[],
|
|
184
|
+
localWasiName?: string,
|
|
43
185
|
): string {
|
|
44
186
|
function requireTuple(tuple: string, identSize = 8) {
|
|
45
187
|
const identLow = ' '.repeat(identSize - 2)
|
|
@@ -69,6 +211,8 @@ ${ident}loadErrors.push(e)
|
|
|
69
211
|
${identLow}}${versionCheck}`
|
|
70
212
|
}
|
|
71
213
|
|
|
214
|
+
const flavors = resolveWasiFlavors(wasiFlavors)
|
|
215
|
+
|
|
72
216
|
return `const { readFileSync } = require('fs')
|
|
73
217
|
let nativeBinding = null
|
|
74
218
|
const loadErrors = []
|
|
@@ -225,47 +369,84 @@ function requireNative() {
|
|
|
225
369
|
}
|
|
226
370
|
}
|
|
227
371
|
|
|
228
|
-
|
|
372
|
+
function createLoadErrorChain(errors) {
|
|
373
|
+
return errors.reduce((previous, current) => {
|
|
374
|
+
let message
|
|
375
|
+
try {
|
|
376
|
+
message =
|
|
377
|
+
current && typeof current.message === 'string'
|
|
378
|
+
? current.message
|
|
379
|
+
: String(current)
|
|
380
|
+
} catch {
|
|
381
|
+
message = 'Unknown error'
|
|
382
|
+
}
|
|
383
|
+
const error = new Error(message)
|
|
384
|
+
error.cause = previous
|
|
385
|
+
return error
|
|
386
|
+
}, null)
|
|
387
|
+
}
|
|
229
388
|
|
|
230
389
|
// NAPI_RS_FORCE_WASI is a tri-state flag:
|
|
231
390
|
// unset / any other value → native binding preferred, WASI is only a fallback
|
|
232
|
-
// 'true' →
|
|
233
|
-
// 'error' →
|
|
391
|
+
// 'true' → prefer WASI, but retain native as a lazy fallback
|
|
392
|
+
// 'error' → require WASI without initializing a native fallback
|
|
234
393
|
// Treating any non-empty string as truthy (the historical behavior) meant
|
|
235
394
|
// NAPI_RS_FORCE_WASI=false, NAPI_RS_FORCE_WASI=0, etc. inadvertently triggered
|
|
236
395
|
// the WASI path, causing ENOENT for packages shipped without a .wasi.cjs file.
|
|
396
|
+
//
|
|
397
|
+
// NAPI_RS_WASI_FLAVOR selects one exact generated flavor and implies strict
|
|
398
|
+
// WASI loading. It never crosses into another flavor or falls back to native.
|
|
399
|
+
const __napiWasiFlavors = ${JSON.stringify(flavors)}
|
|
400
|
+
const __napiWasiFlavor = process.env.NAPI_RS_WASI_FLAVOR
|
|
401
|
+
const __napiWasiFlavorRequested =
|
|
402
|
+
typeof __napiWasiFlavor === 'string' && __napiWasiFlavor.length > 0
|
|
403
|
+
if (
|
|
404
|
+
__napiWasiFlavorRequested &&
|
|
405
|
+
__napiWasiFlavors.indexOf(__napiWasiFlavor) === -1
|
|
406
|
+
) {
|
|
407
|
+
throw new Error(
|
|
408
|
+
'Unsupported WASI flavor "' +
|
|
409
|
+
__napiWasiFlavor +
|
|
410
|
+
'". Available flavors: ' +
|
|
411
|
+
__napiWasiFlavors.join(', '),
|
|
412
|
+
)
|
|
413
|
+
}
|
|
414
|
+
const forceWasiError = process.env.NAPI_RS_FORCE_WASI === 'error'
|
|
237
415
|
const forceWasi =
|
|
238
|
-
process.env.NAPI_RS_FORCE_WASI === 'true' ||
|
|
416
|
+
process.env.NAPI_RS_FORCE_WASI === 'true' ||
|
|
417
|
+
forceWasiError ||
|
|
418
|
+
__napiWasiFlavorRequested
|
|
419
|
+
|
|
420
|
+
if (!forceWasi) {
|
|
421
|
+
nativeBinding = requireNative()
|
|
422
|
+
}
|
|
239
423
|
|
|
240
424
|
if (!nativeBinding || forceWasi) {
|
|
241
425
|
let wasiBinding = null
|
|
242
|
-
let
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
if (
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
wasiBindingError = err
|
|
259
|
-
} else {
|
|
260
|
-
wasiBindingError.cause = err
|
|
261
|
-
}
|
|
262
|
-
loadErrors.push(err)
|
|
263
|
-
}
|
|
264
|
-
}
|
|
426
|
+
let wasiBindingLoaded = false
|
|
427
|
+
const wasiBindingErrors = []
|
|
428
|
+
${createWasiFallbackChain(
|
|
429
|
+
localName,
|
|
430
|
+
pkgName,
|
|
431
|
+
flavors,
|
|
432
|
+
packageVersion,
|
|
433
|
+
localWasiName,
|
|
434
|
+
)}
|
|
435
|
+
if (
|
|
436
|
+
!wasiBindingLoaded &&
|
|
437
|
+
forceWasi &&
|
|
438
|
+
!forceWasiError &&
|
|
439
|
+
!__napiWasiFlavorRequested
|
|
440
|
+
) {
|
|
441
|
+
nativeBinding = requireNative()
|
|
265
442
|
}
|
|
266
|
-
if (
|
|
267
|
-
const error = new Error(
|
|
268
|
-
|
|
443
|
+
if ((forceWasiError || __napiWasiFlavorRequested) && !wasiBindingLoaded) {
|
|
444
|
+
const error = new Error(
|
|
445
|
+
__napiWasiFlavorRequested
|
|
446
|
+
? 'WASI binding for flavor "' + __napiWasiFlavor + '" not found'
|
|
447
|
+
: 'WASI binding not found and NAPI_RS_FORCE_WASI is set to error',
|
|
448
|
+
)
|
|
449
|
+
error.cause = createLoadErrorChain(wasiBindingErrors)
|
|
269
450
|
throw error
|
|
270
451
|
}
|
|
271
452
|
}
|
|
@@ -279,10 +460,7 @@ if (!nativeBinding) {
|
|
|
279
460
|
)
|
|
280
461
|
// assign instead of the \`new Error(message, { cause })\` options form,
|
|
281
462
|
// which Node < 16.9 silently ignores
|
|
282
|
-
error.cause = loadErrors
|
|
283
|
-
cur.cause = err
|
|
284
|
-
return cur
|
|
285
|
-
})
|
|
463
|
+
error.cause = createLoadErrorChain(loadErrors)
|
|
286
464
|
throw error
|
|
287
465
|
}
|
|
288
466
|
throw new Error(\`Failed to load native binding\`)
|