@napi-rs/cli 3.8.1 → 3.8.2
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 +40 -6
- package/dist/index.cjs +40 -6
- package/dist/index.d.cts +22 -4
- package/dist/index.d.ts +22 -4
- package/dist/index.js +40 -6
- package/docs/wasi.md +22 -0
- package/package.json +1 -1
- package/src/api/__tests__/pre-publish.spec.ts +106 -0
- package/src/api/pre-publish.ts +69 -19
- package/src/utils/config.ts +20 -1
package/docs/wasi.md
CHANGED
|
@@ -153,6 +153,28 @@ module runs inside the host process, so `wasm32` or host-OS restrictions would
|
|
|
153
153
|
make npm reject a direct install or skip the optional dependency on otherwise
|
|
154
154
|
supported hosts.
|
|
155
155
|
|
|
156
|
+
Because nothing gates the install, the root package does not declare the WASI
|
|
157
|
+
package in `optionalDependencies` when native targets are also configured. npm
|
|
158
|
+
evaluates every `optionalDependencies` entry independently, so a declared WASI
|
|
159
|
+
package is downloaded by every consumer, including the ones that already
|
|
160
|
+
resolved a native package and will never load the `.wasm` binary. The generated
|
|
161
|
+
binding loader picks WASI at require time instead, and environments without a
|
|
162
|
+
native package are expected to install it on demand.
|
|
163
|
+
|
|
164
|
+
When WASI is the only configured target it is the primary artifact rather than a
|
|
165
|
+
fallback, so it is declared by default. Set `napi.wasm.optionalDependency` to
|
|
166
|
+
override the default in either direction:
|
|
167
|
+
|
|
168
|
+
```json
|
|
169
|
+
{
|
|
170
|
+
"napi": {
|
|
171
|
+
"wasm": {
|
|
172
|
+
"optionalDependency": true
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
156
178
|
`napi.wasm.initialMemory` is measured in 64 KiB WebAssembly pages. The regular
|
|
157
179
|
Node and browser loaders retain the historical 4,000-page (250 MiB) default.
|
|
158
180
|
The deferred `./workerd` loader defaults to 1,024 pages (64 MiB), leaving
|
package/package.json
CHANGED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import test from 'ava'
|
|
2
|
+
|
|
3
|
+
import { resolveRootOptionalDependencies } from '../pre-publish.js'
|
|
4
|
+
import { parseTriple } from '../../utils/index.js'
|
|
5
|
+
|
|
6
|
+
const PACKAGE_NAME = '@scope/pkg'
|
|
7
|
+
const VERSION = '1.2.3'
|
|
8
|
+
|
|
9
|
+
const NATIVE_TARGETS = ['aarch64-apple-darwin', 'x86_64-unknown-linux-gnu'].map(
|
|
10
|
+
parseTriple,
|
|
11
|
+
)
|
|
12
|
+
const WASI_TARGET = parseTriple('wasm32-wasip1-threads')
|
|
13
|
+
const THREADLESS_WASI_TARGET = parseTriple('wasm32-wasip1')
|
|
14
|
+
|
|
15
|
+
const NATIVE_ENTRIES = {
|
|
16
|
+
[`${PACKAGE_NAME}-darwin-arm64`]: VERSION,
|
|
17
|
+
[`${PACKAGE_NAME}-linux-x64-gnu`]: VERSION,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function resolve(
|
|
21
|
+
targets: Parameters<typeof resolveRootOptionalDependencies>[0]['targets'],
|
|
22
|
+
overrides: Partial<
|
|
23
|
+
Parameters<typeof resolveRootOptionalDependencies>[0]
|
|
24
|
+
> = {},
|
|
25
|
+
) {
|
|
26
|
+
return resolveRootOptionalDependencies({
|
|
27
|
+
existing: undefined,
|
|
28
|
+
managedPackageNames: [PACKAGE_NAME],
|
|
29
|
+
packageName: PACKAGE_NAME,
|
|
30
|
+
targets,
|
|
31
|
+
version: VERSION,
|
|
32
|
+
...overrides,
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
test('omits the WASI package when native targets are configured', (t) => {
|
|
37
|
+
// The WASI binary is a require-time fallback, not something npm should
|
|
38
|
+
// install on hosts that already resolved a native package.
|
|
39
|
+
t.deepEqual(resolve([...NATIVE_TARGETS, WASI_TARGET]), NATIVE_ENTRIES)
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
test('omits the threadless WASI package too', (t) => {
|
|
43
|
+
t.deepEqual(
|
|
44
|
+
resolve([...NATIVE_TARGETS, THREADLESS_WASI_TARGET]),
|
|
45
|
+
NATIVE_ENTRIES,
|
|
46
|
+
)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
test('declares the WASI package when it is the only target', (t) => {
|
|
50
|
+
// With no native package to fall back from, WASI is the primary artifact.
|
|
51
|
+
t.deepEqual(resolve([WASI_TARGET]), {
|
|
52
|
+
[`${PACKAGE_NAME}-wasm32-wasi`]: VERSION,
|
|
53
|
+
})
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
test('declares every WASI flavor when only WASI targets are configured', (t) => {
|
|
57
|
+
t.deepEqual(resolve([WASI_TARGET, THREADLESS_WASI_TARGET]), {
|
|
58
|
+
[`${PACKAGE_NAME}-wasm32-wasi`]: VERSION,
|
|
59
|
+
[`${PACKAGE_NAME}-wasm32-wasip1`]: VERSION,
|
|
60
|
+
})
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
test('wasm.optionalDependency=true opts back into declaring WASI', (t) => {
|
|
64
|
+
t.deepEqual(
|
|
65
|
+
resolve([...NATIVE_TARGETS, WASI_TARGET], {
|
|
66
|
+
wasm: { optionalDependency: true },
|
|
67
|
+
}),
|
|
68
|
+
{
|
|
69
|
+
...NATIVE_ENTRIES,
|
|
70
|
+
[`${PACKAGE_NAME}-wasm32-wasi`]: VERSION,
|
|
71
|
+
},
|
|
72
|
+
)
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
test('wasm.optionalDependency=false opts out even for WASI-only builds', (t) => {
|
|
76
|
+
t.deepEqual(
|
|
77
|
+
resolve([WASI_TARGET], { wasm: { optionalDependency: false } }),
|
|
78
|
+
{},
|
|
79
|
+
)
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
test('drops a stale WASI entry left over from a previous release', (t) => {
|
|
83
|
+
// Consumers upgrading from a release that did declare the WASI package must
|
|
84
|
+
// not keep the entry, otherwise the regression survives the fix.
|
|
85
|
+
t.deepEqual(
|
|
86
|
+
resolve([...NATIVE_TARGETS, WASI_TARGET], {
|
|
87
|
+
existing: {
|
|
88
|
+
...NATIVE_ENTRIES,
|
|
89
|
+
[`${PACKAGE_NAME}-wasm32-wasi`]: '1.2.2',
|
|
90
|
+
},
|
|
91
|
+
}),
|
|
92
|
+
NATIVE_ENTRIES,
|
|
93
|
+
)
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
test('preserves unmanaged optionalDependencies', (t) => {
|
|
97
|
+
t.deepEqual(
|
|
98
|
+
resolve([...NATIVE_TARGETS, WASI_TARGET], {
|
|
99
|
+
existing: { 'unrelated-package': '^1.0.0' },
|
|
100
|
+
}),
|
|
101
|
+
{
|
|
102
|
+
'unrelated-package': '^1.0.0',
|
|
103
|
+
...NATIVE_ENTRIES,
|
|
104
|
+
},
|
|
105
|
+
)
|
|
106
|
+
})
|
package/src/api/pre-publish.ts
CHANGED
|
@@ -60,6 +60,7 @@ import {
|
|
|
60
60
|
type CommonPackageJsonFields,
|
|
61
61
|
type FileSystemTransactionWrite,
|
|
62
62
|
type Target,
|
|
63
|
+
type UserNapiConfig,
|
|
63
64
|
} from '../utils/index.js'
|
|
64
65
|
|
|
65
66
|
const debug = debugFactory('pre-publish')
|
|
@@ -227,6 +228,61 @@ async function copyOwnedTemporaryPath(source: string, destination: string) {
|
|
|
227
228
|
}
|
|
228
229
|
}
|
|
229
230
|
|
|
231
|
+
function wasiIsOnlyTarget(targets: Target[]) {
|
|
232
|
+
return (
|
|
233
|
+
targets.length > 0 && targets.every((target) => target.platform === 'wasi')
|
|
234
|
+
)
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Build the root package's `optionalDependencies` map.
|
|
239
|
+
*
|
|
240
|
+
* A WASI package is a fallback for hosts that cannot load a `.node` binary, and
|
|
241
|
+
* the generated binding loader selects it at require time rather than npm
|
|
242
|
+
* selecting it at install time. Declaring it as an `optionalDependency`
|
|
243
|
+
* alongside the native packages cannot express "install this only when nothing
|
|
244
|
+
* else matched": npm evaluates every entry independently, so every consumer
|
|
245
|
+
* downloads a `.wasm` binary they will never load.
|
|
246
|
+
*
|
|
247
|
+
* It is therefore only declared when WASI is the only configured target, which
|
|
248
|
+
* makes it the primary artifact rather than a fallback.
|
|
249
|
+
* `napi.wasm.optionalDependency` overrides the default in both directions.
|
|
250
|
+
*
|
|
251
|
+
* See https://github.com/rolldown/rolldown/issues/10556
|
|
252
|
+
*/
|
|
253
|
+
export function resolveRootOptionalDependencies({
|
|
254
|
+
existing,
|
|
255
|
+
managedPackageNames,
|
|
256
|
+
packageName,
|
|
257
|
+
targets,
|
|
258
|
+
version,
|
|
259
|
+
wasm,
|
|
260
|
+
}: {
|
|
261
|
+
existing: unknown
|
|
262
|
+
managedPackageNames: Iterable<string>
|
|
263
|
+
packageName: string
|
|
264
|
+
targets: Target[]
|
|
265
|
+
version: string
|
|
266
|
+
wasm?: UserNapiConfig['wasm']
|
|
267
|
+
}): Record<string, unknown> {
|
|
268
|
+
const optionalDependencies: Record<string, unknown> = {
|
|
269
|
+
...asRecord(existing),
|
|
270
|
+
}
|
|
271
|
+
for (const managedPackageName of managedPackageNames) {
|
|
272
|
+
for (const suffix of MANAGED_OPTIONAL_DEPENDENCY_SUFFIXES) {
|
|
273
|
+
delete optionalDependencies[`${managedPackageName}-${suffix}`]
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
const declareWasi = wasm?.optionalDependency ?? wasiIsOnlyTarget(targets)
|
|
277
|
+
for (const target of targets) {
|
|
278
|
+
if (target.platform === 'wasi' && !declareWasi) {
|
|
279
|
+
continue
|
|
280
|
+
}
|
|
281
|
+
optionalDependencies[`${packageName}-${target.platformArchABI}`] = version
|
|
282
|
+
}
|
|
283
|
+
return optionalDependencies
|
|
284
|
+
}
|
|
285
|
+
|
|
230
286
|
export async function prePublish(userOptions: PrePublishOptions) {
|
|
231
287
|
debug('Receive pre-publish options:')
|
|
232
288
|
debug(' %O', userOptions)
|
|
@@ -341,9 +397,6 @@ export async function prePublish(userOptions: PrePublishOptions) {
|
|
|
341
397
|
rootFacade,
|
|
342
398
|
)
|
|
343
399
|
}
|
|
344
|
-
const optionalDependencies = {
|
|
345
|
-
...asRecord(packageJson.optionalDependencies),
|
|
346
|
-
}
|
|
347
400
|
const managedPackageNames = new Set([packageName])
|
|
348
401
|
for (const flavorPackage of rootFacadeReconciliation.managedFlavorPackages) {
|
|
349
402
|
for (const suffix of MANAGED_OPTIONAL_DEPENDENCY_SUFFIXES) {
|
|
@@ -353,22 +406,19 @@ export async function prePublish(userOptions: PrePublishOptions) {
|
|
|
353
406
|
}
|
|
354
407
|
}
|
|
355
408
|
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
packageJson.engines?.node ?? MINIMUM_WASI_NODE_VERSION,
|
|
370
|
-
)
|
|
371
|
-
: undefined
|
|
409
|
+
const optionalDependencies = resolveRootOptionalDependencies({
|
|
410
|
+
existing: packageJson.optionalDependencies,
|
|
411
|
+
managedPackageNames,
|
|
412
|
+
packageName,
|
|
413
|
+
targets,
|
|
414
|
+
version: packageJson.version,
|
|
415
|
+
wasm,
|
|
416
|
+
})
|
|
417
|
+
const nodeEngine = wasiIsOnlyTarget(targets)
|
|
418
|
+
? restrictWasiNodeEngine(
|
|
419
|
+
packageJson.engines?.node ?? MINIMUM_WASI_NODE_VERSION,
|
|
420
|
+
)
|
|
421
|
+
: undefined
|
|
372
422
|
const rootReleasePlan: RootReleaseMaterializationPlan = {
|
|
373
423
|
packageJson: reconciledPackageJson,
|
|
374
424
|
optionalDependencies,
|
package/src/utils/config.ts
CHANGED
|
@@ -72,10 +72,29 @@ export interface UserNapiConfig {
|
|
|
72
72
|
*/
|
|
73
73
|
maximumMemory?: number
|
|
74
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Whether the generated `<packageName>-wasm32-wasi` package is declared as
|
|
77
|
+
* an `optionalDependency` of the root package.
|
|
78
|
+
*
|
|
79
|
+
* When native targets are configured the WASI package is a fallback for
|
|
80
|
+
* hosts that cannot load a `.node` binary, so declaring it would make every
|
|
81
|
+
* consumer download the `.wasm` binary they will never load. It is
|
|
82
|
+
* therefore omitted by default and is expected to be installed on demand by
|
|
83
|
+
* the environments that need it.
|
|
84
|
+
*
|
|
85
|
+
* When WASI is the only configured target it is the primary artifact and is
|
|
86
|
+
* declared by default.
|
|
87
|
+
*
|
|
88
|
+
* Set this explicitly to override either default.
|
|
89
|
+
*
|
|
90
|
+
* @default true when every configured target is WASI, false otherwise
|
|
91
|
+
*/
|
|
92
|
+
optionalDependency?: boolean
|
|
93
|
+
|
|
75
94
|
/**
|
|
76
95
|
* Browser wasm binding configuration
|
|
77
96
|
*/
|
|
78
|
-
browser
|
|
97
|
+
browser?: {
|
|
79
98
|
/**
|
|
80
99
|
* Whether to use fs module in browser
|
|
81
100
|
*/
|