@napi-rs/cli 3.10.6 → 3.10.8
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 +432 -22
- package/dist/index.cjs +432 -22
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +432 -22
- package/docs/wasi.md +68 -0
- package/package.json +2 -2
- package/src/api/__tests__/artifacts.spec.ts +1644 -1
- package/src/api/__tests__/templates.spec.ts +692 -5
- package/src/api/artifacts.ts +547 -21
- package/src/api/templates/load-wasi-template.ts +213 -1
- package/src/utils/__tests__/__snapshots__/target.spec.ts.md +7 -0
- package/src/utils/__tests__/__snapshots__/target.spec.ts.snap +0 -0
- package/src/utils/misc.ts +2 -2
- package/src/utils/target.ts +1 -0
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs'
|
|
2
|
-
import { mkdir, readFile, rm, writeFile } from 'node:fs/promises'
|
|
2
|
+
import { mkdir, readFile, rm, symlink, writeFile } from 'node:fs/promises'
|
|
3
3
|
import { tmpdir } from 'node:os'
|
|
4
4
|
import { join } from 'node:path'
|
|
5
5
|
|
|
6
6
|
import ava, { type TestFn } from 'ava'
|
|
7
7
|
|
|
8
8
|
import { collectArtifacts } from '../artifacts.js'
|
|
9
|
+
import { WASI_ARTIFACT_METADATA_PREFIX } from '../build.js'
|
|
9
10
|
|
|
10
11
|
const test = ava as TestFn<{ tmpDir: string }>
|
|
11
12
|
|
|
@@ -110,3 +111,1645 @@ test('resolves a relative WASI build output directory from cwd', async (t) => {
|
|
|
110
111
|
),
|
|
111
112
|
)
|
|
112
113
|
})
|
|
114
|
+
|
|
115
|
+
test('keeps the generated WASI root entry when package.json#main is a handwritten wrapper', async (t) => {
|
|
116
|
+
const { tmpDir } = t.context
|
|
117
|
+
const binaryName = 'mixed-entry'
|
|
118
|
+
const packageName = '@napi-rs/mixed-entry'
|
|
119
|
+
const artifactsDir = join(tmpDir, 'artifacts')
|
|
120
|
+
|
|
121
|
+
// Mirrors packages like @node-rs/bcrypt: `main` points at a handwritten
|
|
122
|
+
// `index.js` wrapper while `napi build --js binding.js` generates the
|
|
123
|
+
// loader under a different name recorded as `rootEntry` in the WASI
|
|
124
|
+
// loader metadata.
|
|
125
|
+
await mkdir(artifactsDir, { recursive: true })
|
|
126
|
+
await writeFile(
|
|
127
|
+
join(tmpDir, 'package.json'),
|
|
128
|
+
JSON.stringify({
|
|
129
|
+
name: packageName,
|
|
130
|
+
version: '0.0.0',
|
|
131
|
+
main: 'index.js',
|
|
132
|
+
napi: {
|
|
133
|
+
binaryName,
|
|
134
|
+
targets: ['x86_64-unknown-linux-gnu', 'wasm32-wasip1-threads'],
|
|
135
|
+
},
|
|
136
|
+
}),
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
const loader = `${WASI_ARTIFACT_METADATA_PREFIX}${JSON.stringify({
|
|
140
|
+
version: 2,
|
|
141
|
+
rootEntry: 'binding.js',
|
|
142
|
+
exports: ['create'],
|
|
143
|
+
managedRootEntries: [
|
|
144
|
+
'browser.js',
|
|
145
|
+
'binding.js',
|
|
146
|
+
`${binaryName}.wasm`,
|
|
147
|
+
`${binaryName}.debug.wasm`,
|
|
148
|
+
],
|
|
149
|
+
})}\nmodule.exports = {}\n`
|
|
150
|
+
|
|
151
|
+
await Promise.all([
|
|
152
|
+
writeFile(join(artifactsDir, `${binaryName}.linux-x64-gnu.node`), 'bin'),
|
|
153
|
+
writeFile(join(artifactsDir, `${binaryName}.wasm32-wasi.wasm`), 'wasm'),
|
|
154
|
+
writeFile(join(artifactsDir, `${binaryName}.wasi.cjs`), loader),
|
|
155
|
+
writeFile(
|
|
156
|
+
join(artifactsDir, `${binaryName}.wasi.d.cts`),
|
|
157
|
+
'declare const _default: {}\nexport = _default\n',
|
|
158
|
+
),
|
|
159
|
+
writeFile(
|
|
160
|
+
join(artifactsDir, `${binaryName}.wasi-browser.js`),
|
|
161
|
+
'export {}\n',
|
|
162
|
+
),
|
|
163
|
+
writeFile(join(artifactsDir, 'wasi-worker.mjs'), 'export {}\n'),
|
|
164
|
+
writeFile(join(artifactsDir, 'wasi-worker-browser.mjs'), 'export {}\n'),
|
|
165
|
+
// The generated loader sits at the package root alongside the wrapper,
|
|
166
|
+
// next to neither the `.node` artifact nor `package.json#main`.
|
|
167
|
+
writeFile(join(tmpDir, 'binding.js'), 'module.exports = {}\n'),
|
|
168
|
+
writeFile(join(tmpDir, 'index.js'), 'module.exports = {}\n'),
|
|
169
|
+
])
|
|
170
|
+
|
|
171
|
+
await collectArtifacts({ cwd: tmpDir })
|
|
172
|
+
|
|
173
|
+
// `binding.js` is a managed root entry that `package.json#main` does not
|
|
174
|
+
// cover; it must be kept rather than swept as a stale destination.
|
|
175
|
+
t.is(
|
|
176
|
+
await readFile(join(tmpDir, 'binding.js'), 'utf8'),
|
|
177
|
+
'module.exports = {}\n',
|
|
178
|
+
)
|
|
179
|
+
t.is(
|
|
180
|
+
await readFile(join(tmpDir, 'index.js'), 'utf8'),
|
|
181
|
+
'module.exports = {}\n',
|
|
182
|
+
)
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
test('the WASI artifact source wins over a stale package-root root entry', async (t) => {
|
|
186
|
+
const { tmpDir } = t.context
|
|
187
|
+
const binaryName = 'mixed-entry'
|
|
188
|
+
const packageName = '@napi-rs/mixed-entry'
|
|
189
|
+
const artifactsDir = join(tmpDir, 'artifacts')
|
|
190
|
+
const buildOutputDir = join(tmpDir, 'build-output')
|
|
191
|
+
|
|
192
|
+
// Same layout as above but with --build-output-dir: binaries land in
|
|
193
|
+
// `artifacts`, the generated WASI loader set including `binding.js` lands
|
|
194
|
+
// in `build-output`, and the package root holds a stale `binding.js` next
|
|
195
|
+
// to the handwritten `index.js` wrapper. A second stale copy beside the
|
|
196
|
+
// `.node` artifacts must not suppress the WASI-source copy either.
|
|
197
|
+
await mkdir(artifactsDir, { recursive: true })
|
|
198
|
+
await mkdir(buildOutputDir, { recursive: true })
|
|
199
|
+
await writeFile(
|
|
200
|
+
join(tmpDir, 'package.json'),
|
|
201
|
+
JSON.stringify({
|
|
202
|
+
name: packageName,
|
|
203
|
+
version: '0.0.0',
|
|
204
|
+
main: 'index.js',
|
|
205
|
+
napi: {
|
|
206
|
+
binaryName,
|
|
207
|
+
targets: ['x86_64-unknown-linux-gnu', 'wasm32-wasip1-threads'],
|
|
208
|
+
},
|
|
209
|
+
}),
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
const loader = `${WASI_ARTIFACT_METADATA_PREFIX}${JSON.stringify({
|
|
213
|
+
version: 2,
|
|
214
|
+
rootEntry: 'binding.js',
|
|
215
|
+
exports: ['create'],
|
|
216
|
+
managedRootEntries: ['browser.js', 'binding.js'],
|
|
217
|
+
})}\nmodule.exports = {}\n`
|
|
218
|
+
const freshBinding = 'module.exports = { fresh: true }\n'
|
|
219
|
+
|
|
220
|
+
await Promise.all([
|
|
221
|
+
writeFile(join(artifactsDir, `${binaryName}.linux-x64-gnu.node`), 'bin'),
|
|
222
|
+
writeFile(join(artifactsDir, `${binaryName}.wasm32-wasi.wasm`), 'wasm'),
|
|
223
|
+
writeFile(
|
|
224
|
+
join(artifactsDir, 'binding.js'),
|
|
225
|
+
'module.exports = { stale: "native-adjacent" }\n',
|
|
226
|
+
),
|
|
227
|
+
writeFile(join(buildOutputDir, `${binaryName}.wasi.cjs`), loader),
|
|
228
|
+
writeFile(
|
|
229
|
+
join(buildOutputDir, `${binaryName}.wasi.d.cts`),
|
|
230
|
+
'declare const _default: {}\nexport = _default\n',
|
|
231
|
+
),
|
|
232
|
+
writeFile(
|
|
233
|
+
join(buildOutputDir, `${binaryName}.wasi-browser.js`),
|
|
234
|
+
'export {}\n',
|
|
235
|
+
),
|
|
236
|
+
writeFile(join(buildOutputDir, 'wasi-worker.mjs'), 'export {}\n'),
|
|
237
|
+
writeFile(join(buildOutputDir, 'wasi-worker-browser.mjs'), 'export {}\n'),
|
|
238
|
+
writeFile(join(buildOutputDir, 'binding.js'), freshBinding),
|
|
239
|
+
writeFile(join(tmpDir, 'binding.js'), 'module.exports = { stale: true }\n'),
|
|
240
|
+
writeFile(join(tmpDir, 'index.js'), 'module.exports = {}\n'),
|
|
241
|
+
])
|
|
242
|
+
|
|
243
|
+
await collectArtifacts({
|
|
244
|
+
cwd: tmpDir,
|
|
245
|
+
buildOutputDir: 'build-output',
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
// The artifact-source copy of the generated loader must replace the stale
|
|
249
|
+
// package-root file; the handwritten wrapper is untouched.
|
|
250
|
+
t.is(await readFile(join(tmpDir, 'binding.js'), 'utf8'), freshBinding)
|
|
251
|
+
t.is(
|
|
252
|
+
await readFile(join(tmpDir, 'index.js'), 'utf8'),
|
|
253
|
+
'module.exports = {}\n',
|
|
254
|
+
)
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
test('a WASI-source root entry never replaces the shared native main loader', async (t) => {
|
|
258
|
+
const { tmpDir } = t.context
|
|
259
|
+
const binaryName = 'same-entry'
|
|
260
|
+
const packageName = '@napi-rs/same-entry'
|
|
261
|
+
const artifactsDir = join(tmpDir, 'artifacts')
|
|
262
|
+
const buildOutputDir = join(tmpDir, 'build-output')
|
|
263
|
+
|
|
264
|
+
// The default layout: `main` is the generated loader `index.js`, which the
|
|
265
|
+
// WASI metadata also records as rootEntry. The `index.js` inside the WASI
|
|
266
|
+
// artifact source is the WASI loader (`*.wasi.cjs` chain), so it must lose
|
|
267
|
+
// to the native loader copies even though all three differ.
|
|
268
|
+
await mkdir(artifactsDir, { recursive: true })
|
|
269
|
+
await mkdir(buildOutputDir, { recursive: true })
|
|
270
|
+
await writeFile(
|
|
271
|
+
join(tmpDir, 'package.json'),
|
|
272
|
+
JSON.stringify({
|
|
273
|
+
name: packageName,
|
|
274
|
+
version: '0.0.0',
|
|
275
|
+
main: 'index.js',
|
|
276
|
+
napi: {
|
|
277
|
+
binaryName,
|
|
278
|
+
targets: ['x86_64-unknown-linux-gnu', 'wasm32-wasip1-threads'],
|
|
279
|
+
},
|
|
280
|
+
}),
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
const loader = `${WASI_ARTIFACT_METADATA_PREFIX}${JSON.stringify({
|
|
284
|
+
version: 2,
|
|
285
|
+
rootEntry: 'index.js',
|
|
286
|
+
exports: ['create'],
|
|
287
|
+
managedRootEntries: ['browser.js', 'index.js'],
|
|
288
|
+
})}\nmodule.exports = {}\n`
|
|
289
|
+
|
|
290
|
+
await Promise.all([
|
|
291
|
+
writeFile(join(artifactsDir, `${binaryName}.linux-x64-gnu.node`), 'bin'),
|
|
292
|
+
writeFile(join(artifactsDir, `${binaryName}.wasm32-wasi.wasm`), 'wasm'),
|
|
293
|
+
writeFile(join(buildOutputDir, `${binaryName}.wasi.cjs`), loader),
|
|
294
|
+
writeFile(
|
|
295
|
+
join(buildOutputDir, `${binaryName}.wasi.d.cts`),
|
|
296
|
+
'declare const _default: {}\nexport = _default\n',
|
|
297
|
+
),
|
|
298
|
+
writeFile(
|
|
299
|
+
join(buildOutputDir, `${binaryName}.wasi-browser.js`),
|
|
300
|
+
'export {}\n',
|
|
301
|
+
),
|
|
302
|
+
writeFile(join(buildOutputDir, 'wasi-worker.mjs'), 'export {}\n'),
|
|
303
|
+
writeFile(join(buildOutputDir, 'wasi-worker-browser.mjs'), 'export {}\n'),
|
|
304
|
+
writeFile(
|
|
305
|
+
join(buildOutputDir, 'index.js'),
|
|
306
|
+
"module.exports = require('./same-entry.wasi.cjs')\n",
|
|
307
|
+
),
|
|
308
|
+
writeFile(join(tmpDir, 'index.js'), 'module.exports = { native: true }\n'),
|
|
309
|
+
])
|
|
310
|
+
|
|
311
|
+
await collectArtifacts({
|
|
312
|
+
cwd: tmpDir,
|
|
313
|
+
buildOutputDir: 'build-output',
|
|
314
|
+
})
|
|
315
|
+
|
|
316
|
+
// The committed native loader survives; the WASI-source copy of the same
|
|
317
|
+
// name must not overwrite it.
|
|
318
|
+
t.is(
|
|
319
|
+
await readFile(join(tmpDir, 'index.js'), 'utf8'),
|
|
320
|
+
'module.exports = { native: true }\n',
|
|
321
|
+
)
|
|
322
|
+
})
|
|
323
|
+
|
|
324
|
+
test('a package main like ./binding.js aliases the same root destination', async (t) => {
|
|
325
|
+
const { tmpDir } = t.context
|
|
326
|
+
const binaryName = 'alias-entry'
|
|
327
|
+
const packageName = '@napi-rs/alias-entry'
|
|
328
|
+
const artifactsDir = join(tmpDir, 'artifacts')
|
|
329
|
+
const buildOutputDir = join(tmpDir, 'build-output')
|
|
330
|
+
|
|
331
|
+
// `main: "./binding.js"` and metadata `rootEntry: "binding.js"` resolve to
|
|
332
|
+
// the same package-root path, so the entry is the shared native loader and
|
|
333
|
+
// the WASI-source copy must not claim it.
|
|
334
|
+
await mkdir(artifactsDir, { recursive: true })
|
|
335
|
+
await mkdir(buildOutputDir, { recursive: true })
|
|
336
|
+
await writeFile(
|
|
337
|
+
join(tmpDir, 'package.json'),
|
|
338
|
+
JSON.stringify({
|
|
339
|
+
name: packageName,
|
|
340
|
+
version: '0.0.0',
|
|
341
|
+
main: './binding.js',
|
|
342
|
+
napi: {
|
|
343
|
+
binaryName,
|
|
344
|
+
targets: ['x86_64-unknown-linux-gnu', 'wasm32-wasip1-threads'],
|
|
345
|
+
},
|
|
346
|
+
}),
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
const loader = `${WASI_ARTIFACT_METADATA_PREFIX}${JSON.stringify({
|
|
350
|
+
version: 2,
|
|
351
|
+
rootEntry: 'binding.js',
|
|
352
|
+
exports: ['create'],
|
|
353
|
+
managedRootEntries: ['browser.js', 'binding.js'],
|
|
354
|
+
})}\nmodule.exports = {}\n`
|
|
355
|
+
|
|
356
|
+
await Promise.all([
|
|
357
|
+
writeFile(join(artifactsDir, `${binaryName}.linux-x64-gnu.node`), 'bin'),
|
|
358
|
+
writeFile(join(artifactsDir, `${binaryName}.wasm32-wasi.wasm`), 'wasm'),
|
|
359
|
+
writeFile(join(buildOutputDir, `${binaryName}.wasi.cjs`), loader),
|
|
360
|
+
writeFile(
|
|
361
|
+
join(buildOutputDir, `${binaryName}.wasi.d.cts`),
|
|
362
|
+
'declare const _default: {}\nexport = _default\n',
|
|
363
|
+
),
|
|
364
|
+
writeFile(
|
|
365
|
+
join(buildOutputDir, `${binaryName}.wasi-browser.js`),
|
|
366
|
+
'export {}\n',
|
|
367
|
+
),
|
|
368
|
+
writeFile(join(buildOutputDir, 'wasi-worker.mjs'), 'export {}\n'),
|
|
369
|
+
writeFile(join(buildOutputDir, 'wasi-worker-browser.mjs'), 'export {}\n'),
|
|
370
|
+
writeFile(
|
|
371
|
+
join(buildOutputDir, 'binding.js'),
|
|
372
|
+
"module.exports = require('./alias-entry.wasi.cjs')\n",
|
|
373
|
+
),
|
|
374
|
+
writeFile(
|
|
375
|
+
join(tmpDir, 'binding.js'),
|
|
376
|
+
'module.exports = { native: true }\n',
|
|
377
|
+
),
|
|
378
|
+
])
|
|
379
|
+
|
|
380
|
+
await collectArtifacts({
|
|
381
|
+
cwd: tmpDir,
|
|
382
|
+
buildOutputDir: 'build-output',
|
|
383
|
+
})
|
|
384
|
+
|
|
385
|
+
t.is(
|
|
386
|
+
await readFile(join(tmpDir, 'binding.js'), 'utf8'),
|
|
387
|
+
'module.exports = { native: true }\n',
|
|
388
|
+
)
|
|
389
|
+
})
|
|
390
|
+
|
|
391
|
+
test('a symlinked parent cannot redirect a root entry outside the package root', async (t) => {
|
|
392
|
+
const { tmpDir } = t.context
|
|
393
|
+
const binaryName = 'link-entry'
|
|
394
|
+
const packageName = '@napi-rs/link-entry'
|
|
395
|
+
const artifactsDir = join(tmpDir, 'artifacts')
|
|
396
|
+
const siblingDir = join(tmpDir, '..', `sibling-${Date.now()}`)
|
|
397
|
+
|
|
398
|
+
// `nested/binding.js` passes the lexical `..` check, but `nested` is a
|
|
399
|
+
// symlink to a sibling directory, so the canonical destination leaves the
|
|
400
|
+
// package root even though it stays inside the transaction boundary.
|
|
401
|
+
await mkdir(artifactsDir, { recursive: true })
|
|
402
|
+
await mkdir(siblingDir, { recursive: true })
|
|
403
|
+
await symlink(siblingDir, join(tmpDir, 'nested'))
|
|
404
|
+
await writeFile(
|
|
405
|
+
join(tmpDir, 'package.json'),
|
|
406
|
+
JSON.stringify({
|
|
407
|
+
name: packageName,
|
|
408
|
+
version: '0.0.0',
|
|
409
|
+
main: 'index.js',
|
|
410
|
+
napi: {
|
|
411
|
+
binaryName,
|
|
412
|
+
targets: ['x86_64-unknown-linux-gnu', 'wasm32-wasip1-threads'],
|
|
413
|
+
},
|
|
414
|
+
}),
|
|
415
|
+
)
|
|
416
|
+
|
|
417
|
+
const loader = `${WASI_ARTIFACT_METADATA_PREFIX}${JSON.stringify({
|
|
418
|
+
version: 2,
|
|
419
|
+
rootEntry: 'nested/binding.js',
|
|
420
|
+
exports: ['create'],
|
|
421
|
+
managedRootEntries: ['browser.js', 'nested/binding.js'],
|
|
422
|
+
})}\nmodule.exports = {}\n`
|
|
423
|
+
|
|
424
|
+
await Promise.all([
|
|
425
|
+
writeFile(join(artifactsDir, `${binaryName}.linux-x64-gnu.node`), 'bin'),
|
|
426
|
+
writeFile(join(artifactsDir, `${binaryName}.wasm32-wasi.wasm`), 'wasm'),
|
|
427
|
+
writeFile(join(artifactsDir, `${binaryName}.wasi.cjs`), loader),
|
|
428
|
+
writeFile(
|
|
429
|
+
join(artifactsDir, `${binaryName}.wasi.d.cts`),
|
|
430
|
+
'declare const _default: {}\nexport = _default\n',
|
|
431
|
+
),
|
|
432
|
+
writeFile(
|
|
433
|
+
join(artifactsDir, `${binaryName}.wasi-browser.js`),
|
|
434
|
+
'export {}\n',
|
|
435
|
+
),
|
|
436
|
+
writeFile(join(artifactsDir, 'wasi-worker.mjs'), 'export {}\n'),
|
|
437
|
+
writeFile(join(artifactsDir, 'wasi-worker-browser.mjs'), 'export {}\n'),
|
|
438
|
+
writeFile(join(tmpDir, 'index.js'), 'module.exports = {}\n'),
|
|
439
|
+
])
|
|
440
|
+
|
|
441
|
+
try {
|
|
442
|
+
await t.throwsAsync(collectArtifacts({ cwd: tmpDir }), {
|
|
443
|
+
message: /resolves outside the package root/,
|
|
444
|
+
})
|
|
445
|
+
t.false(existsSync(join(siblingDir, 'binding.js')))
|
|
446
|
+
} finally {
|
|
447
|
+
await rm(siblingDir, { recursive: true, force: true })
|
|
448
|
+
}
|
|
449
|
+
})
|
|
450
|
+
|
|
451
|
+
test('a WASI-declared index.js resolves from the artifact source when main is a different wrapper', async (t) => {
|
|
452
|
+
const { tmpDir } = t.context
|
|
453
|
+
const binaryName = 'index-entry'
|
|
454
|
+
const packageName = '@napi-rs/index-entry'
|
|
455
|
+
const artifactsDir = join(tmpDir, 'artifacts')
|
|
456
|
+
const buildOutputDir = join(tmpDir, 'build-output')
|
|
457
|
+
|
|
458
|
+
// `main` points at a handwritten `wrapper.js` and the WASI loader metadata
|
|
459
|
+
// declares `index.js` as its generated root entry. `index.js` is only a
|
|
460
|
+
// shared name when it is the effective main; here it is a distinct WASI
|
|
461
|
+
// root entry that must resolve from the build output, not from the stale
|
|
462
|
+
// package-root copy.
|
|
463
|
+
await mkdir(artifactsDir, { recursive: true })
|
|
464
|
+
await mkdir(buildOutputDir, { recursive: true })
|
|
465
|
+
await writeFile(
|
|
466
|
+
join(tmpDir, 'package.json'),
|
|
467
|
+
JSON.stringify({
|
|
468
|
+
name: packageName,
|
|
469
|
+
version: '0.0.0',
|
|
470
|
+
main: 'wrapper.js',
|
|
471
|
+
napi: {
|
|
472
|
+
binaryName,
|
|
473
|
+
targets: ['x86_64-unknown-linux-gnu', 'wasm32-wasip1-threads'],
|
|
474
|
+
},
|
|
475
|
+
}),
|
|
476
|
+
)
|
|
477
|
+
|
|
478
|
+
const loader = `${WASI_ARTIFACT_METADATA_PREFIX}${JSON.stringify({
|
|
479
|
+
version: 2,
|
|
480
|
+
rootEntry: 'index.js',
|
|
481
|
+
exports: ['create'],
|
|
482
|
+
managedRootEntries: ['browser.js', 'index.js'],
|
|
483
|
+
})}\nmodule.exports = {}\n`
|
|
484
|
+
const freshIndex = "module.exports = require('./index-entry.wasi.cjs')\n"
|
|
485
|
+
|
|
486
|
+
await Promise.all([
|
|
487
|
+
writeFile(join(artifactsDir, `${binaryName}.linux-x64-gnu.node`), 'bin'),
|
|
488
|
+
writeFile(join(artifactsDir, `${binaryName}.wasm32-wasi.wasm`), 'wasm'),
|
|
489
|
+
writeFile(join(buildOutputDir, `${binaryName}.wasi.cjs`), loader),
|
|
490
|
+
writeFile(
|
|
491
|
+
join(buildOutputDir, `${binaryName}.wasi.d.cts`),
|
|
492
|
+
'declare const _default: {}\nexport = _default\n',
|
|
493
|
+
),
|
|
494
|
+
writeFile(
|
|
495
|
+
join(buildOutputDir, `${binaryName}.wasi-browser.js`),
|
|
496
|
+
'export {}\n',
|
|
497
|
+
),
|
|
498
|
+
writeFile(join(buildOutputDir, 'wasi-worker.mjs'), 'export {}\n'),
|
|
499
|
+
writeFile(join(buildOutputDir, 'wasi-worker-browser.mjs'), 'export {}\n'),
|
|
500
|
+
writeFile(join(buildOutputDir, 'index.js'), freshIndex),
|
|
501
|
+
writeFile(join(tmpDir, 'index.js'), 'module.exports = { stale: true }\n'),
|
|
502
|
+
writeFile(
|
|
503
|
+
join(tmpDir, 'wrapper.js'),
|
|
504
|
+
'module.exports = { native: true }\n',
|
|
505
|
+
),
|
|
506
|
+
])
|
|
507
|
+
|
|
508
|
+
await collectArtifacts({
|
|
509
|
+
cwd: tmpDir,
|
|
510
|
+
buildOutputDir: 'build-output',
|
|
511
|
+
})
|
|
512
|
+
|
|
513
|
+
// The WASI-source copy of the declared `index.js` replaces the stale
|
|
514
|
+
// package-root file; the handwritten wrapper stays untouched.
|
|
515
|
+
t.is(await readFile(join(tmpDir, 'index.js'), 'utf8'), freshIndex)
|
|
516
|
+
t.is(
|
|
517
|
+
await readFile(join(tmpDir, 'wrapper.js'), 'utf8'),
|
|
518
|
+
'module.exports = { native: true }\n',
|
|
519
|
+
)
|
|
520
|
+
})
|
|
521
|
+
|
|
522
|
+
test('a distinct root entry follows the threaded root loader across WASI flavors', async (t) => {
|
|
523
|
+
const { tmpDir } = t.context
|
|
524
|
+
const binaryName = 'flavor-entry'
|
|
525
|
+
const packageName = '@napi-rs/flavor-entry'
|
|
526
|
+
const nativeDir = join(tmpDir, 'artifacts', 'native')
|
|
527
|
+
const threadlessDir = join(tmpDir, 'artifacts', 'wasip1')
|
|
528
|
+
const threadsDir = join(tmpDir, 'artifacts', 'threads')
|
|
529
|
+
|
|
530
|
+
// Both WASI flavors are configured, the threadless target first. The root
|
|
531
|
+
// loader comes from the threaded target, so the generated `binding.js`
|
|
532
|
+
// must come from the threaded source too: each flavor's copy embeds
|
|
533
|
+
// loader references for its own `*.wasi.cjs` chain.
|
|
534
|
+
await Promise.all([
|
|
535
|
+
mkdir(nativeDir, { recursive: true }),
|
|
536
|
+
mkdir(threadlessDir, { recursive: true }),
|
|
537
|
+
mkdir(threadsDir, { recursive: true }),
|
|
538
|
+
])
|
|
539
|
+
await writeFile(
|
|
540
|
+
join(tmpDir, 'package.json'),
|
|
541
|
+
JSON.stringify({
|
|
542
|
+
name: packageName,
|
|
543
|
+
version: '0.0.0',
|
|
544
|
+
main: 'index.js',
|
|
545
|
+
napi: {
|
|
546
|
+
binaryName,
|
|
547
|
+
targets: [
|
|
548
|
+
'x86_64-unknown-linux-gnu',
|
|
549
|
+
'wasm32-wasip1',
|
|
550
|
+
'wasm32-wasip1-threads',
|
|
551
|
+
],
|
|
552
|
+
},
|
|
553
|
+
}),
|
|
554
|
+
)
|
|
555
|
+
|
|
556
|
+
const loaderFor = (rootEntryFlavor: string) =>
|
|
557
|
+
`${WASI_ARTIFACT_METADATA_PREFIX}${JSON.stringify({
|
|
558
|
+
version: 2,
|
|
559
|
+
rootEntry: 'binding.js',
|
|
560
|
+
exports: ['create'],
|
|
561
|
+
managedRootEntries: ['browser.js', 'binding.js'],
|
|
562
|
+
})}\nmodule.exports = { flavor: '${rootEntryFlavor}' }\n`
|
|
563
|
+
|
|
564
|
+
await Promise.all([
|
|
565
|
+
writeFile(join(nativeDir, `${binaryName}.linux-x64-gnu.node`), 'bin'),
|
|
566
|
+
writeFile(
|
|
567
|
+
join(threadsDir, `${binaryName}.wasm32-wasi.wasm`),
|
|
568
|
+
'threads wasm',
|
|
569
|
+
),
|
|
570
|
+
writeFile(join(threadsDir, `${binaryName}.wasi.cjs`), loaderFor('threads')),
|
|
571
|
+
writeFile(
|
|
572
|
+
join(threadsDir, `${binaryName}.wasi.d.cts`),
|
|
573
|
+
'declare const _default: {}\nexport = _default\n',
|
|
574
|
+
),
|
|
575
|
+
writeFile(join(threadsDir, `${binaryName}.wasi-browser.js`), 'export {}\n'),
|
|
576
|
+
writeFile(join(threadsDir, 'wasi-worker.mjs'), 'export {}\n'),
|
|
577
|
+
writeFile(join(threadsDir, 'wasi-worker-browser.mjs'), 'export {}\n'),
|
|
578
|
+
writeFile(
|
|
579
|
+
join(threadsDir, 'binding.js'),
|
|
580
|
+
"module.exports = require('./flavor-entry.wasi.cjs')\n",
|
|
581
|
+
),
|
|
582
|
+
writeFile(
|
|
583
|
+
join(threadlessDir, `${binaryName}.wasm32-wasip1.wasm`),
|
|
584
|
+
'threadless wasm',
|
|
585
|
+
),
|
|
586
|
+
writeFile(
|
|
587
|
+
join(threadlessDir, `${binaryName}.wasip1.cjs`),
|
|
588
|
+
loaderFor('wasip1'),
|
|
589
|
+
),
|
|
590
|
+
writeFile(
|
|
591
|
+
join(threadlessDir, `${binaryName}.wasip1.d.cts`),
|
|
592
|
+
'declare const _default: {}\nexport = _default\n',
|
|
593
|
+
),
|
|
594
|
+
writeFile(
|
|
595
|
+
join(threadlessDir, `${binaryName}.wasip1-browser.js`),
|
|
596
|
+
'export {}\n',
|
|
597
|
+
),
|
|
598
|
+
writeFile(
|
|
599
|
+
join(threadlessDir, `${binaryName}.wasip1-deferred.js`),
|
|
600
|
+
'export {}\n',
|
|
601
|
+
),
|
|
602
|
+
writeFile(
|
|
603
|
+
join(threadlessDir, `${binaryName}.wasip1-deferred.d.ts`),
|
|
604
|
+
'export {}\n',
|
|
605
|
+
),
|
|
606
|
+
writeFile(
|
|
607
|
+
join(threadlessDir, 'binding.js'),
|
|
608
|
+
"module.exports = require('./flavor-entry.wasip1.cjs')\n",
|
|
609
|
+
),
|
|
610
|
+
writeFile(join(tmpDir, 'index.js'), 'module.exports = { native: true }\n'),
|
|
611
|
+
])
|
|
612
|
+
|
|
613
|
+
await collectArtifacts({ cwd: tmpDir })
|
|
614
|
+
|
|
615
|
+
// The published root entry must match the threaded root loader even
|
|
616
|
+
// though the threadless target is configured first.
|
|
617
|
+
t.is(
|
|
618
|
+
await readFile(join(tmpDir, 'binding.js'), 'utf8'),
|
|
619
|
+
"module.exports = require('./flavor-entry.wasi.cjs')\n",
|
|
620
|
+
)
|
|
621
|
+
})
|
|
622
|
+
|
|
623
|
+
test('a main specifier Node resolves to a declared root entry stays shared', async (t) => {
|
|
624
|
+
const { tmpDir } = t.context
|
|
625
|
+
const binaryName = 'extless-entry'
|
|
626
|
+
const packageName = '@napi-rs/extless-entry'
|
|
627
|
+
const artifactsDir = join(tmpDir, 'artifacts')
|
|
628
|
+
const buildOutputDir = join(tmpDir, 'build-output')
|
|
629
|
+
|
|
630
|
+
// CommonJS `main` resolution maps `./binding` to `binding.js`, so the
|
|
631
|
+
// WASI-declared `binding.js` is the native root entry — the WASI-source
|
|
632
|
+
// copy is the WASI loader chain and must never claim that destination.
|
|
633
|
+
await mkdir(artifactsDir, { recursive: true })
|
|
634
|
+
await mkdir(buildOutputDir, { recursive: true })
|
|
635
|
+
await writeFile(
|
|
636
|
+
join(tmpDir, 'package.json'),
|
|
637
|
+
JSON.stringify({
|
|
638
|
+
name: packageName,
|
|
639
|
+
version: '0.0.0',
|
|
640
|
+
main: './binding',
|
|
641
|
+
napi: {
|
|
642
|
+
binaryName,
|
|
643
|
+
targets: ['x86_64-unknown-linux-gnu', 'wasm32-wasip1-threads'],
|
|
644
|
+
},
|
|
645
|
+
}),
|
|
646
|
+
)
|
|
647
|
+
|
|
648
|
+
const loader = `${WASI_ARTIFACT_METADATA_PREFIX}${JSON.stringify({
|
|
649
|
+
version: 2,
|
|
650
|
+
rootEntry: 'binding.js',
|
|
651
|
+
exports: ['create'],
|
|
652
|
+
managedRootEntries: ['browser.js', 'binding.js'],
|
|
653
|
+
})}\nmodule.exports = {}\n`
|
|
654
|
+
|
|
655
|
+
await Promise.all([
|
|
656
|
+
writeFile(join(artifactsDir, `${binaryName}.linux-x64-gnu.node`), 'bin'),
|
|
657
|
+
writeFile(join(artifactsDir, `${binaryName}.wasm32-wasi.wasm`), 'wasm'),
|
|
658
|
+
writeFile(join(buildOutputDir, `${binaryName}.wasi.cjs`), loader),
|
|
659
|
+
writeFile(
|
|
660
|
+
join(buildOutputDir, `${binaryName}.wasi.d.cts`),
|
|
661
|
+
'declare const _default: {}\nexport = _default\n',
|
|
662
|
+
),
|
|
663
|
+
writeFile(
|
|
664
|
+
join(buildOutputDir, `${binaryName}.wasi-browser.js`),
|
|
665
|
+
'export {}\n',
|
|
666
|
+
),
|
|
667
|
+
writeFile(join(buildOutputDir, 'wasi-worker.mjs'), 'export {}\n'),
|
|
668
|
+
writeFile(join(buildOutputDir, 'wasi-worker-browser.mjs'), 'export {}\n'),
|
|
669
|
+
writeFile(
|
|
670
|
+
join(buildOutputDir, 'binding.js'),
|
|
671
|
+
"module.exports = require('./extless-entry.wasi.cjs')\n",
|
|
672
|
+
),
|
|
673
|
+
writeFile(
|
|
674
|
+
join(tmpDir, 'binding.js'),
|
|
675
|
+
'module.exports = { native: true }\n',
|
|
676
|
+
),
|
|
677
|
+
])
|
|
678
|
+
|
|
679
|
+
await collectArtifacts({
|
|
680
|
+
cwd: tmpDir,
|
|
681
|
+
buildOutputDir: 'build-output',
|
|
682
|
+
})
|
|
683
|
+
|
|
684
|
+
t.is(
|
|
685
|
+
await readFile(join(tmpDir, 'binding.js'), 'utf8'),
|
|
686
|
+
'module.exports = { native: true }\n',
|
|
687
|
+
)
|
|
688
|
+
})
|
|
689
|
+
|
|
690
|
+
test('a directory main keeps its index.js shared with the native root entry', async (t) => {
|
|
691
|
+
const { tmpDir } = t.context
|
|
692
|
+
const binaryName = 'dir-entry'
|
|
693
|
+
const packageName = '@napi-rs/dir-entry'
|
|
694
|
+
const artifactsDir = join(tmpDir, 'artifacts')
|
|
695
|
+
const buildOutputDir = join(tmpDir, 'build-output')
|
|
696
|
+
|
|
697
|
+
// `main: "./dist"` resolves via the directory lookup to `dist/index.js`,
|
|
698
|
+
// so a WASI-declared `dist/index.js` is the native root entry and the
|
|
699
|
+
// WASI-source copy must not claim it.
|
|
700
|
+
await mkdir(artifactsDir, { recursive: true })
|
|
701
|
+
await mkdir(join(buildOutputDir, 'dist'), { recursive: true })
|
|
702
|
+
await mkdir(join(tmpDir, 'dist'), { recursive: true })
|
|
703
|
+
await writeFile(
|
|
704
|
+
join(tmpDir, 'package.json'),
|
|
705
|
+
JSON.stringify({
|
|
706
|
+
name: packageName,
|
|
707
|
+
version: '0.0.0',
|
|
708
|
+
main: './dist',
|
|
709
|
+
napi: {
|
|
710
|
+
binaryName,
|
|
711
|
+
targets: ['x86_64-unknown-linux-gnu', 'wasm32-wasip1-threads'],
|
|
712
|
+
},
|
|
713
|
+
}),
|
|
714
|
+
)
|
|
715
|
+
|
|
716
|
+
const loader = `${WASI_ARTIFACT_METADATA_PREFIX}${JSON.stringify({
|
|
717
|
+
version: 2,
|
|
718
|
+
rootEntry: 'dist/index.js',
|
|
719
|
+
exports: ['create'],
|
|
720
|
+
managedRootEntries: ['browser.js', 'dist/index.js'],
|
|
721
|
+
})}\nmodule.exports = {}\n`
|
|
722
|
+
|
|
723
|
+
await Promise.all([
|
|
724
|
+
writeFile(join(artifactsDir, `${binaryName}.linux-x64-gnu.node`), 'bin'),
|
|
725
|
+
writeFile(join(artifactsDir, `${binaryName}.wasm32-wasi.wasm`), 'wasm'),
|
|
726
|
+
writeFile(join(buildOutputDir, `${binaryName}.wasi.cjs`), loader),
|
|
727
|
+
writeFile(
|
|
728
|
+
join(buildOutputDir, `${binaryName}.wasi.d.cts`),
|
|
729
|
+
'declare const _default: {}\nexport = _default\n',
|
|
730
|
+
),
|
|
731
|
+
writeFile(
|
|
732
|
+
join(buildOutputDir, `${binaryName}.wasi-browser.js`),
|
|
733
|
+
'export {}\n',
|
|
734
|
+
),
|
|
735
|
+
writeFile(join(buildOutputDir, 'wasi-worker.mjs'), 'export {}\n'),
|
|
736
|
+
writeFile(join(buildOutputDir, 'wasi-worker-browser.mjs'), 'export {}\n'),
|
|
737
|
+
writeFile(
|
|
738
|
+
join(buildOutputDir, 'dist', 'index.js'),
|
|
739
|
+
"module.exports = require('./dir-entry.wasi.cjs')\n",
|
|
740
|
+
),
|
|
741
|
+
writeFile(
|
|
742
|
+
join(tmpDir, 'dist', 'index.js'),
|
|
743
|
+
'module.exports = { native: true }\n',
|
|
744
|
+
),
|
|
745
|
+
])
|
|
746
|
+
|
|
747
|
+
await collectArtifacts({
|
|
748
|
+
cwd: tmpDir,
|
|
749
|
+
buildOutputDir: 'build-output',
|
|
750
|
+
})
|
|
751
|
+
|
|
752
|
+
t.is(
|
|
753
|
+
await readFile(join(tmpDir, 'dist', 'index.js'), 'utf8'),
|
|
754
|
+
'module.exports = { native: true }\n',
|
|
755
|
+
)
|
|
756
|
+
})
|
|
757
|
+
|
|
758
|
+
test('a file shadowing a directory main leaves the declared entry distinct', async (t) => {
|
|
759
|
+
const { tmpDir } = t.context
|
|
760
|
+
const binaryName = 'shadow-entry'
|
|
761
|
+
const packageName = '@napi-rs/shadow-entry'
|
|
762
|
+
const artifactsDir = join(tmpDir, 'artifacts')
|
|
763
|
+
const buildOutputDir = join(tmpDir, 'build-output')
|
|
764
|
+
|
|
765
|
+
// `main: "./dist"` resolves to `dist.js` because the file exists, so the
|
|
766
|
+
// directory lookup never runs and `dist/index.js` is not the package
|
|
767
|
+
// entry. The WASI-declared `dist/index.js` is a distinct generated file
|
|
768
|
+
// and resolves from the build output, replacing the stale root copy.
|
|
769
|
+
await mkdir(artifactsDir, { recursive: true })
|
|
770
|
+
await mkdir(join(buildOutputDir, 'dist'), { recursive: true })
|
|
771
|
+
await mkdir(join(tmpDir, 'dist'), { recursive: true })
|
|
772
|
+
await writeFile(
|
|
773
|
+
join(tmpDir, 'package.json'),
|
|
774
|
+
JSON.stringify({
|
|
775
|
+
name: packageName,
|
|
776
|
+
version: '0.0.0',
|
|
777
|
+
main: './dist',
|
|
778
|
+
napi: {
|
|
779
|
+
binaryName,
|
|
780
|
+
targets: ['x86_64-unknown-linux-gnu', 'wasm32-wasip1-threads'],
|
|
781
|
+
},
|
|
782
|
+
}),
|
|
783
|
+
)
|
|
784
|
+
|
|
785
|
+
const loader = `${WASI_ARTIFACT_METADATA_PREFIX}${JSON.stringify({
|
|
786
|
+
version: 2,
|
|
787
|
+
rootEntry: 'dist/index.js',
|
|
788
|
+
exports: ['create'],
|
|
789
|
+
managedRootEntries: ['browser.js', 'dist/index.js'],
|
|
790
|
+
})}\nmodule.exports = {}\n`
|
|
791
|
+
const freshIndex = "module.exports = require('./shadow-entry.wasi.cjs')\n"
|
|
792
|
+
|
|
793
|
+
await Promise.all([
|
|
794
|
+
writeFile(join(artifactsDir, `${binaryName}.linux-x64-gnu.node`), 'bin'),
|
|
795
|
+
writeFile(join(artifactsDir, `${binaryName}.wasm32-wasi.wasm`), 'wasm'),
|
|
796
|
+
writeFile(join(buildOutputDir, `${binaryName}.wasi.cjs`), loader),
|
|
797
|
+
writeFile(
|
|
798
|
+
join(buildOutputDir, `${binaryName}.wasi.d.cts`),
|
|
799
|
+
'declare const _default: {}\nexport = _default\n',
|
|
800
|
+
),
|
|
801
|
+
writeFile(
|
|
802
|
+
join(buildOutputDir, `${binaryName}.wasi-browser.js`),
|
|
803
|
+
'export {}\n',
|
|
804
|
+
),
|
|
805
|
+
writeFile(join(buildOutputDir, 'wasi-worker.mjs'), 'export {}\n'),
|
|
806
|
+
writeFile(join(buildOutputDir, 'wasi-worker-browser.mjs'), 'export {}\n'),
|
|
807
|
+
writeFile(join(buildOutputDir, 'dist', 'index.js'), freshIndex),
|
|
808
|
+
writeFile(join(tmpDir, 'dist.js'), 'module.exports = { native: true }\n'),
|
|
809
|
+
writeFile(
|
|
810
|
+
join(tmpDir, 'dist', 'index.js'),
|
|
811
|
+
'module.exports = { stale: true }\n',
|
|
812
|
+
),
|
|
813
|
+
])
|
|
814
|
+
|
|
815
|
+
await collectArtifacts({
|
|
816
|
+
cwd: tmpDir,
|
|
817
|
+
buildOutputDir: 'build-output',
|
|
818
|
+
})
|
|
819
|
+
|
|
820
|
+
t.is(await readFile(join(tmpDir, 'dist', 'index.js'), 'utf8'), freshIndex)
|
|
821
|
+
t.is(
|
|
822
|
+
await readFile(join(tmpDir, 'dist.js'), 'utf8'),
|
|
823
|
+
'module.exports = { native: true }\n',
|
|
824
|
+
)
|
|
825
|
+
})
|
|
826
|
+
|
|
827
|
+
test('a self-referential directory main resolves to index.js', async (t) => {
|
|
828
|
+
const { tmpDir } = t.context
|
|
829
|
+
const binaryName = 'self-entry'
|
|
830
|
+
const packageName = '@napi-rs/self-entry'
|
|
831
|
+
const artifactsDir = join(tmpDir, 'artifacts')
|
|
832
|
+
|
|
833
|
+
// `main: "."` names the package directory itself; its own package.json
|
|
834
|
+
// main points back at the same directory, which Node resolves as
|
|
835
|
+
// index.js. Collection must terminate instead of recursing through the
|
|
836
|
+
// same manifest forever.
|
|
837
|
+
await mkdir(artifactsDir, { recursive: true })
|
|
838
|
+
await writeFile(
|
|
839
|
+
join(tmpDir, 'package.json'),
|
|
840
|
+
JSON.stringify({
|
|
841
|
+
name: packageName,
|
|
842
|
+
version: '0.0.0',
|
|
843
|
+
main: '.',
|
|
844
|
+
napi: {
|
|
845
|
+
binaryName,
|
|
846
|
+
targets: ['x86_64-unknown-linux-gnu'],
|
|
847
|
+
},
|
|
848
|
+
}),
|
|
849
|
+
)
|
|
850
|
+
|
|
851
|
+
await Promise.all([
|
|
852
|
+
writeFile(join(artifactsDir, `${binaryName}.linux-x64-gnu.node`), 'bin'),
|
|
853
|
+
writeFile(join(tmpDir, 'index.js'), 'module.exports = { native: true }\n'),
|
|
854
|
+
])
|
|
855
|
+
|
|
856
|
+
await collectArtifacts({ cwd: tmpDir })
|
|
857
|
+
|
|
858
|
+
t.is(
|
|
859
|
+
await readFile(join(tmpDir, 'index.js'), 'utf8'),
|
|
860
|
+
'module.exports = { native: true }\n',
|
|
861
|
+
)
|
|
862
|
+
})
|
|
863
|
+
|
|
864
|
+
test('a main resolved through the native artifacts keeps the native loader', async (t) => {
|
|
865
|
+
const { tmpDir } = t.context
|
|
866
|
+
const binaryName = 'clean-entry'
|
|
867
|
+
const packageName = '@napi-rs/clean-entry'
|
|
868
|
+
const artifactsDir = join(tmpDir, 'artifacts')
|
|
869
|
+
const buildOutputDir = join(tmpDir, 'build-output')
|
|
870
|
+
|
|
871
|
+
// The package root has no pre-existing loader: `main: "./binding"`
|
|
872
|
+
// resolves to `binding.js`, which ships inside the native artifacts. The
|
|
873
|
+
// WASI metadata declares the same name, but the resolved entry is the
|
|
874
|
+
// native loader — publishing the WASI-source copy would make `require`
|
|
875
|
+
// of the package main load the WASM build.
|
|
876
|
+
await mkdir(artifactsDir, { recursive: true })
|
|
877
|
+
await mkdir(buildOutputDir, { recursive: true })
|
|
878
|
+
await writeFile(
|
|
879
|
+
join(tmpDir, 'package.json'),
|
|
880
|
+
JSON.stringify({
|
|
881
|
+
name: packageName,
|
|
882
|
+
version: '0.0.0',
|
|
883
|
+
main: './binding',
|
|
884
|
+
napi: {
|
|
885
|
+
binaryName,
|
|
886
|
+
targets: ['x86_64-unknown-linux-gnu', 'wasm32-wasip1-threads'],
|
|
887
|
+
},
|
|
888
|
+
}),
|
|
889
|
+
)
|
|
890
|
+
|
|
891
|
+
const loader = `${WASI_ARTIFACT_METADATA_PREFIX}${JSON.stringify({
|
|
892
|
+
version: 2,
|
|
893
|
+
rootEntry: 'binding.js',
|
|
894
|
+
exports: ['create'],
|
|
895
|
+
managedRootEntries: ['browser.js', 'binding.js'],
|
|
896
|
+
})}\nmodule.exports = {}\n`
|
|
897
|
+
const nativeBinding =
|
|
898
|
+
"module.exports = require('./clean-entry.linux-x64-gnu.node')\n"
|
|
899
|
+
|
|
900
|
+
await Promise.all([
|
|
901
|
+
writeFile(join(artifactsDir, `${binaryName}.linux-x64-gnu.node`), 'bin'),
|
|
902
|
+
writeFile(join(artifactsDir, `${binaryName}.wasm32-wasi.wasm`), 'wasm'),
|
|
903
|
+
writeFile(join(artifactsDir, 'binding.js'), nativeBinding),
|
|
904
|
+
writeFile(join(buildOutputDir, `${binaryName}.wasi.cjs`), loader),
|
|
905
|
+
writeFile(
|
|
906
|
+
join(buildOutputDir, `${binaryName}.wasi.d.cts`),
|
|
907
|
+
'declare const _default: {}\nexport = _default\n',
|
|
908
|
+
),
|
|
909
|
+
writeFile(
|
|
910
|
+
join(buildOutputDir, `${binaryName}.wasi-browser.js`),
|
|
911
|
+
'export {}\n',
|
|
912
|
+
),
|
|
913
|
+
writeFile(join(buildOutputDir, 'wasi-worker.mjs'), 'export {}\n'),
|
|
914
|
+
writeFile(join(buildOutputDir, 'wasi-worker-browser.mjs'), 'export {}\n'),
|
|
915
|
+
writeFile(
|
|
916
|
+
join(buildOutputDir, 'binding.js'),
|
|
917
|
+
"module.exports = require('./clean-entry.wasi.cjs')\n",
|
|
918
|
+
),
|
|
919
|
+
])
|
|
920
|
+
|
|
921
|
+
await collectArtifacts({
|
|
922
|
+
cwd: tmpDir,
|
|
923
|
+
buildOutputDir: 'build-output',
|
|
924
|
+
})
|
|
925
|
+
|
|
926
|
+
// The resolved main entry comes from the native artifacts, not the
|
|
927
|
+
// WASI-source copy of the same name.
|
|
928
|
+
t.is(await readFile(join(tmpDir, 'binding.js'), 'utf8'), nativeBinding)
|
|
929
|
+
})
|
|
930
|
+
|
|
931
|
+
test('an existing extensionless main shadows the declared extension variant', async (t) => {
|
|
932
|
+
const { tmpDir } = t.context
|
|
933
|
+
const binaryName = 'exact-entry'
|
|
934
|
+
const packageName = '@napi-rs/exact-entry'
|
|
935
|
+
const artifactsDir = join(tmpDir, 'artifacts')
|
|
936
|
+
const buildOutputDir = join(tmpDir, 'build-output')
|
|
937
|
+
|
|
938
|
+
// `main: "./binding"` resolves to the extensionless file `binding`
|
|
939
|
+
// because it exists, so `binding.js` is unreachable as the package
|
|
940
|
+
// entry. The WASI-declared `binding.js` is a distinct generated file and
|
|
941
|
+
// resolves from the build output, replacing the stale root copy.
|
|
942
|
+
await mkdir(artifactsDir, { recursive: true })
|
|
943
|
+
await mkdir(buildOutputDir, { recursive: true })
|
|
944
|
+
await writeFile(
|
|
945
|
+
join(tmpDir, 'package.json'),
|
|
946
|
+
JSON.stringify({
|
|
947
|
+
name: packageName,
|
|
948
|
+
version: '0.0.0',
|
|
949
|
+
main: './binding',
|
|
950
|
+
napi: {
|
|
951
|
+
binaryName,
|
|
952
|
+
targets: ['x86_64-unknown-linux-gnu', 'wasm32-wasip1-threads'],
|
|
953
|
+
},
|
|
954
|
+
}),
|
|
955
|
+
)
|
|
956
|
+
|
|
957
|
+
const loader = `${WASI_ARTIFACT_METADATA_PREFIX}${JSON.stringify({
|
|
958
|
+
version: 2,
|
|
959
|
+
rootEntry: 'binding.js',
|
|
960
|
+
exports: ['create'],
|
|
961
|
+
managedRootEntries: ['browser.js', 'binding.js'],
|
|
962
|
+
})}\nmodule.exports = {}\n`
|
|
963
|
+
const freshBinding = "module.exports = require('./exact-entry.wasi.cjs')\n"
|
|
964
|
+
|
|
965
|
+
await Promise.all([
|
|
966
|
+
writeFile(join(artifactsDir, `${binaryName}.linux-x64-gnu.node`), 'bin'),
|
|
967
|
+
writeFile(join(artifactsDir, `${binaryName}.wasm32-wasi.wasm`), 'wasm'),
|
|
968
|
+
writeFile(join(buildOutputDir, `${binaryName}.wasi.cjs`), loader),
|
|
969
|
+
writeFile(
|
|
970
|
+
join(buildOutputDir, `${binaryName}.wasi.d.cts`),
|
|
971
|
+
'declare const _default: {}\nexport = _default\n',
|
|
972
|
+
),
|
|
973
|
+
writeFile(
|
|
974
|
+
join(buildOutputDir, `${binaryName}.wasi-browser.js`),
|
|
975
|
+
'export {}\n',
|
|
976
|
+
),
|
|
977
|
+
writeFile(join(buildOutputDir, 'wasi-worker.mjs'), 'export {}\n'),
|
|
978
|
+
writeFile(join(buildOutputDir, 'wasi-worker-browser.mjs'), 'export {}\n'),
|
|
979
|
+
writeFile(join(buildOutputDir, 'binding.js'), freshBinding),
|
|
980
|
+
writeFile(join(tmpDir, 'binding'), 'module.exports = { exact: true }\n'),
|
|
981
|
+
writeFile(join(tmpDir, 'binding.js'), 'module.exports = { stale: true }\n'),
|
|
982
|
+
])
|
|
983
|
+
|
|
984
|
+
await collectArtifacts({
|
|
985
|
+
cwd: tmpDir,
|
|
986
|
+
buildOutputDir: 'build-output',
|
|
987
|
+
})
|
|
988
|
+
|
|
989
|
+
t.is(await readFile(join(tmpDir, 'binding.js'), 'utf8'), freshBinding)
|
|
990
|
+
t.is(
|
|
991
|
+
await readFile(join(tmpDir, 'binding'), 'utf8'),
|
|
992
|
+
'module.exports = { exact: true }\n',
|
|
993
|
+
)
|
|
994
|
+
})
|
|
995
|
+
|
|
996
|
+
test('an exports entry target shares its name with the native root entry', async (t) => {
|
|
997
|
+
const { tmpDir } = t.context
|
|
998
|
+
const binaryName = 'exports-entry'
|
|
999
|
+
const packageName = '@napi-rs/exports-entry'
|
|
1000
|
+
const artifactsDir = join(tmpDir, 'artifacts')
|
|
1001
|
+
const buildOutputDir = join(tmpDir, 'build-output')
|
|
1002
|
+
|
|
1003
|
+
// `exports["."]` resolves ahead of `main`, so `entry.js` is the package
|
|
1004
|
+
// entry even though `main` names a wrapper. The WASI metadata declares
|
|
1005
|
+
// the same name; the native artifact copy must win over the WASI-source
|
|
1006
|
+
// loader chain.
|
|
1007
|
+
await mkdir(artifactsDir, { recursive: true })
|
|
1008
|
+
await mkdir(buildOutputDir, { recursive: true })
|
|
1009
|
+
await writeFile(
|
|
1010
|
+
join(tmpDir, 'package.json'),
|
|
1011
|
+
JSON.stringify({
|
|
1012
|
+
name: packageName,
|
|
1013
|
+
version: '0.0.0',
|
|
1014
|
+
main: 'wrapper.js',
|
|
1015
|
+
exports: { '.': './entry.js' },
|
|
1016
|
+
napi: {
|
|
1017
|
+
binaryName,
|
|
1018
|
+
targets: ['x86_64-unknown-linux-gnu', 'wasm32-wasip1-threads'],
|
|
1019
|
+
},
|
|
1020
|
+
}),
|
|
1021
|
+
)
|
|
1022
|
+
|
|
1023
|
+
const loader = `${WASI_ARTIFACT_METADATA_PREFIX}${JSON.stringify({
|
|
1024
|
+
version: 2,
|
|
1025
|
+
rootEntry: 'entry.js',
|
|
1026
|
+
exports: ['create'],
|
|
1027
|
+
managedRootEntries: ['browser.js', 'entry.js'],
|
|
1028
|
+
})}\nmodule.exports = {}\n`
|
|
1029
|
+
const nativeEntry =
|
|
1030
|
+
"module.exports = require('./exports-entry.linux-x64-gnu.node')\n"
|
|
1031
|
+
|
|
1032
|
+
await Promise.all([
|
|
1033
|
+
writeFile(join(artifactsDir, `${binaryName}.linux-x64-gnu.node`), 'bin'),
|
|
1034
|
+
writeFile(join(artifactsDir, `${binaryName}.wasm32-wasi.wasm`), 'wasm'),
|
|
1035
|
+
writeFile(join(artifactsDir, 'entry.js'), nativeEntry),
|
|
1036
|
+
writeFile(join(buildOutputDir, `${binaryName}.wasi.cjs`), loader),
|
|
1037
|
+
writeFile(
|
|
1038
|
+
join(buildOutputDir, `${binaryName}.wasi.d.cts`),
|
|
1039
|
+
'declare const _default: {}\nexport = _default\n',
|
|
1040
|
+
),
|
|
1041
|
+
writeFile(
|
|
1042
|
+
join(buildOutputDir, `${binaryName}.wasi-browser.js`),
|
|
1043
|
+
'export {}\n',
|
|
1044
|
+
),
|
|
1045
|
+
writeFile(join(buildOutputDir, 'wasi-worker.mjs'), 'export {}\n'),
|
|
1046
|
+
writeFile(join(buildOutputDir, 'wasi-worker-browser.mjs'), 'export {}\n'),
|
|
1047
|
+
writeFile(
|
|
1048
|
+
join(buildOutputDir, 'entry.js'),
|
|
1049
|
+
"module.exports = require('./exports-entry.wasi.cjs')\n",
|
|
1050
|
+
),
|
|
1051
|
+
writeFile(join(tmpDir, 'wrapper.js'), 'module.exports = { wrap: true }\n'),
|
|
1052
|
+
])
|
|
1053
|
+
|
|
1054
|
+
await collectArtifacts({
|
|
1055
|
+
cwd: tmpDir,
|
|
1056
|
+
buildOutputDir: 'build-output',
|
|
1057
|
+
})
|
|
1058
|
+
|
|
1059
|
+
t.is(await readFile(join(tmpDir, 'entry.js'), 'utf8'), nativeEntry)
|
|
1060
|
+
})
|
|
1061
|
+
|
|
1062
|
+
test('a missing nested main falls back to the directory index', async (t) => {
|
|
1063
|
+
const { tmpDir } = t.context
|
|
1064
|
+
const binaryName = 'nested-miss'
|
|
1065
|
+
const packageName = '@napi-rs/nested-miss'
|
|
1066
|
+
const artifactsDir = join(tmpDir, 'artifacts')
|
|
1067
|
+
const buildOutputDir = join(tmpDir, 'build-output')
|
|
1068
|
+
|
|
1069
|
+
// `main: "./dist"` finds `dist/package.json` whose `main` points at a
|
|
1070
|
+
// file that does not exist. Node then falls back to `dist/index.js`, so
|
|
1071
|
+
// the WASI-declared `dist/index.js` shares the native root entry and the
|
|
1072
|
+
// WASI-source copy must not overwrite the existing native file.
|
|
1073
|
+
await mkdir(artifactsDir, { recursive: true })
|
|
1074
|
+
await mkdir(join(buildOutputDir, 'dist'), { recursive: true })
|
|
1075
|
+
await mkdir(join(tmpDir, 'dist'), { recursive: true })
|
|
1076
|
+
await writeFile(
|
|
1077
|
+
join(tmpDir, 'package.json'),
|
|
1078
|
+
JSON.stringify({
|
|
1079
|
+
name: packageName,
|
|
1080
|
+
version: '0.0.0',
|
|
1081
|
+
main: './dist',
|
|
1082
|
+
napi: {
|
|
1083
|
+
binaryName,
|
|
1084
|
+
targets: ['x86_64-unknown-linux-gnu', 'wasm32-wasip1-threads'],
|
|
1085
|
+
},
|
|
1086
|
+
}),
|
|
1087
|
+
)
|
|
1088
|
+
await writeFile(
|
|
1089
|
+
join(tmpDir, 'dist', 'package.json'),
|
|
1090
|
+
JSON.stringify({ main: './lib.js' }),
|
|
1091
|
+
)
|
|
1092
|
+
|
|
1093
|
+
const loader = `${WASI_ARTIFACT_METADATA_PREFIX}${JSON.stringify({
|
|
1094
|
+
version: 2,
|
|
1095
|
+
rootEntry: 'dist/index.js',
|
|
1096
|
+
exports: ['create'],
|
|
1097
|
+
managedRootEntries: ['browser.js', 'dist/index.js'],
|
|
1098
|
+
})}\nmodule.exports = {}\n`
|
|
1099
|
+
|
|
1100
|
+
await Promise.all([
|
|
1101
|
+
writeFile(join(artifactsDir, `${binaryName}.linux-x64-gnu.node`), 'bin'),
|
|
1102
|
+
writeFile(join(artifactsDir, `${binaryName}.wasm32-wasi.wasm`), 'wasm'),
|
|
1103
|
+
writeFile(join(buildOutputDir, `${binaryName}.wasi.cjs`), loader),
|
|
1104
|
+
writeFile(
|
|
1105
|
+
join(buildOutputDir, `${binaryName}.wasi.d.cts`),
|
|
1106
|
+
'declare const _default: {}\nexport = _default\n',
|
|
1107
|
+
),
|
|
1108
|
+
writeFile(
|
|
1109
|
+
join(buildOutputDir, `${binaryName}.wasi-browser.js`),
|
|
1110
|
+
'export {}\n',
|
|
1111
|
+
),
|
|
1112
|
+
writeFile(join(buildOutputDir, 'wasi-worker.mjs'), 'export {}\n'),
|
|
1113
|
+
writeFile(join(buildOutputDir, 'wasi-worker-browser.mjs'), 'export {}\n'),
|
|
1114
|
+
writeFile(
|
|
1115
|
+
join(buildOutputDir, 'dist', 'index.js'),
|
|
1116
|
+
"module.exports = require('./nested-miss.wasi.cjs')\n",
|
|
1117
|
+
),
|
|
1118
|
+
writeFile(
|
|
1119
|
+
join(tmpDir, 'dist', 'index.js'),
|
|
1120
|
+
'module.exports = { native: true }\n',
|
|
1121
|
+
),
|
|
1122
|
+
])
|
|
1123
|
+
|
|
1124
|
+
await collectArtifacts({
|
|
1125
|
+
cwd: tmpDir,
|
|
1126
|
+
buildOutputDir: 'build-output',
|
|
1127
|
+
})
|
|
1128
|
+
|
|
1129
|
+
t.is(
|
|
1130
|
+
await readFile(join(tmpDir, 'dist', 'index.js'), 'utf8'),
|
|
1131
|
+
'module.exports = { native: true }\n',
|
|
1132
|
+
)
|
|
1133
|
+
})
|
|
1134
|
+
|
|
1135
|
+
test('a condition-only exports map shares the declared root entry', async (t) => {
|
|
1136
|
+
const { tmpDir } = t.context
|
|
1137
|
+
const binaryName = 'cond-entry'
|
|
1138
|
+
const packageName = '@napi-rs/cond-entry'
|
|
1139
|
+
const artifactsDir = join(tmpDir, 'artifacts')
|
|
1140
|
+
const buildOutputDir = join(tmpDir, 'build-output')
|
|
1141
|
+
|
|
1142
|
+
// A condition-only exports map has no '.' key but still declares the
|
|
1143
|
+
// package entry. The WASI-declared `binding.js` shares the native root
|
|
1144
|
+
// entry, so the WASI-source copy never claims it.
|
|
1145
|
+
await mkdir(artifactsDir, { recursive: true })
|
|
1146
|
+
await mkdir(buildOutputDir, { recursive: true })
|
|
1147
|
+
await writeFile(
|
|
1148
|
+
join(tmpDir, 'package.json'),
|
|
1149
|
+
JSON.stringify({
|
|
1150
|
+
name: packageName,
|
|
1151
|
+
version: '0.0.0',
|
|
1152
|
+
exports: { node: './binding.js', default: './binding.js' },
|
|
1153
|
+
napi: {
|
|
1154
|
+
binaryName,
|
|
1155
|
+
targets: ['x86_64-unknown-linux-gnu', 'wasm32-wasip1-threads'],
|
|
1156
|
+
},
|
|
1157
|
+
}),
|
|
1158
|
+
)
|
|
1159
|
+
|
|
1160
|
+
const loader = `${WASI_ARTIFACT_METADATA_PREFIX}${JSON.stringify({
|
|
1161
|
+
version: 2,
|
|
1162
|
+
rootEntry: 'binding.js',
|
|
1163
|
+
exports: ['create'],
|
|
1164
|
+
managedRootEntries: ['browser.js', 'binding.js'],
|
|
1165
|
+
})}\nmodule.exports = {}\n`
|
|
1166
|
+
const nativeBinding =
|
|
1167
|
+
"module.exports = require('./cond-entry.linux-x64-gnu.node')\n"
|
|
1168
|
+
|
|
1169
|
+
await Promise.all([
|
|
1170
|
+
writeFile(join(artifactsDir, `${binaryName}.linux-x64-gnu.node`), 'bin'),
|
|
1171
|
+
writeFile(join(artifactsDir, `${binaryName}.wasm32-wasi.wasm`), 'wasm'),
|
|
1172
|
+
writeFile(join(artifactsDir, 'binding.js'), nativeBinding),
|
|
1173
|
+
writeFile(join(buildOutputDir, `${binaryName}.wasi.cjs`), loader),
|
|
1174
|
+
writeFile(
|
|
1175
|
+
join(buildOutputDir, `${binaryName}.wasi.d.cts`),
|
|
1176
|
+
'declare const _default: {}\nexport = _default\n',
|
|
1177
|
+
),
|
|
1178
|
+
writeFile(
|
|
1179
|
+
join(buildOutputDir, `${binaryName}.wasi-browser.js`),
|
|
1180
|
+
'export {}\n',
|
|
1181
|
+
),
|
|
1182
|
+
writeFile(join(buildOutputDir, 'wasi-worker.mjs'), 'export {}\n'),
|
|
1183
|
+
writeFile(join(buildOutputDir, 'wasi-worker-browser.mjs'), 'export {}\n'),
|
|
1184
|
+
writeFile(
|
|
1185
|
+
join(buildOutputDir, 'binding.js'),
|
|
1186
|
+
"module.exports = require('./cond-entry.wasi.cjs')\n",
|
|
1187
|
+
),
|
|
1188
|
+
])
|
|
1189
|
+
|
|
1190
|
+
await collectArtifacts({
|
|
1191
|
+
cwd: tmpDir,
|
|
1192
|
+
buildOutputDir: 'build-output',
|
|
1193
|
+
})
|
|
1194
|
+
|
|
1195
|
+
t.is(await readFile(join(tmpDir, 'binding.js'), 'utf8'), nativeBinding)
|
|
1196
|
+
})
|
|
1197
|
+
|
|
1198
|
+
test('auxiliary export targets do not consume the native root slot', async (t) => {
|
|
1199
|
+
const { tmpDir } = t.context
|
|
1200
|
+
const binaryName = 'typed-entry'
|
|
1201
|
+
const packageName = '@napi-rs/typed-entry'
|
|
1202
|
+
const artifactsDir = join(tmpDir, 'artifacts')
|
|
1203
|
+
const buildOutputDir = join(tmpDir, 'build-output')
|
|
1204
|
+
|
|
1205
|
+
// `types` lists first in the conditional map, but a declaration file is
|
|
1206
|
+
// not a runtime entry. The native scan must still copy `binding.js` from
|
|
1207
|
+
// the artifacts rather than stopping at the `.d.ts`.
|
|
1208
|
+
await mkdir(artifactsDir, { recursive: true })
|
|
1209
|
+
await mkdir(buildOutputDir, { recursive: true })
|
|
1210
|
+
await writeFile(
|
|
1211
|
+
join(tmpDir, 'package.json'),
|
|
1212
|
+
JSON.stringify({
|
|
1213
|
+
name: packageName,
|
|
1214
|
+
version: '0.0.0',
|
|
1215
|
+
main: './binding.js',
|
|
1216
|
+
exports: {
|
|
1217
|
+
'.': {
|
|
1218
|
+
types: './binding.d.ts',
|
|
1219
|
+
default: './binding.js',
|
|
1220
|
+
},
|
|
1221
|
+
},
|
|
1222
|
+
napi: {
|
|
1223
|
+
binaryName,
|
|
1224
|
+
targets: ['x86_64-unknown-linux-gnu', 'wasm32-wasip1-threads'],
|
|
1225
|
+
},
|
|
1226
|
+
}),
|
|
1227
|
+
)
|
|
1228
|
+
|
|
1229
|
+
const loader = `${WASI_ARTIFACT_METADATA_PREFIX}${JSON.stringify({
|
|
1230
|
+
version: 2,
|
|
1231
|
+
rootEntry: 'binding.js',
|
|
1232
|
+
exports: ['create'],
|
|
1233
|
+
managedRootEntries: ['browser.js', 'binding.js'],
|
|
1234
|
+
})}\nmodule.exports = {}\n`
|
|
1235
|
+
const nativeBinding =
|
|
1236
|
+
"module.exports = require('./typed-entry.linux-x64-gnu.node')\n"
|
|
1237
|
+
|
|
1238
|
+
await Promise.all([
|
|
1239
|
+
writeFile(join(artifactsDir, `${binaryName}.linux-x64-gnu.node`), 'bin'),
|
|
1240
|
+
writeFile(join(artifactsDir, `${binaryName}.wasm32-wasi.wasm`), 'wasm'),
|
|
1241
|
+
writeFile(join(artifactsDir, 'binding.js'), nativeBinding),
|
|
1242
|
+
writeFile(join(artifactsDir, 'binding.d.ts'), 'declare const x: 1\n'),
|
|
1243
|
+
writeFile(join(buildOutputDir, `${binaryName}.wasi.cjs`), loader),
|
|
1244
|
+
writeFile(
|
|
1245
|
+
join(buildOutputDir, `${binaryName}.wasi.d.cts`),
|
|
1246
|
+
'declare const _default: {}\nexport = _default\n',
|
|
1247
|
+
),
|
|
1248
|
+
writeFile(
|
|
1249
|
+
join(buildOutputDir, `${binaryName}.wasi-browser.js`),
|
|
1250
|
+
'export {}\n',
|
|
1251
|
+
),
|
|
1252
|
+
writeFile(join(buildOutputDir, 'wasi-worker.mjs'), 'export {}\n'),
|
|
1253
|
+
writeFile(join(buildOutputDir, 'wasi-worker-browser.mjs'), 'export {}\n'),
|
|
1254
|
+
writeFile(
|
|
1255
|
+
join(buildOutputDir, 'binding.js'),
|
|
1256
|
+
"module.exports = require('./typed-entry.wasi.cjs')\n",
|
|
1257
|
+
),
|
|
1258
|
+
])
|
|
1259
|
+
|
|
1260
|
+
await collectArtifacts({
|
|
1261
|
+
cwd: tmpDir,
|
|
1262
|
+
buildOutputDir: 'build-output',
|
|
1263
|
+
})
|
|
1264
|
+
|
|
1265
|
+
t.is(await readFile(join(tmpDir, 'binding.js'), 'utf8'), nativeBinding)
|
|
1266
|
+
})
|
|
1267
|
+
|
|
1268
|
+
test('a stale managed entry cannot escape the package root through a symlink', async (t) => {
|
|
1269
|
+
const { tmpDir } = t.context
|
|
1270
|
+
const binaryName = 'escape-entry'
|
|
1271
|
+
const packageName = '@napi-rs/escape-entry'
|
|
1272
|
+
const packageDir = join(tmpDir, 'pkg')
|
|
1273
|
+
const siblingDir = join(tmpDir, 'sibling')
|
|
1274
|
+
const artifactsDir = join(tmpDir, 'artifacts')
|
|
1275
|
+
const buildOutputDir = join(packageDir, 'build-output')
|
|
1276
|
+
|
|
1277
|
+
// The old loader at the package root recorded `linked/old.js` as a
|
|
1278
|
+
// managed root entry. `linked` is now a symlink into a sibling package,
|
|
1279
|
+
// so deleting the stale entry must not follow the link.
|
|
1280
|
+
await mkdir(artifactsDir, { recursive: true })
|
|
1281
|
+
await mkdir(buildOutputDir, { recursive: true })
|
|
1282
|
+
await mkdir(siblingDir, { recursive: true })
|
|
1283
|
+
await mkdir(join(packageDir), { recursive: true })
|
|
1284
|
+
await symlink(siblingDir, join(packageDir, 'linked'))
|
|
1285
|
+
await writeFile(join(siblingDir, 'old.js'), 'module.exports = {}\n')
|
|
1286
|
+
await writeFile(
|
|
1287
|
+
join(packageDir, 'package.json'),
|
|
1288
|
+
JSON.stringify({
|
|
1289
|
+
name: packageName,
|
|
1290
|
+
version: '0.0.0',
|
|
1291
|
+
main: 'index.js',
|
|
1292
|
+
napi: {
|
|
1293
|
+
binaryName,
|
|
1294
|
+
targets: ['x86_64-unknown-linux-gnu', 'wasm32-wasip1-threads'],
|
|
1295
|
+
},
|
|
1296
|
+
}),
|
|
1297
|
+
)
|
|
1298
|
+
|
|
1299
|
+
const oldLoader = `${WASI_ARTIFACT_METADATA_PREFIX}${JSON.stringify({
|
|
1300
|
+
version: 2,
|
|
1301
|
+
rootEntry: 'linked/old.js',
|
|
1302
|
+
managedRootEntries: ['browser.js', 'linked/old.js'],
|
|
1303
|
+
})}\nmodule.exports = {}\n`
|
|
1304
|
+
const newLoader = `${WASI_ARTIFACT_METADATA_PREFIX}${JSON.stringify({
|
|
1305
|
+
version: 2,
|
|
1306
|
+
rootEntry: 'binding.js',
|
|
1307
|
+
exports: ['create'],
|
|
1308
|
+
managedRootEntries: ['browser.js', 'binding.js'],
|
|
1309
|
+
})}\nmodule.exports = {}\n`
|
|
1310
|
+
|
|
1311
|
+
await Promise.all([
|
|
1312
|
+
writeFile(join(packageDir, `${binaryName}.wasi.cjs`), oldLoader),
|
|
1313
|
+
writeFile(join(artifactsDir, `${binaryName}.linux-x64-gnu.node`), 'bin'),
|
|
1314
|
+
writeFile(join(artifactsDir, `${binaryName}.wasm32-wasi.wasm`), 'wasm'),
|
|
1315
|
+
writeFile(join(buildOutputDir, `${binaryName}.wasi.cjs`), newLoader),
|
|
1316
|
+
writeFile(
|
|
1317
|
+
join(buildOutputDir, `${binaryName}.wasi.d.cts`),
|
|
1318
|
+
'declare const _default: {}\nexport = _default\n',
|
|
1319
|
+
),
|
|
1320
|
+
writeFile(
|
|
1321
|
+
join(buildOutputDir, `${binaryName}.wasi-browser.js`),
|
|
1322
|
+
'export {}\n',
|
|
1323
|
+
),
|
|
1324
|
+
writeFile(join(buildOutputDir, 'wasi-worker.mjs'), 'export {}\n'),
|
|
1325
|
+
writeFile(join(buildOutputDir, 'wasi-worker-browser.mjs'), 'export {}\n'),
|
|
1326
|
+
writeFile(join(buildOutputDir, 'binding.js'), 'module.exports = {}\n'),
|
|
1327
|
+
writeFile(
|
|
1328
|
+
join(packageDir, 'index.js'),
|
|
1329
|
+
'module.exports = { native: true }\n',
|
|
1330
|
+
),
|
|
1331
|
+
])
|
|
1332
|
+
|
|
1333
|
+
await collectArtifacts({
|
|
1334
|
+
cwd: packageDir,
|
|
1335
|
+
outputDir: artifactsDir,
|
|
1336
|
+
buildOutputDir: 'build-output',
|
|
1337
|
+
})
|
|
1338
|
+
|
|
1339
|
+
// The sibling file survives: the stale managed entry resolves outside
|
|
1340
|
+
// the package root through the symlink and is skipped.
|
|
1341
|
+
t.is(
|
|
1342
|
+
await readFile(join(siblingDir, 'old.js'), 'utf8'),
|
|
1343
|
+
'module.exports = {}\n',
|
|
1344
|
+
)
|
|
1345
|
+
})
|
|
1346
|
+
|
|
1347
|
+
test('an extensionless declared main entry is copied from native artifacts', async (t) => {
|
|
1348
|
+
const { tmpDir } = t.context
|
|
1349
|
+
const binaryName = 'bare-entry'
|
|
1350
|
+
const packageName = '@napi-rs/bare-entry'
|
|
1351
|
+
const artifactsDir = join(tmpDir, 'artifacts')
|
|
1352
|
+
const buildOutputDir = join(tmpDir, 'build-output')
|
|
1353
|
+
|
|
1354
|
+
// `main: "./binding"` resolves to the extensionless file `binding`, which
|
|
1355
|
+
// ships in the native artifacts. The WASI metadata declares the same
|
|
1356
|
+
// name, so the entry is shared and the native copy must reach the
|
|
1357
|
+
// package root — filtering it out of the scan because it lacks an
|
|
1358
|
+
// extension would fail collection instead.
|
|
1359
|
+
await mkdir(artifactsDir, { recursive: true })
|
|
1360
|
+
await mkdir(buildOutputDir, { recursive: true })
|
|
1361
|
+
await writeFile(
|
|
1362
|
+
join(tmpDir, 'package.json'),
|
|
1363
|
+
JSON.stringify({
|
|
1364
|
+
name: packageName,
|
|
1365
|
+
version: '0.0.0',
|
|
1366
|
+
main: './binding',
|
|
1367
|
+
napi: {
|
|
1368
|
+
binaryName,
|
|
1369
|
+
targets: ['x86_64-unknown-linux-gnu', 'wasm32-wasip1-threads'],
|
|
1370
|
+
},
|
|
1371
|
+
}),
|
|
1372
|
+
)
|
|
1373
|
+
|
|
1374
|
+
const loader = `${WASI_ARTIFACT_METADATA_PREFIX}${JSON.stringify({
|
|
1375
|
+
version: 2,
|
|
1376
|
+
rootEntry: 'binding',
|
|
1377
|
+
exports: ['create'],
|
|
1378
|
+
managedRootEntries: ['browser.js', 'binding'],
|
|
1379
|
+
})}\nmodule.exports = {}\n`
|
|
1380
|
+
const nativeBinding =
|
|
1381
|
+
"module.exports = require('./bare-entry.linux-x64-gnu.node')\n"
|
|
1382
|
+
|
|
1383
|
+
await Promise.all([
|
|
1384
|
+
writeFile(join(artifactsDir, `${binaryName}.linux-x64-gnu.node`), 'bin'),
|
|
1385
|
+
writeFile(join(artifactsDir, `${binaryName}.wasm32-wasi.wasm`), 'wasm'),
|
|
1386
|
+
writeFile(join(artifactsDir, 'binding'), nativeBinding),
|
|
1387
|
+
writeFile(join(buildOutputDir, `${binaryName}.wasi.cjs`), loader),
|
|
1388
|
+
writeFile(
|
|
1389
|
+
join(buildOutputDir, `${binaryName}.wasi.d.cts`),
|
|
1390
|
+
'declare const _default: {}\nexport = _default\n',
|
|
1391
|
+
),
|
|
1392
|
+
writeFile(
|
|
1393
|
+
join(buildOutputDir, `${binaryName}.wasi-browser.js`),
|
|
1394
|
+
'export {}\n',
|
|
1395
|
+
),
|
|
1396
|
+
writeFile(join(buildOutputDir, 'wasi-worker.mjs'), 'export {}\n'),
|
|
1397
|
+
writeFile(join(buildOutputDir, 'wasi-worker-browser.mjs'), 'export {}\n'),
|
|
1398
|
+
writeFile(
|
|
1399
|
+
join(buildOutputDir, 'binding'),
|
|
1400
|
+
"module.exports = require('./bare-entry.wasi.cjs')\n",
|
|
1401
|
+
),
|
|
1402
|
+
])
|
|
1403
|
+
|
|
1404
|
+
await collectArtifacts({
|
|
1405
|
+
cwd: tmpDir,
|
|
1406
|
+
buildOutputDir: 'build-output',
|
|
1407
|
+
})
|
|
1408
|
+
|
|
1409
|
+
t.is(await readFile(join(tmpDir, 'binding'), 'utf8'), nativeBinding)
|
|
1410
|
+
})
|
|
1411
|
+
|
|
1412
|
+
test('a dying managed entry does not shadow a declared main resolution', async (t) => {
|
|
1413
|
+
const { tmpDir } = t.context
|
|
1414
|
+
const binaryName = 'moved-entry'
|
|
1415
|
+
const packageName = '@napi-rs/moved-entry'
|
|
1416
|
+
const artifactsDir = join(tmpDir, 'artifacts')
|
|
1417
|
+
const buildOutputDir = join(tmpDir, 'build-output')
|
|
1418
|
+
|
|
1419
|
+
// The previous loader managed `dist.js` at the package root; the new
|
|
1420
|
+
// contract declares `dist/index.js`. The transaction deletes `dist.js`,
|
|
1421
|
+
// so `main: "./dist"` resolves through the directory form and
|
|
1422
|
+
// `dist/index.js` is shared — the WASI-source copy must not overwrite
|
|
1423
|
+
// the native loader that wins after `dist.js` is gone.
|
|
1424
|
+
await mkdir(artifactsDir, { recursive: true })
|
|
1425
|
+
await mkdir(join(buildOutputDir, 'dist'), { recursive: true })
|
|
1426
|
+
await mkdir(join(tmpDir, 'dist'), { recursive: true })
|
|
1427
|
+
await writeFile(
|
|
1428
|
+
join(tmpDir, 'package.json'),
|
|
1429
|
+
JSON.stringify({
|
|
1430
|
+
name: packageName,
|
|
1431
|
+
version: '0.0.0',
|
|
1432
|
+
main: './dist',
|
|
1433
|
+
napi: {
|
|
1434
|
+
binaryName,
|
|
1435
|
+
targets: ['x86_64-unknown-linux-gnu', 'wasm32-wasip1-threads'],
|
|
1436
|
+
},
|
|
1437
|
+
}),
|
|
1438
|
+
)
|
|
1439
|
+
|
|
1440
|
+
const oldLoader = `${WASI_ARTIFACT_METADATA_PREFIX}${JSON.stringify({
|
|
1441
|
+
version: 2,
|
|
1442
|
+
rootEntry: 'dist.js',
|
|
1443
|
+
managedRootEntries: ['browser.js', 'dist.js'],
|
|
1444
|
+
})}\nmodule.exports = {}\n`
|
|
1445
|
+
const newLoader = `${WASI_ARTIFACT_METADATA_PREFIX}${JSON.stringify({
|
|
1446
|
+
version: 2,
|
|
1447
|
+
rootEntry: 'dist/index.js',
|
|
1448
|
+
exports: ['create'],
|
|
1449
|
+
managedRootEntries: ['browser.js', 'dist/index.js'],
|
|
1450
|
+
})}\nmodule.exports = {}\n`
|
|
1451
|
+
const nativeIndex = 'module.exports = { native: true }\n'
|
|
1452
|
+
|
|
1453
|
+
await Promise.all([
|
|
1454
|
+
writeFile(join(tmpDir, `${binaryName}.wasi.cjs`), oldLoader),
|
|
1455
|
+
writeFile(join(tmpDir, 'dist.js'), 'module.exports = { old: true }\n'),
|
|
1456
|
+
writeFile(join(artifactsDir, `${binaryName}.linux-x64-gnu.node`), 'bin'),
|
|
1457
|
+
writeFile(join(artifactsDir, `${binaryName}.wasm32-wasi.wasm`), 'wasm'),
|
|
1458
|
+
writeFile(join(buildOutputDir, `${binaryName}.wasi.cjs`), newLoader),
|
|
1459
|
+
writeFile(
|
|
1460
|
+
join(buildOutputDir, `${binaryName}.wasi.d.cts`),
|
|
1461
|
+
'declare const _default: {}\nexport = _default\n',
|
|
1462
|
+
),
|
|
1463
|
+
writeFile(
|
|
1464
|
+
join(buildOutputDir, `${binaryName}.wasi-browser.js`),
|
|
1465
|
+
'export {}\n',
|
|
1466
|
+
),
|
|
1467
|
+
writeFile(join(buildOutputDir, 'wasi-worker.mjs'), 'export {}\n'),
|
|
1468
|
+
writeFile(join(buildOutputDir, 'wasi-worker-browser.mjs'), 'export {}\n'),
|
|
1469
|
+
writeFile(
|
|
1470
|
+
join(buildOutputDir, 'dist', 'index.js'),
|
|
1471
|
+
"module.exports = require('./moved-entry.wasi.cjs')\n",
|
|
1472
|
+
),
|
|
1473
|
+
writeFile(join(tmpDir, 'dist', 'index.js'), nativeIndex),
|
|
1474
|
+
])
|
|
1475
|
+
|
|
1476
|
+
await collectArtifacts({
|
|
1477
|
+
cwd: tmpDir,
|
|
1478
|
+
buildOutputDir: 'build-output',
|
|
1479
|
+
})
|
|
1480
|
+
|
|
1481
|
+
// The surviving main resolution is the native `dist/index.js`; the stale
|
|
1482
|
+
// managed `dist.js` is removed rather than shadowing it.
|
|
1483
|
+
t.is(await readFile(join(tmpDir, 'dist', 'index.js'), 'utf8'), nativeIndex)
|
|
1484
|
+
t.false(existsSync(join(tmpDir, 'dist.js')))
|
|
1485
|
+
})
|
|
1486
|
+
|
|
1487
|
+
test('an unresolvable main falls back to the package-root index.js', async (t) => {
|
|
1488
|
+
const { tmpDir } = t.context
|
|
1489
|
+
const binaryName = 'fallback-entry'
|
|
1490
|
+
const packageName = '@napi-rs/fallback-entry'
|
|
1491
|
+
const artifactsDir = join(tmpDir, 'artifacts')
|
|
1492
|
+
const buildOutputDir = join(tmpDir, 'build-output')
|
|
1493
|
+
|
|
1494
|
+
// `main` points at a file that exists nowhere, so Node falls back to the
|
|
1495
|
+
// package-root `index.js`. The WASI-declared `index.js` shares that
|
|
1496
|
+
// entry: the native root file survives and the WASI-source copy must
|
|
1497
|
+
// not replace it.
|
|
1498
|
+
await mkdir(artifactsDir, { recursive: true })
|
|
1499
|
+
await mkdir(buildOutputDir, { recursive: true })
|
|
1500
|
+
await writeFile(
|
|
1501
|
+
join(tmpDir, 'package.json'),
|
|
1502
|
+
JSON.stringify({
|
|
1503
|
+
name: packageName,
|
|
1504
|
+
version: '0.0.0',
|
|
1505
|
+
main: './gone.js',
|
|
1506
|
+
napi: {
|
|
1507
|
+
binaryName,
|
|
1508
|
+
targets: ['x86_64-unknown-linux-gnu', 'wasm32-wasip1-threads'],
|
|
1509
|
+
},
|
|
1510
|
+
}),
|
|
1511
|
+
)
|
|
1512
|
+
|
|
1513
|
+
const loader = `${WASI_ARTIFACT_METADATA_PREFIX}${JSON.stringify({
|
|
1514
|
+
version: 2,
|
|
1515
|
+
rootEntry: 'index.js',
|
|
1516
|
+
exports: ['create'],
|
|
1517
|
+
managedRootEntries: ['browser.js', 'index.js'],
|
|
1518
|
+
})}\nmodule.exports = {}\n`
|
|
1519
|
+
const nativeIndex =
|
|
1520
|
+
"module.exports = require('./fallback-entry.linux-x64-gnu.node')\n"
|
|
1521
|
+
|
|
1522
|
+
await Promise.all([
|
|
1523
|
+
writeFile(join(artifactsDir, `${binaryName}.linux-x64-gnu.node`), 'bin'),
|
|
1524
|
+
writeFile(join(artifactsDir, `${binaryName}.wasm32-wasi.wasm`), 'wasm'),
|
|
1525
|
+
writeFile(join(buildOutputDir, `${binaryName}.wasi.cjs`), loader),
|
|
1526
|
+
writeFile(
|
|
1527
|
+
join(buildOutputDir, `${binaryName}.wasi.d.cts`),
|
|
1528
|
+
'declare const _default: {}\nexport = _default\n',
|
|
1529
|
+
),
|
|
1530
|
+
writeFile(
|
|
1531
|
+
join(buildOutputDir, `${binaryName}.wasi-browser.js`),
|
|
1532
|
+
'export {}\n',
|
|
1533
|
+
),
|
|
1534
|
+
writeFile(join(buildOutputDir, 'wasi-worker.mjs'), 'export {}\n'),
|
|
1535
|
+
writeFile(join(buildOutputDir, 'wasi-worker-browser.mjs'), 'export {}\n'),
|
|
1536
|
+
writeFile(
|
|
1537
|
+
join(buildOutputDir, 'index.js'),
|
|
1538
|
+
"module.exports = require('./fallback-entry.wasi.cjs')\n",
|
|
1539
|
+
),
|
|
1540
|
+
writeFile(join(tmpDir, 'index.js'), nativeIndex),
|
|
1541
|
+
])
|
|
1542
|
+
|
|
1543
|
+
await collectArtifacts({
|
|
1544
|
+
cwd: tmpDir,
|
|
1545
|
+
buildOutputDir: 'build-output',
|
|
1546
|
+
})
|
|
1547
|
+
|
|
1548
|
+
t.is(await readFile(join(tmpDir, 'index.js'), 'utf8'), nativeIndex)
|
|
1549
|
+
})
|
|
1550
|
+
|
|
1551
|
+
test('a shared root entry absent everywhere falls back to the WASI loader', async (t) => {
|
|
1552
|
+
const { tmpDir } = t.context
|
|
1553
|
+
const binaryName = 'fallback-shared'
|
|
1554
|
+
const packageName = '@napi-rs/fallback-shared'
|
|
1555
|
+
const artifactsDir = join(tmpDir, 'artifacts')
|
|
1556
|
+
const buildOutputDir = join(tmpDir, 'build-output')
|
|
1557
|
+
|
|
1558
|
+
// `main: "index.js"` shares its name with the declared root entry, but
|
|
1559
|
+
// no native loader and no package-root copy of `index.js` exist. The
|
|
1560
|
+
// WASI fallback below publishes the loader-generated copy instead of
|
|
1561
|
+
// failing collection.
|
|
1562
|
+
await mkdir(artifactsDir, { recursive: true })
|
|
1563
|
+
await mkdir(buildOutputDir, { recursive: true })
|
|
1564
|
+
await writeFile(
|
|
1565
|
+
join(tmpDir, 'package.json'),
|
|
1566
|
+
JSON.stringify({
|
|
1567
|
+
name: packageName,
|
|
1568
|
+
version: '0.0.0',
|
|
1569
|
+
main: 'index.js',
|
|
1570
|
+
napi: {
|
|
1571
|
+
binaryName,
|
|
1572
|
+
targets: ['x86_64-unknown-linux-gnu', 'wasm32-wasip1-threads'],
|
|
1573
|
+
},
|
|
1574
|
+
}),
|
|
1575
|
+
)
|
|
1576
|
+
|
|
1577
|
+
const loader = `${WASI_ARTIFACT_METADATA_PREFIX}${JSON.stringify({
|
|
1578
|
+
version: 2,
|
|
1579
|
+
rootEntry: 'index.js',
|
|
1580
|
+
exports: ['create'],
|
|
1581
|
+
managedRootEntries: ['browser.js', 'index.js'],
|
|
1582
|
+
})}\nmodule.exports = {}\n`
|
|
1583
|
+
const wasiIndex = "module.exports = require('./fallback-shared.wasi.cjs')\n"
|
|
1584
|
+
|
|
1585
|
+
await Promise.all([
|
|
1586
|
+
writeFile(join(artifactsDir, `${binaryName}.linux-x64-gnu.node`), 'bin'),
|
|
1587
|
+
writeFile(join(artifactsDir, `${binaryName}.wasm32-wasi.wasm`), 'wasm'),
|
|
1588
|
+
writeFile(join(buildOutputDir, `${binaryName}.wasi.cjs`), loader),
|
|
1589
|
+
writeFile(
|
|
1590
|
+
join(buildOutputDir, `${binaryName}.wasi.d.cts`),
|
|
1591
|
+
'declare const _default: {}\nexport = _default\n',
|
|
1592
|
+
),
|
|
1593
|
+
writeFile(
|
|
1594
|
+
join(buildOutputDir, `${binaryName}.wasi-browser.js`),
|
|
1595
|
+
'export {}\n',
|
|
1596
|
+
),
|
|
1597
|
+
writeFile(join(buildOutputDir, 'wasi-worker.mjs'), 'export {}\n'),
|
|
1598
|
+
writeFile(join(buildOutputDir, 'wasi-worker-browser.mjs'), 'export {}\n'),
|
|
1599
|
+
writeFile(join(buildOutputDir, 'index.js'), wasiIndex),
|
|
1600
|
+
])
|
|
1601
|
+
|
|
1602
|
+
await collectArtifacts({
|
|
1603
|
+
cwd: tmpDir,
|
|
1604
|
+
buildOutputDir: 'build-output',
|
|
1605
|
+
})
|
|
1606
|
+
|
|
1607
|
+
t.is(await readFile(join(tmpDir, 'index.js'), 'utf8'), wasiIndex)
|
|
1608
|
+
})
|
|
1609
|
+
|
|
1610
|
+
test('conflicting WASI root entry declarations are rejected', async (t) => {
|
|
1611
|
+
const { tmpDir } = t.context
|
|
1612
|
+
const binaryName = 'skewed-entry'
|
|
1613
|
+
const packageName = '@napi-rs/skewed-entry'
|
|
1614
|
+
const nativeDir = join(tmpDir, 'artifacts', 'native')
|
|
1615
|
+
const threadlessDir = join(tmpDir, 'artifacts', 'wasip1')
|
|
1616
|
+
const threadsDir = join(tmpDir, 'artifacts', 'threads')
|
|
1617
|
+
|
|
1618
|
+
// The flavors were built with different `--js` names, so their root-entry
|
|
1619
|
+
// contracts cannot be spliced into one coherent package. Splicing would
|
|
1620
|
+
// let a stale same-named file in the preferred source satisfy a contract
|
|
1621
|
+
// it never declared.
|
|
1622
|
+
await Promise.all([
|
|
1623
|
+
mkdir(nativeDir, { recursive: true }),
|
|
1624
|
+
mkdir(threadlessDir, { recursive: true }),
|
|
1625
|
+
mkdir(threadsDir, { recursive: true }),
|
|
1626
|
+
])
|
|
1627
|
+
await writeFile(
|
|
1628
|
+
join(tmpDir, 'package.json'),
|
|
1629
|
+
JSON.stringify({
|
|
1630
|
+
name: packageName,
|
|
1631
|
+
version: '0.0.0',
|
|
1632
|
+
main: 'index.js',
|
|
1633
|
+
napi: {
|
|
1634
|
+
binaryName,
|
|
1635
|
+
targets: [
|
|
1636
|
+
'x86_64-unknown-linux-gnu',
|
|
1637
|
+
'wasm32-wasip1',
|
|
1638
|
+
'wasm32-wasip1-threads',
|
|
1639
|
+
],
|
|
1640
|
+
},
|
|
1641
|
+
}),
|
|
1642
|
+
)
|
|
1643
|
+
|
|
1644
|
+
const loaderFor = (rootEntry: string) =>
|
|
1645
|
+
`${WASI_ARTIFACT_METADATA_PREFIX}${JSON.stringify({
|
|
1646
|
+
version: 2,
|
|
1647
|
+
rootEntry,
|
|
1648
|
+
exports: ['create'],
|
|
1649
|
+
managedRootEntries: ['browser.js', rootEntry],
|
|
1650
|
+
})}\nmodule.exports = {}\n`
|
|
1651
|
+
|
|
1652
|
+
await Promise.all([
|
|
1653
|
+
writeFile(join(nativeDir, `${binaryName}.linux-x64-gnu.node`), 'bin'),
|
|
1654
|
+
writeFile(
|
|
1655
|
+
join(threadsDir, `${binaryName}.wasm32-wasi.wasm`),
|
|
1656
|
+
'threads wasm',
|
|
1657
|
+
),
|
|
1658
|
+
writeFile(
|
|
1659
|
+
join(threadsDir, `${binaryName}.wasi.cjs`),
|
|
1660
|
+
loaderFor('binding.js'),
|
|
1661
|
+
),
|
|
1662
|
+
writeFile(
|
|
1663
|
+
join(threadsDir, `${binaryName}.wasi.d.cts`),
|
|
1664
|
+
'declare const _default: {}\nexport = _default\n',
|
|
1665
|
+
),
|
|
1666
|
+
writeFile(join(threadsDir, `${binaryName}.wasi-browser.js`), 'export {}\n'),
|
|
1667
|
+
writeFile(join(threadsDir, 'wasi-worker.mjs'), 'export {}\n'),
|
|
1668
|
+
writeFile(join(threadsDir, 'wasi-worker-browser.mjs'), 'export {}\n'),
|
|
1669
|
+
writeFile(
|
|
1670
|
+
join(threadlessDir, `${binaryName}.wasm32-wasip1.wasm`),
|
|
1671
|
+
'threadless wasm',
|
|
1672
|
+
),
|
|
1673
|
+
writeFile(
|
|
1674
|
+
join(threadlessDir, `${binaryName}.wasip1.cjs`),
|
|
1675
|
+
loaderFor('index.js'),
|
|
1676
|
+
),
|
|
1677
|
+
writeFile(
|
|
1678
|
+
join(threadlessDir, `${binaryName}.wasip1.d.cts`),
|
|
1679
|
+
'declare const _default: {}\nexport = _default\n',
|
|
1680
|
+
),
|
|
1681
|
+
writeFile(
|
|
1682
|
+
join(threadlessDir, `${binaryName}.wasip1-browser.js`),
|
|
1683
|
+
'export {}\n',
|
|
1684
|
+
),
|
|
1685
|
+
writeFile(
|
|
1686
|
+
join(threadlessDir, `${binaryName}.wasip1-deferred.js`),
|
|
1687
|
+
'export {}\n',
|
|
1688
|
+
),
|
|
1689
|
+
writeFile(
|
|
1690
|
+
join(threadlessDir, `${binaryName}.wasip1-deferred.d.ts`),
|
|
1691
|
+
'export {}\n',
|
|
1692
|
+
),
|
|
1693
|
+
writeFile(join(tmpDir, 'index.js'), 'module.exports = { native: true }\n'),
|
|
1694
|
+
])
|
|
1695
|
+
|
|
1696
|
+
await t.throwsAsync(collectArtifacts({ cwd: tmpDir }), {
|
|
1697
|
+
message: /conflicting root entries/,
|
|
1698
|
+
})
|
|
1699
|
+
})
|
|
1700
|
+
|
|
1701
|
+
test('an explicit build output without the declared root entry is rejected', async (t) => {
|
|
1702
|
+
const { tmpDir } = t.context
|
|
1703
|
+
const binaryName = 'strict-entry'
|
|
1704
|
+
const packageName = '@napi-rs/strict-entry'
|
|
1705
|
+
const artifactsDir = join(tmpDir, 'artifacts')
|
|
1706
|
+
const buildOutputDir = join(tmpDir, 'build-output')
|
|
1707
|
+
|
|
1708
|
+
// --build-output-dir makes the WASI output authoritative: its loader
|
|
1709
|
+
// metadata declares binding.js, the directory does not contain it, and a
|
|
1710
|
+
// stale package-root copy must not paper over the incomplete source.
|
|
1711
|
+
await mkdir(artifactsDir, { recursive: true })
|
|
1712
|
+
await mkdir(buildOutputDir, { recursive: true })
|
|
1713
|
+
await writeFile(
|
|
1714
|
+
join(tmpDir, 'package.json'),
|
|
1715
|
+
JSON.stringify({
|
|
1716
|
+
name: packageName,
|
|
1717
|
+
version: '0.0.0',
|
|
1718
|
+
main: 'index.js',
|
|
1719
|
+
napi: {
|
|
1720
|
+
binaryName,
|
|
1721
|
+
targets: ['x86_64-unknown-linux-gnu', 'wasm32-wasip1-threads'],
|
|
1722
|
+
},
|
|
1723
|
+
}),
|
|
1724
|
+
)
|
|
1725
|
+
|
|
1726
|
+
const loader = `${WASI_ARTIFACT_METADATA_PREFIX}${JSON.stringify({
|
|
1727
|
+
version: 2,
|
|
1728
|
+
rootEntry: 'binding.js',
|
|
1729
|
+
exports: ['create'],
|
|
1730
|
+
managedRootEntries: ['browser.js', 'binding.js'],
|
|
1731
|
+
})}\nmodule.exports = {}\n`
|
|
1732
|
+
|
|
1733
|
+
await Promise.all([
|
|
1734
|
+
writeFile(join(artifactsDir, `${binaryName}.linux-x64-gnu.node`), 'bin'),
|
|
1735
|
+
writeFile(join(artifactsDir, `${binaryName}.wasm32-wasi.wasm`), 'wasm'),
|
|
1736
|
+
writeFile(join(buildOutputDir, `${binaryName}.wasi.cjs`), loader),
|
|
1737
|
+
writeFile(
|
|
1738
|
+
join(buildOutputDir, `${binaryName}.wasi.d.cts`),
|
|
1739
|
+
'declare const _default: {}\nexport = _default\n',
|
|
1740
|
+
),
|
|
1741
|
+
writeFile(
|
|
1742
|
+
join(buildOutputDir, `${binaryName}.wasi-browser.js`),
|
|
1743
|
+
'export {}\n',
|
|
1744
|
+
),
|
|
1745
|
+
writeFile(join(buildOutputDir, 'wasi-worker.mjs'), 'export {}\n'),
|
|
1746
|
+
writeFile(join(buildOutputDir, 'wasi-worker-browser.mjs'), 'export {}\n'),
|
|
1747
|
+
writeFile(join(tmpDir, 'binding.js'), 'module.exports = { stale: true }\n'),
|
|
1748
|
+
writeFile(join(tmpDir, 'index.js'), 'module.exports = {}\n'),
|
|
1749
|
+
])
|
|
1750
|
+
|
|
1751
|
+
await t.throwsAsync(
|
|
1752
|
+
collectArtifacts({ cwd: tmpDir, buildOutputDir: 'build-output' }),
|
|
1753
|
+
{ message: /does not contain it/ },
|
|
1754
|
+
)
|
|
1755
|
+
})
|