@napi-rs/cli
Version:
Cli tools for napi-rs
165 lines (146 loc) • 5.25 kB
text/typescript
import ava, { type ExecutionContext } from 'ava'
import { parseSync } from 'oxc-parser'
import { createCjsBinding, createEsmBinding } from '../templates/js-binding.js'
import { createWasiBrowserBinding } from '../templates/load-wasi-template.js'
import { createWasiBrowserWorkerBinding } from '../templates/wasi-worker-template.js'
const test = ava
// Snapshot tests for full template output
test('createWasiBrowserBinding default', (t) => {
t.snapshot(createWasiBrowserBinding('test-wasi'))
})
test('createWasiBrowserBinding with errorEvent', (t) => {
t.snapshot(
createWasiBrowserBinding(
'test-wasi',
4000,
65536,
false,
false,
false,
true,
),
)
})
test('createWasiBrowserBinding with errorEvent and fs', (t) => {
t.snapshot(
createWasiBrowserBinding(
'test-wasi',
4000,
65536,
true,
false,
false,
true,
),
)
})
test('createWasiBrowserWorkerBinding default', (t) => {
t.snapshot(createWasiBrowserWorkerBinding(false, false))
})
test('createWasiBrowserWorkerBinding with errorEvent', (t) => {
t.snapshot(createWasiBrowserWorkerBinding(false, true))
})
test('createWasiBrowserWorkerBinding with errorEvent and fs', (t) => {
t.snapshot(createWasiBrowserWorkerBinding(true, true))
})
function assertValidJS(t: ExecutionContext, code: string, label: string) {
const { errors } = parseSync('test.mjs', code, { sourceType: 'module' })
t.deepEqual(
errors,
[],
`${label}: generated code should have no syntax errors`,
)
}
const browserBindingCases: Array<{
name: string
args: Parameters<typeof createWasiBrowserBinding>
}> = [
{ name: 'default', args: ['test'] },
{ name: 'fs', args: ['test', 4000, 65536, true] },
{ name: 'asyncInit', args: ['test', 4000, 65536, false, true] },
{ name: 'buffer', args: ['test', 4000, 65536, false, false, true] },
{
name: 'errorEvent',
args: ['test', 4000, 65536, false, false, false, true],
},
{
name: 'fs + errorEvent',
args: ['test', 4000, 65536, true, false, false, true],
},
{ name: 'fs + buffer', args: ['test', 4000, 65536, true, false, true] },
{
name: 'fs + buffer + errorEvent',
args: ['test', 4000, 65536, true, false, true, true],
},
{
name: 'asyncInit + errorEvent',
args: ['test', 4000, 65536, false, true, false, true],
},
{ name: 'all options', args: ['test', 4000, 65536, true, true, true, true] },
]
for (const { name, args } of browserBindingCases) {
test(`createWasiBrowserBinding syntax valid: ${name}`, (t) => {
assertValidJS(t, createWasiBrowserBinding(...args), name)
})
}
const workerBindingCases: Array<{
name: string
args: Parameters<typeof createWasiBrowserWorkerBinding>
}> = [
{ name: 'default', args: [false, false] },
{ name: 'fs', args: [true, false] },
{ name: 'errorEvent', args: [false, true] },
{ name: 'fs + errorEvent', args: [true, true] },
]
for (const { name, args } of workerBindingCases) {
test(`createWasiBrowserWorkerBinding syntax valid: ${name}`, (t) => {
assertValidJS(t, createWasiBrowserWorkerBinding(...args), name)
})
}
// The CJS binding loader ships inside published packages whose `engines`
// can declare support for old Node versions (e.g. `>= 10`). It must therefore
// avoid syntax/APIs newer than what those runtimes support:
// - `require('node:*')` — the `node:` scheme in CommonJS `require()` is only
// available on Node >= 14.18 / 16.
// - optional chaining (`?.`) and nullish coalescing (`??`) — Node >= 14.
// - the `new Error(message, { cause })` options form — Node < 16.9 ignores
// the second argument, dropping the load-error chain; assign
// `error.cause` instead.
const cjsBindingCases: Array<{ name: string; code: string }> = [
{
name: 'default',
code: createCjsBinding('test', '@scope/test', ['sum', 'sub']),
},
{
name: 'with version check',
code: createCjsBinding('test', '@scope/test', ['sum', 'sub'], '1.0.0'),
},
]
// Matches a `node:` builtin scheme in either a `require('node:fs')` or an
// `import ... from 'node:module'` specifier.
const NODE_SCHEME_RE = /['"]node:/
for (const { name, code } of cjsBindingCases) {
test(`createCjsBinding is Node 12 compatible: ${name}`, (t) => {
assertValidJS(t, code, name)
t.false(
NODE_SCHEME_RE.test(code),
'CJS loader must not use the node: scheme (unsupported on Node < 14.18/16 for require())',
)
t.false(code.includes('?.'), 'CJS loader must not use optional chaining')
t.false(code.includes('??'), 'CJS loader must not use nullish coalescing')
t.false(
/\bcause:/.test(code),
'CJS loader must not pass `{ cause }` to the Error constructor (ignored on Node < 16.9); assign `error.cause` instead',
)
})
}
test('createEsmBinding is Node 12 compatible', (t) => {
const code = createEsmBinding('test', '@scope/test', ['sum'])
assertValidJS(t, code, 'esm')
t.false(
NODE_SCHEME_RE.test(code),
'ESM loader must not use the node: scheme (incl. `import ... from "node:module"`)',
)
t.false(code.includes('?.'), 'ESM loader must not use optional chaining')
t.false(code.includes('??'), 'ESM loader must not use nullish coalescing')
})