@napi-rs/cli 3.7.2 → 3.7.4

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.
Files changed (48) hide show
  1. package/dist/cli.js +240 -64
  2. package/dist/index.cjs +239 -63
  3. package/dist/index.d.cts +5 -5
  4. package/dist/index.d.ts +5 -5
  5. package/dist/index.js +240 -64
  6. package/package.json +3 -3
  7. package/src/api/__tests__/__snapshots__/templates.spec.ts.md +353 -0
  8. package/src/api/__tests__/__snapshots__/templates.spec.ts.snap +0 -0
  9. package/src/api/__tests__/android-build.spec.ts +113 -0
  10. package/src/api/__tests__/artifacts.spec.ts +95 -0
  11. package/src/api/__tests__/build-regressions.spec.ts +128 -0
  12. package/src/api/__tests__/build.spec.ts +1020 -0
  13. package/src/api/__tests__/create-npm-dirs.spec.ts +561 -0
  14. package/src/api/__tests__/new-node-api-version.spec.ts +87 -0
  15. package/src/api/__tests__/new.spec.ts +527 -0
  16. package/src/api/__tests__/rename.spec.ts +181 -0
  17. package/src/api/__tests__/templates.spec.ts +164 -0
  18. package/src/api/__tests__/universalize.spec.ts +128 -0
  19. package/src/api/artifacts.ts +7 -16
  20. package/src/api/build.ts +354 -111
  21. package/src/api/new.ts +65 -1
  22. package/src/commands/__tests__/new.spec.ts +13 -0
  23. package/src/def/build.ts +6 -6
  24. package/src/utils/__tests__/__fixtures__/napi_type_def +202 -0
  25. package/src/utils/__tests__/__fixtures__/optional-napi-derive/Cargo.toml +3 -0
  26. package/src/utils/__tests__/__fixtures__/optional-napi-derive/main-crate/Cargo.toml +8 -0
  27. package/src/utils/__tests__/__fixtures__/optional-napi-derive/main-crate/src/lib.rs +1 -0
  28. package/src/utils/__tests__/__fixtures__/optional-napi-derive/napi-derive/Cargo.toml +5 -0
  29. package/src/utils/__tests__/__fixtures__/optional-napi-derive/napi-derive/src/lib.rs +1 -0
  30. package/src/utils/__tests__/__fixtures__/optional-napi-derive/with-optional-derive/Cargo.toml +11 -0
  31. package/src/utils/__tests__/__fixtures__/optional-napi-derive/with-optional-derive/src/lib.rs +1 -0
  32. package/src/utils/__tests__/__fixtures__/runtime_string_enum_flag +2 -0
  33. package/src/utils/__tests__/__snapshots__/config.spec.ts.md +97 -0
  34. package/src/utils/__tests__/__snapshots__/config.spec.ts.snap +0 -0
  35. package/src/utils/__tests__/__snapshots__/target.spec.ts.md +180 -0
  36. package/src/utils/__tests__/__snapshots__/target.spec.ts.snap +0 -0
  37. package/src/utils/__tests__/__snapshots__/typegen.spec.ts.md +1197 -0
  38. package/src/utils/__tests__/__snapshots__/typegen.spec.ts.snap +0 -0
  39. package/src/utils/__tests__/__snapshots__/version.spec.ts.md +22 -0
  40. package/src/utils/__tests__/__snapshots__/version.spec.ts.snap +0 -0
  41. package/src/utils/__tests__/config.spec.ts +81 -0
  42. package/src/utils/__tests__/metadata.spec.ts +69 -0
  43. package/src/utils/__tests__/misc.spec.ts +56 -0
  44. package/src/utils/__tests__/target.spec.ts +19 -0
  45. package/src/utils/__tests__/typegen.spec.ts +85 -0
  46. package/src/utils/__tests__/version.spec.ts +7 -0
  47. package/src/utils/metadata.ts +94 -3
  48. package/src/utils/version.ts +2 -0
@@ -0,0 +1,164 @@
1
+ import ava, { type ExecutionContext } from 'ava'
2
+ import { parseSync } from 'oxc-parser'
3
+
4
+ import { createCjsBinding, createEsmBinding } from '../templates/js-binding.js'
5
+ import { createWasiBrowserBinding } from '../templates/load-wasi-template.js'
6
+ import { createWasiBrowserWorkerBinding } from '../templates/wasi-worker-template.js'
7
+
8
+ const test = ava
9
+
10
+ // Snapshot tests for full template output
11
+
12
+ test('createWasiBrowserBinding default', (t) => {
13
+ t.snapshot(createWasiBrowserBinding('test-wasi'))
14
+ })
15
+
16
+ test('createWasiBrowserBinding with errorEvent', (t) => {
17
+ t.snapshot(
18
+ createWasiBrowserBinding(
19
+ 'test-wasi',
20
+ 4000,
21
+ 65536,
22
+ false,
23
+ false,
24
+ false,
25
+ true,
26
+ ),
27
+ )
28
+ })
29
+
30
+ test('createWasiBrowserBinding with errorEvent and fs', (t) => {
31
+ t.snapshot(
32
+ createWasiBrowserBinding(
33
+ 'test-wasi',
34
+ 4000,
35
+ 65536,
36
+ true,
37
+ false,
38
+ false,
39
+ true,
40
+ ),
41
+ )
42
+ })
43
+
44
+ test('createWasiBrowserWorkerBinding default', (t) => {
45
+ t.snapshot(createWasiBrowserWorkerBinding(false, false))
46
+ })
47
+
48
+ test('createWasiBrowserWorkerBinding with errorEvent', (t) => {
49
+ t.snapshot(createWasiBrowserWorkerBinding(false, true))
50
+ })
51
+
52
+ test('createWasiBrowserWorkerBinding with errorEvent and fs', (t) => {
53
+ t.snapshot(createWasiBrowserWorkerBinding(true, true))
54
+ })
55
+
56
+ function assertValidJS(t: ExecutionContext, code: string, label: string) {
57
+ const { errors } = parseSync('test.mjs', code, { sourceType: 'module' })
58
+ t.deepEqual(
59
+ errors,
60
+ [],
61
+ `${label}: generated code should have no syntax errors`,
62
+ )
63
+ }
64
+
65
+ const browserBindingCases: Array<{
66
+ name: string
67
+ args: Parameters<typeof createWasiBrowserBinding>
68
+ }> = [
69
+ { name: 'default', args: ['test'] },
70
+ { name: 'fs', args: ['test', 4000, 65536, true] },
71
+ { name: 'asyncInit', args: ['test', 4000, 65536, false, true] },
72
+ { name: 'buffer', args: ['test', 4000, 65536, false, false, true] },
73
+ {
74
+ name: 'errorEvent',
75
+ args: ['test', 4000, 65536, false, false, false, true],
76
+ },
77
+ {
78
+ name: 'fs + errorEvent',
79
+ args: ['test', 4000, 65536, true, false, false, true],
80
+ },
81
+ { name: 'fs + buffer', args: ['test', 4000, 65536, true, false, true] },
82
+ {
83
+ name: 'fs + buffer + errorEvent',
84
+ args: ['test', 4000, 65536, true, false, true, true],
85
+ },
86
+ {
87
+ name: 'asyncInit + errorEvent',
88
+ args: ['test', 4000, 65536, false, true, false, true],
89
+ },
90
+ { name: 'all options', args: ['test', 4000, 65536, true, true, true, true] },
91
+ ]
92
+
93
+ for (const { name, args } of browserBindingCases) {
94
+ test(`createWasiBrowserBinding syntax valid: ${name}`, (t) => {
95
+ assertValidJS(t, createWasiBrowserBinding(...args), name)
96
+ })
97
+ }
98
+
99
+ const workerBindingCases: Array<{
100
+ name: string
101
+ args: Parameters<typeof createWasiBrowserWorkerBinding>
102
+ }> = [
103
+ { name: 'default', args: [false, false] },
104
+ { name: 'fs', args: [true, false] },
105
+ { name: 'errorEvent', args: [false, true] },
106
+ { name: 'fs + errorEvent', args: [true, true] },
107
+ ]
108
+
109
+ for (const { name, args } of workerBindingCases) {
110
+ test(`createWasiBrowserWorkerBinding syntax valid: ${name}`, (t) => {
111
+ assertValidJS(t, createWasiBrowserWorkerBinding(...args), name)
112
+ })
113
+ }
114
+
115
+ // The CJS binding loader ships inside published packages whose `engines`
116
+ // can declare support for old Node versions (e.g. `>= 10`). It must therefore
117
+ // avoid syntax/APIs newer than what those runtimes support:
118
+ // - `require('node:*')` — the `node:` scheme in CommonJS `require()` is only
119
+ // available on Node >= 14.18 / 16.
120
+ // - optional chaining (`?.`) and nullish coalescing (`??`) — Node >= 14.
121
+ // - the `new Error(message, { cause })` options form — Node < 16.9 ignores
122
+ // the second argument, dropping the load-error chain; assign
123
+ // `error.cause` instead.
124
+ const cjsBindingCases: Array<{ name: string; code: string }> = [
125
+ {
126
+ name: 'default',
127
+ code: createCjsBinding('test', '@scope/test', ['sum', 'sub']),
128
+ },
129
+ {
130
+ name: 'with version check',
131
+ code: createCjsBinding('test', '@scope/test', ['sum', 'sub'], '1.0.0'),
132
+ },
133
+ ]
134
+
135
+ // Matches a `node:` builtin scheme in either a `require('node:fs')` or an
136
+ // `import ... from 'node:module'` specifier.
137
+ const NODE_SCHEME_RE = /['"]node:/
138
+
139
+ for (const { name, code } of cjsBindingCases) {
140
+ test(`createCjsBinding is Node 12 compatible: ${name}`, (t) => {
141
+ assertValidJS(t, code, name)
142
+ t.false(
143
+ NODE_SCHEME_RE.test(code),
144
+ 'CJS loader must not use the node: scheme (unsupported on Node < 14.18/16 for require())',
145
+ )
146
+ t.false(code.includes('?.'), 'CJS loader must not use optional chaining')
147
+ t.false(code.includes('??'), 'CJS loader must not use nullish coalescing')
148
+ t.false(
149
+ /\bcause:/.test(code),
150
+ 'CJS loader must not pass `{ cause }` to the Error constructor (ignored on Node < 16.9); assign `error.cause` instead',
151
+ )
152
+ })
153
+ }
154
+
155
+ test('createEsmBinding is Node 12 compatible', (t) => {
156
+ const code = createEsmBinding('test', '@scope/test', ['sum'])
157
+ assertValidJS(t, code, 'esm')
158
+ t.false(
159
+ NODE_SCHEME_RE.test(code),
160
+ 'ESM loader must not use the node: scheme (incl. `import ... from "node:module"`)',
161
+ )
162
+ t.false(code.includes('?.'), 'ESM loader must not use optional chaining')
163
+ t.false(code.includes('??'), 'ESM loader must not use nullish coalescing')
164
+ })
@@ -0,0 +1,128 @@
1
+ import { existsSync } from 'node:fs'
2
+ import { mkdir, rm, writeFile } from 'node:fs/promises'
3
+ import { tmpdir } from 'node:os'
4
+ import { join } from 'node:path'
5
+
6
+ import ava, { type TestFn } from 'ava'
7
+
8
+ import { universalizeBinaries } from '../universalize.js'
9
+
10
+ const test = ava as TestFn<{
11
+ tmpDir: string
12
+ cwd: string
13
+ packageJsonPath: string
14
+ }>
15
+
16
+ test.beforeEach(async (t) => {
17
+ const timestamp = Date.now()
18
+ const random = Math.random().toString(36).substring(7)
19
+ const tmpDir = join(
20
+ tmpdir(),
21
+ 'napi-rs-test',
22
+ `universalize-${timestamp}-${random}`,
23
+ )
24
+ const cwd = join(tmpDir, 'cwd')
25
+ const packageJsonDir = join(tmpDir, 'pkg')
26
+ const packageJsonPath = join(packageJsonDir, 'package.json')
27
+
28
+ await mkdir(cwd, { recursive: true })
29
+ await mkdir(packageJsonDir, { recursive: true })
30
+
31
+ t.context = {
32
+ tmpDir,
33
+ cwd,
34
+ packageJsonPath,
35
+ }
36
+ })
37
+
38
+ test.afterEach.always(async (t) => {
39
+ if (existsSync(t.context.tmpDir)) {
40
+ await rm(t.context.tmpDir, { recursive: true, force: true })
41
+ }
42
+ })
43
+
44
+ test('resolves an absolute packageJsonPath instead of joining it with cwd', async (t) => {
45
+ const { cwd, packageJsonPath } = t.context
46
+
47
+ await writeFile(
48
+ packageJsonPath,
49
+ `${JSON.stringify(
50
+ {
51
+ name: 'universalize-repro',
52
+ napi: {
53
+ binaryName: 'universalize-repro',
54
+ targets: ['universal-apple-darwin'],
55
+ },
56
+ },
57
+ null,
58
+ 2,
59
+ )}\n`,
60
+ )
61
+
62
+ const error = await t.throwsAsync(() =>
63
+ universalizeBinaries({
64
+ cwd,
65
+ packageJsonPath,
66
+ }),
67
+ )
68
+
69
+ t.truthy(error)
70
+ t.notRegex(error.message, new RegExp(`${cwd}.+package\\.json`))
71
+
72
+ if (process.platform === 'darwin') {
73
+ t.regex(error.message, /Some binary files were not found:/)
74
+ } else {
75
+ t.is(
76
+ error.message,
77
+ `'universal' arch for platform '${process.platform}' not found in config!`,
78
+ )
79
+ }
80
+ })
81
+
82
+ test('throws when lipo fails to create a universal binary', async (t) => {
83
+ if (process.platform !== 'darwin') {
84
+ t.pass()
85
+ return
86
+ }
87
+
88
+ const { cwd, packageJsonPath, tmpDir } = t.context
89
+ const outputDir = join(tmpDir, 'out')
90
+ const outputPath = join(outputDir, 'universalize-repro.darwin-universal.node')
91
+
92
+ await mkdir(outputDir, { recursive: true })
93
+ await writeFile(
94
+ packageJsonPath,
95
+ `${JSON.stringify(
96
+ {
97
+ name: 'universalize-repro',
98
+ napi: {
99
+ binaryName: 'universalize-repro',
100
+ targets: ['universal-apple-darwin'],
101
+ },
102
+ },
103
+ null,
104
+ 2,
105
+ )}\n`,
106
+ )
107
+
108
+ await writeFile(
109
+ join(outputDir, 'universalize-repro.darwin-x64.node'),
110
+ 'not a mach-o\n',
111
+ )
112
+ await writeFile(
113
+ join(outputDir, 'universalize-repro.darwin-arm64.node'),
114
+ 'not a mach-o\n',
115
+ )
116
+
117
+ const error = await t.throwsAsync(() =>
118
+ universalizeBinaries({
119
+ cwd,
120
+ packageJsonPath,
121
+ outputDir,
122
+ }),
123
+ )
124
+
125
+ t.truthy(error)
126
+ t.regex(error.message, /Failed to create universal binary/)
127
+ t.false(existsSync(outputPath))
128
+ })
@@ -87,27 +87,18 @@ export async function collectArtifacts(userOptions: ArtifactsOptions) {
87
87
 
88
88
  const wasiTarget = targets.find((t) => t.platform === 'wasi')
89
89
  if (wasiTarget) {
90
+ const buildOutputDir = options.buildOutputDir
91
+ ? resolve(options.cwd, options.buildOutputDir)
92
+ : options.cwd
90
93
  const wasiDir = join(
91
94
  options.cwd,
92
95
  options.npmDir,
93
96
  wasiTarget.platformArchABI,
94
97
  )
95
- const cjsFile = join(
96
- options.buildOutputDir ?? options.cwd,
97
- `${binaryName}.wasi.cjs`,
98
- )
99
- const workerFile = join(
100
- options.buildOutputDir ?? options.cwd,
101
- `wasi-worker.mjs`,
102
- )
103
- const browserEntry = join(
104
- options.buildOutputDir ?? options.cwd,
105
- `${binaryName}.wasi-browser.js`,
106
- )
107
- const browserWorkerFile = join(
108
- options.buildOutputDir ?? options.cwd,
109
- `wasi-worker-browser.mjs`,
110
- )
98
+ const cjsFile = join(buildOutputDir, `${binaryName}.wasi.cjs`)
99
+ const workerFile = join(buildOutputDir, `wasi-worker.mjs`)
100
+ const browserEntry = join(buildOutputDir, `${binaryName}.wasi-browser.js`)
101
+ const browserWorkerFile = join(buildOutputDir, `wasi-worker-browser.mjs`)
111
102
  debug.info(
112
103
  `Move wasi binding file [${colors.yellowBright(
113
104
  cjsFile,