@napi-rs/cli 3.0.0-alpha.4 → 3.0.0-alpha.41

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 (177) hide show
  1. package/cli.mjs +0 -0
  2. package/dist/api/artifacts.js +32 -8
  3. package/dist/api/artifacts.js.map +1 -0
  4. package/dist/api/build.d.ts +1 -1
  5. package/dist/api/build.js +231 -30
  6. package/dist/api/build.js.map +1 -0
  7. package/dist/api/create-npm-dirs.js +46 -8
  8. package/dist/api/create-npm-dirs.js.map +1 -0
  9. package/dist/api/new.js +47 -20
  10. package/dist/api/new.js.map +1 -0
  11. package/dist/api/pre-publish.js +10 -7
  12. package/dist/api/pre-publish.js.map +1 -0
  13. package/dist/api/rename.js +10 -1
  14. package/dist/api/rename.js.map +1 -0
  15. package/dist/api/templates/.gitignore.d.ts +1 -1
  16. package/dist/api/templates/.gitignore.js +2 -0
  17. package/dist/api/templates/.gitignore.js.map +1 -0
  18. package/dist/api/templates/.npmignore.js +1 -0
  19. package/dist/api/templates/.npmignore.js.map +1 -0
  20. package/dist/api/templates/build.rs.js +1 -0
  21. package/dist/api/templates/build.rs.js.map +1 -0
  22. package/dist/api/templates/cargo.toml.js +1 -0
  23. package/dist/api/templates/cargo.toml.js.map +1 -0
  24. package/dist/api/templates/ci-template.d.ts +2 -1
  25. package/dist/api/templates/ci-template.js +150 -152
  26. package/dist/api/templates/ci-template.js.map +1 -0
  27. package/dist/api/templates/ci.yml.d.ts +2 -1
  28. package/dist/api/templates/ci.yml.js +19 -4
  29. package/dist/api/templates/ci.yml.js.map +1 -0
  30. package/dist/api/templates/index.js +1 -0
  31. package/dist/api/templates/index.js.map +1 -0
  32. package/dist/api/templates/js-binding.d.ts +1 -1
  33. package/dist/api/templates/js-binding.js +143 -30
  34. package/dist/api/templates/js-binding.js.map +1 -0
  35. package/dist/api/templates/lib.rs.js +2 -4
  36. package/dist/api/templates/lib.rs.js.map +1 -0
  37. package/dist/api/templates/load-wasi-template.d.ts +2 -0
  38. package/dist/api/templates/load-wasi-template.js +143 -0
  39. package/dist/api/templates/load-wasi-template.js.map +1 -0
  40. package/dist/api/templates/package.json.d.ts +9 -2
  41. package/dist/api/templates/package.json.js +53 -28
  42. package/dist/api/templates/package.json.js.map +1 -0
  43. package/dist/api/templates/wasi-worker-template.d.ts +2 -0
  44. package/dist/api/templates/wasi-worker-template.js +93 -0
  45. package/dist/api/templates/wasi-worker-template.js.map +1 -0
  46. package/dist/api/universalize.js +6 -5
  47. package/dist/api/universalize.js.map +1 -0
  48. package/dist/api/version.js +3 -2
  49. package/dist/api/version.js.map +1 -0
  50. package/dist/cli.js +1 -0
  51. package/dist/cli.js.map +1 -0
  52. package/dist/commands/artifacts.js +4 -4
  53. package/dist/commands/artifacts.js.map +1 -0
  54. package/dist/commands/build.js +2 -1
  55. package/dist/commands/build.js.map +1 -0
  56. package/dist/commands/create-npm-dirs.js +1 -0
  57. package/dist/commands/create-npm-dirs.js.map +1 -0
  58. package/dist/commands/help.js +2 -2
  59. package/dist/commands/help.js.map +1 -0
  60. package/dist/commands/new.js +15 -7
  61. package/dist/commands/new.js.map +1 -0
  62. package/dist/commands/pre-publish.js +1 -0
  63. package/dist/commands/pre-publish.js.map +1 -0
  64. package/dist/commands/rename.js +1 -0
  65. package/dist/commands/rename.js.map +1 -0
  66. package/dist/commands/universalize.js +1 -0
  67. package/dist/commands/universalize.js.map +1 -0
  68. package/dist/commands/version.js +1 -0
  69. package/dist/commands/version.js.map +1 -0
  70. package/dist/def/artifacts.d.ts +21 -1
  71. package/dist/def/artifacts.js +12 -4
  72. package/dist/def/artifacts.js.map +1 -0
  73. package/dist/def/build.d.ts +20 -2
  74. package/dist/def/build.js +16 -4
  75. package/dist/def/build.js.map +1 -0
  76. package/dist/def/create-npm-dirs.d.ts +10 -0
  77. package/dist/def/create-npm-dirs.js +6 -2
  78. package/dist/def/create-npm-dirs.js.map +1 -0
  79. package/dist/def/new.d.ts +24 -6
  80. package/dist/def/new.js +13 -3
  81. package/dist/def/new.js.map +1 -0
  82. package/dist/def/pre-publish.d.ts +10 -0
  83. package/dist/def/pre-publish.js +9 -5
  84. package/dist/def/pre-publish.js.map +1 -0
  85. package/dist/def/rename.d.ts +11 -1
  86. package/dist/def/rename.js +7 -3
  87. package/dist/def/rename.js.map +1 -0
  88. package/dist/def/universalize.d.ts +10 -0
  89. package/dist/def/universalize.js +6 -2
  90. package/dist/def/universalize.js.map +1 -0
  91. package/dist/def/version.d.ts +10 -0
  92. package/dist/def/version.js +6 -2
  93. package/dist/def/version.js.map +1 -0
  94. package/dist/index.cjs +6022 -8033
  95. package/dist/index.d.ts +1 -0
  96. package/dist/index.js +2 -0
  97. package/dist/index.js.map +1 -0
  98. package/dist/utils/__tests__/config.spec.d.ts +1 -0
  99. package/dist/utils/__tests__/config.spec.js +63 -0
  100. package/dist/utils/__tests__/config.spec.js.map +1 -0
  101. package/dist/utils/__tests__/target.spec.js +1 -0
  102. package/dist/utils/__tests__/target.spec.js.map +1 -0
  103. package/dist/utils/__tests__/typegen.spec.js +9 -4
  104. package/dist/utils/__tests__/typegen.spec.js.map +1 -0
  105. package/dist/utils/__tests__/version.spec.js +1 -0
  106. package/dist/utils/__tests__/version.spec.js.map +1 -0
  107. package/dist/utils/cargo.js +2 -1
  108. package/dist/utils/cargo.js.map +1 -0
  109. package/dist/utils/ci.js +1 -0
  110. package/dist/utils/ci.js.map +1 -0
  111. package/dist/utils/config.d.ts +25 -5
  112. package/dist/utils/config.js +39 -6
  113. package/dist/utils/config.js.map +1 -0
  114. package/dist/utils/index.js +1 -0
  115. package/dist/utils/index.js.map +1 -0
  116. package/dist/utils/log.js +1 -0
  117. package/dist/utils/log.js.map +1 -0
  118. package/dist/utils/metadata.js +3 -2
  119. package/dist/utils/metadata.js.map +1 -0
  120. package/dist/utils/misc.d.ts +1 -1
  121. package/dist/utils/misc.js +4 -3
  122. package/dist/utils/misc.js.map +1 -0
  123. package/dist/utils/target.d.ts +9 -5
  124. package/dist/utils/target.js +16 -1
  125. package/dist/utils/target.js.map +1 -0
  126. package/dist/utils/typegen.d.ts +4 -1
  127. package/dist/utils/typegen.js +27 -6
  128. package/dist/utils/typegen.js.map +1 -0
  129. package/dist/utils/version.d.ts +2 -1
  130. package/dist/utils/version.js +13 -10
  131. package/dist/utils/version.js.map +1 -0
  132. package/package.json +50 -16
  133. package/src/api/artifacts.ts +85 -10
  134. package/src/api/build.ts +381 -32
  135. package/src/api/create-npm-dirs.ts +56 -8
  136. package/src/api/new.ts +69 -33
  137. package/src/api/pre-publish.ts +21 -12
  138. package/src/api/rename.ts +10 -1
  139. package/src/api/templates/.gitignore.ts +1 -0
  140. package/src/api/templates/ci-template.ts +151 -152
  141. package/src/api/templates/ci.yml.ts +19 -4
  142. package/src/api/templates/js-binding.ts +146 -30
  143. package/src/api/templates/lib.rs.ts +1 -4
  144. package/src/api/templates/load-wasi-template.ts +150 -0
  145. package/src/api/templates/package.json.ts +74 -27
  146. package/src/api/templates/wasi-worker-template.ts +93 -0
  147. package/src/api/universalize.ts +8 -5
  148. package/src/api/version.ts +5 -2
  149. package/src/commands/artifacts.ts +2 -2
  150. package/src/commands/build.ts +1 -1
  151. package/src/commands/new.ts +14 -8
  152. package/src/def/artifacts.ts +22 -3
  153. package/src/def/build.ts +32 -4
  154. package/src/def/create-npm-dirs.ts +9 -0
  155. package/src/def/new.ts +28 -3
  156. package/src/def/pre-publish.ts +12 -3
  157. package/src/def/rename.ts +11 -2
  158. package/src/def/universalize.ts +9 -0
  159. package/src/def/version.ts +9 -0
  160. package/src/index.ts +2 -0
  161. package/src/utils/__tests__/__snapshots__/config.spec.ts.md +97 -0
  162. package/src/utils/__tests__/__snapshots__/config.spec.ts.snap +0 -0
  163. package/src/utils/__tests__/__snapshots__/target.spec.ts.md +7 -0
  164. package/src/utils/__tests__/__snapshots__/target.spec.ts.snap +0 -0
  165. package/src/utils/__tests__/__snapshots__/typegen.spec.ts.md +555 -0
  166. package/src/utils/__tests__/__snapshots__/typegen.spec.ts.snap +0 -0
  167. package/src/utils/__tests__/__snapshots__/version.spec.ts.md +9 -8
  168. package/src/utils/__tests__/__snapshots__/version.spec.ts.snap +0 -0
  169. package/src/utils/__tests__/config.spec.ts +81 -0
  170. package/src/utils/__tests__/typegen.spec.ts +19 -4
  171. package/src/utils/cargo.ts +1 -1
  172. package/src/utils/config.ts +85 -9
  173. package/src/utils/metadata.ts +2 -2
  174. package/src/utils/misc.ts +11 -3
  175. package/src/utils/target.ts +23 -7
  176. package/src/utils/typegen.ts +30 -5
  177. package/src/utils/version.ts +11 -9
package/src/api/build.ts CHANGED
@@ -1,9 +1,12 @@
1
- import { spawn } from 'child_process'
2
- import { createHash } from 'crypto'
3
- import { tmpdir } from 'os'
4
- import { parse, join, resolve } from 'path'
1
+ import { spawn } from 'node:child_process'
2
+ import { createHash } from 'node:crypto'
3
+ import { existsSync, mkdirSync } from 'node:fs'
4
+ import { createRequire } from 'node:module'
5
+ import { tmpdir, homedir } from 'node:os'
6
+ import { parse, join, resolve } from 'node:path'
5
7
 
6
8
  import * as colors from 'colorette'
9
+ import { include as setjmpInclude, lib as setjmpLib } from 'wasm-sjlj'
7
10
 
8
11
  import { BuildOptions as RawBuildOptions } from '../def/build.js'
9
12
  import {
@@ -20,6 +23,7 @@ import {
20
23
  parseMetadata,
21
24
  parseTriple,
22
25
  processTypeDef,
26
+ readFileAsync,
23
27
  readNapiConfig,
24
28
  Target,
25
29
  targetToEnvVar,
@@ -28,11 +32,20 @@ import {
28
32
  writeFileAsync,
29
33
  } from '../utils/index.js'
30
34
 
31
- import { createJsBinding } from './templates/index.js'
35
+ import { createCjsBinding } from './templates/index.js'
36
+ import {
37
+ createWasiBinding,
38
+ createWasiBrowserBinding,
39
+ } from './templates/load-wasi-template.js'
40
+ import {
41
+ WASI_WORKER_BROWSER_TEMPLATE,
42
+ WASI_WORKER_TEMPLATE,
43
+ } from './templates/wasi-worker-template.js'
32
44
 
33
45
  const debug = debugFactory('build')
46
+ const require = createRequire(import.meta.url)
34
47
 
35
- type OutputKind = 'js' | 'dts' | 'node' | 'exe'
48
+ type OutputKind = 'js' | 'dts' | 'node' | 'exe' | 'wasm'
36
49
  type Output = {
37
50
  kind: OutputKind
38
51
  path: string
@@ -76,15 +89,18 @@ export async function buildProject(options: BuildOptions) {
76
89
  options.target
77
90
  ? parseTriple(options.target)
78
91
  : process.env.CARGO_BUILD_TARGET
79
- ? parseTriple(process.env.CARGO_BUILD_TARGET)
80
- : getSystemDefaultTarget(),
92
+ ? parseTriple(process.env.CARGO_BUILD_TARGET)
93
+ : getSystemDefaultTarget(),
81
94
  crateDir,
82
95
  resolvePath(options.outputDir ?? crateDir),
83
96
  options.targetDir ??
84
97
  process.env.CARGO_BUILD_TARGET_DIR ??
85
98
  metadata.target_directory,
86
99
  await readNapiConfig(
87
- resolvePath(options.packageJsonPath ?? 'package.json'),
100
+ resolvePath(
101
+ options.configPath ?? options.packageJsonPath ?? 'package.json',
102
+ ),
103
+ options.configPath ? resolvePath(options.configPath) : undefined,
88
104
  ),
89
105
  )
90
106
 
@@ -138,18 +154,147 @@ class Builder {
138
154
  .setPackage()
139
155
  .setFeatures()
140
156
  .setTarget()
157
+ .pickCrossToolchain()
141
158
  .setEnvs()
142
159
  .setBypassArgs()
143
160
  .exec()
144
161
  }
145
162
 
163
+ private pickCrossToolchain() {
164
+ if (!this.options.useNapiCross) {
165
+ return this
166
+ }
167
+ if (this.options.useCross) {
168
+ debug.warn(
169
+ 'You are trying to use both `--cross` and `--use-napi-cross` options, `--use-cross` will be ignored.',
170
+ )
171
+ }
172
+
173
+ if (this.options.crossCompile) {
174
+ debug.warn(
175
+ 'You are trying to use both `--cross-compile` and `--use-napi-cross` options, `--cross-compile` will be ignored.',
176
+ )
177
+ }
178
+
179
+ try {
180
+ const { version, download } = require('@napi-rs/cross-toolchain')
181
+
182
+ const toolchainPath = join(
183
+ homedir(),
184
+ '.napi-rs',
185
+ 'cross-toolchain',
186
+ version,
187
+ this.target.triple,
188
+ )
189
+ mkdirSync(toolchainPath, { recursive: true })
190
+ if (existsSync(join(toolchainPath, 'package.json'))) {
191
+ debug(`Toolchain ${toolchainPath} exists, skip extracting`)
192
+ } else {
193
+ const tarArchive = download(process.arch, this.target.triple)
194
+ tarArchive.unpack(toolchainPath)
195
+ }
196
+ const upperCaseTarget = targetToEnvVar(this.target.triple)
197
+ const linkerEnv = `CARGO_TARGET_${upperCaseTarget}_LINKER`
198
+ this.envs[linkerEnv] = join(
199
+ toolchainPath,
200
+ 'bin',
201
+ `${this.target.triple}-gcc`,
202
+ )
203
+ if (!process.env.TARGET_SYSROOT) {
204
+ this.envs[`TARGET_SYSROOT`] = join(
205
+ toolchainPath,
206
+ this.target.triple,
207
+ 'sysroot',
208
+ )
209
+ }
210
+ if (!process.env.TARGET_AR) {
211
+ this.envs[`TARGET_AR`] = join(
212
+ toolchainPath,
213
+ 'bin',
214
+ `${this.target.triple}-ar`,
215
+ )
216
+ }
217
+ if (!process.env.TARGET_RANLIB) {
218
+ this.envs[`TARGET_RANLIB`] = join(
219
+ toolchainPath,
220
+ 'bin',
221
+ `${this.target.triple}-ranlib`,
222
+ )
223
+ }
224
+ if (!process.env.TARGET_READELF) {
225
+ this.envs[`TARGET_READELF`] = join(
226
+ toolchainPath,
227
+ 'bin',
228
+ `${this.target.triple}-readelf`,
229
+ )
230
+ }
231
+ if (!process.env.TARGET_C_INCLUDE_PATH) {
232
+ this.envs[`TARGET_C_INCLUDE_PATH`] = join(
233
+ toolchainPath,
234
+ this.target.triple,
235
+ 'sysroot',
236
+ 'usr',
237
+ 'include/',
238
+ )
239
+ }
240
+ if (!process.env.CC && !process.env.TARGET_CC) {
241
+ this.envs[`CC`] = join(
242
+ toolchainPath,
243
+ 'bin',
244
+ `${this.target.triple}-gcc`,
245
+ )
246
+ this.envs[`TARGET_CC`] = join(
247
+ toolchainPath,
248
+ 'bin',
249
+ `${this.target.triple}-gcc`,
250
+ )
251
+ }
252
+ if (!process.env.CXX && !process.env.TARGET_CXX) {
253
+ this.envs[`CXX`] = join(
254
+ toolchainPath,
255
+ 'bin',
256
+ `${this.target.triple}-g++`,
257
+ )
258
+ this.envs[`TARGET_CXX`] = join(
259
+ toolchainPath,
260
+ 'bin',
261
+ `${this.target.triple}-g++`,
262
+ )
263
+ }
264
+ if (
265
+ (process.env.CC === 'clang' &&
266
+ (process.env.TARGET_CC === 'clang' || !process.env.TARGET_CC)) ||
267
+ process.env.TARGET_CC === 'clang'
268
+ ) {
269
+ this.envs.CFLAGS = `--sysroot=${this.envs.TARGET_SYSROOT}`
270
+ }
271
+ if (
272
+ (process.env.CXX === 'clang++' &&
273
+ (process.env.TARGET_CXX === 'clang++' || !process.env.TARGET_CXX)) ||
274
+ process.env.TARGET_CXX === 'clang++'
275
+ ) {
276
+ this.envs.CXXFLAGS = `--sysroot=${this.envs.TARGET_SYSROOT}`
277
+ }
278
+ } catch (e) {
279
+ debug.warn('Pick cross toolchain failed', e as Error)
280
+ // ignore, do nothing
281
+ }
282
+ return this
283
+ }
284
+
146
285
  private exec() {
147
286
  debug(`Start building crate: ${this.crate.name}`)
148
287
  debug(' %i', `cargo ${this.args.join(' ')}`)
149
288
 
150
289
  const controller = new AbortController()
151
290
 
291
+ const watch = this.options.watch
152
292
  const buildTask = new Promise<void>((resolve, reject) => {
293
+ if (this.options.useCross && this.options.crossCompile) {
294
+ throw new Error(
295
+ '`--use-cross` and `--cross-compile` can not be used together',
296
+ )
297
+ }
153
298
  const command =
154
299
  process.env.CARGO ?? (this.options.useCross ? 'cross' : 'cargo')
155
300
  const buildProcess = spawn(command, this.args, {
@@ -157,7 +302,7 @@ class Builder {
157
302
  ...process.env,
158
303
  ...this.envs,
159
304
  },
160
- stdio: 'inherit',
305
+ stdio: watch ? ['inherit', 'inherit', 'pipe'] : 'inherit',
161
306
  cwd: this.cwd,
162
307
  signal: controller.signal,
163
308
  })
@@ -178,6 +323,15 @@ class Builder {
178
323
  }),
179
324
  )
180
325
  })
326
+
327
+ // watch mode only, they are piped through stderr
328
+ buildProcess.stderr?.on('data', (data) => {
329
+ const output = data.toString()
330
+ console.error(output)
331
+ if (/Finished\s(dev|release)/.test(output)) {
332
+ this.postBuild().catch(() => {})
333
+ }
334
+ })
181
335
  })
182
336
 
183
337
  return {
@@ -207,6 +361,7 @@ class Builder {
207
361
  this.crateDir,
208
362
  '--',
209
363
  'cargo',
364
+ 'build',
210
365
  )
211
366
  set = true
212
367
  }
@@ -353,18 +508,61 @@ class Builder {
353
508
  const targetArch = this.target.arch === 'arm' ? 'armv7a' : 'aarch64'
354
509
  const targetPlatform =
355
510
  this.target.arch === 'arm' ? 'androideabi24' : 'android24'
511
+ const hostPlatform =
512
+ process.platform === 'darwin'
513
+ ? 'darwin'
514
+ : process.platform === 'win32'
515
+ ? 'windows'
516
+ : 'linux'
356
517
  Object.assign(this.envs, {
357
- CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/${targetArch}-linux-android24-clang`,
358
- CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/${targetArch}-linux-androideabi24-clang`,
359
- CC: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/${targetArch}-linux-${targetPlatform}-clang`,
360
- CXX: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/${targetArch}-linux-${targetPlatform}-clang++`,
361
- AR: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar`,
518
+ CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/${hostPlatform}-x86_64/bin/${targetArch}-linux-android24-clang`,
519
+ CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/${hostPlatform}-x86_64/bin/${targetArch}-linux-androideabi24-clang`,
520
+ CC: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/${hostPlatform}-x86_64/bin/${targetArch}-linux-${targetPlatform}-clang`,
521
+ CXX: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/${hostPlatform}-x86_64/bin/${targetArch}-linux-${targetPlatform}-clang++`,
522
+ AR: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/${hostPlatform}-x86_64/bin/llvm-ar`,
523
+ RANLIB: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/${hostPlatform}-x86_64/bin/llvm-ranlib`,
362
524
  ANDROID_NDK: ANDROID_NDK_LATEST_HOME,
363
- PATH: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin:${process.env.PATH}`,
525
+ PATH: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/${hostPlatform}-x86_64/bin:${process.env.PATH}`,
364
526
  })
365
527
  }
366
528
  // END LINKER
367
529
 
530
+ if (this.target.platform === 'wasi') {
531
+ const emnapi = join(
532
+ require.resolve('emnapi'),
533
+ '..',
534
+ 'lib',
535
+ 'wasm32-wasi-threads',
536
+ )
537
+ this.envs.EMNAPI_LINK_DIR = emnapi
538
+ this.envs.SETJMP_LINK_DIR = setjmpLib
539
+ const { WASI_SDK_PATH } = process.env
540
+
541
+ if (WASI_SDK_PATH && existsSync(WASI_SDK_PATH)) {
542
+ this.envs.CARGO_TARGET_WASM32_WASI_PREVIEW1_THREADS_LINKER = join(
543
+ WASI_SDK_PATH,
544
+ 'bin',
545
+ 'wasm-ld',
546
+ )
547
+ this.setEnvIfNotExists('CC', join(WASI_SDK_PATH, 'bin', 'clang'))
548
+ this.setEnvIfNotExists('CXX', join(WASI_SDK_PATH, 'bin', 'clang++'))
549
+ this.setEnvIfNotExists('AR', join(WASI_SDK_PATH, 'bin', 'ar'))
550
+ this.setEnvIfNotExists('RANLIB', join(WASI_SDK_PATH, 'bin', 'ranlib'))
551
+ this.setEnvIfNotExists(
552
+ 'CFLAGS',
553
+ `--target=wasm32-wasi-threads --sysroot=${WASI_SDK_PATH}/share/wasi-sysroot -pthread -mllvm -wasm-enable-sjlj -I${setjmpInclude}`,
554
+ )
555
+ this.setEnvIfNotExists(
556
+ 'CXXFLAGS',
557
+ `--target=wasm32-wasi-threads --sysroot=${WASI_SDK_PATH}/share/wasi-sysroot -pthread -mllvm -wasm-enable-sjlj -I${setjmpInclude}`,
558
+ )
559
+ this.setEnvIfNotExists(
560
+ `LDFLAGS`,
561
+ `-fuse-ld=${WASI_SDK_PATH}/bin/wasm-ld --target=wasm32-wasi-threads`,
562
+ )
563
+ }
564
+ }
565
+
368
566
  debug('Set envs: ')
369
567
  Object.entries(this.envs).forEach(([k, v]) => {
370
568
  debug(' %i', `${k}=${v}`)
@@ -375,11 +573,17 @@ class Builder {
375
573
 
376
574
  private setFeatures() {
377
575
  const args = []
576
+ if (this.options.allFeatures && this.options.allFeatures) {
577
+ throw new Error(
578
+ 'Cannot specify --all-features and --no-default-features together',
579
+ )
580
+ }
378
581
  if (this.options.allFeatures) {
379
582
  args.push('--all-features')
380
583
  } else if (this.options.noDefaultFeatures) {
381
584
  args.push('--no-default-features')
382
- } else if (this.options.features) {
585
+ }
586
+ if (this.options.features) {
383
587
  args.push('--features', ...this.options.features)
384
588
  }
385
589
 
@@ -407,6 +611,10 @@ class Builder {
407
611
  this.args.push('--profile', this.options.profile)
408
612
  }
409
613
 
614
+ if (this.options.manifestPath) {
615
+ this.args.push('--manifest-path', this.options.manifestPath)
616
+ }
617
+
410
618
  if (this.options.cargoOptions?.length) {
411
619
  this.args.push(...this.options.cargoOptions)
412
620
  }
@@ -448,12 +656,48 @@ class Builder {
448
656
  })
449
657
  }
450
658
 
451
- await this.copyArtifact()
659
+ const dest = await this.copyArtifact()
452
660
 
453
661
  // only for cdylib
454
662
  if (this.cdyLibName) {
455
- await this.generateTypeDef()
456
- await this.writeJsBinding()
663
+ const idents = await this.generateTypeDef()
664
+ const intermediateWasiRegisterFile = this.envs.WASI_REGISTER_TMP_PATH
665
+ const wasiRegisterFunctions =
666
+ this.target.arch === 'wasm32'
667
+ ? await (async function readIntermediateWasiRegisterFile() {
668
+ const fileContent = await readFileAsync(
669
+ intermediateWasiRegisterFile,
670
+ 'utf8',
671
+ ).catch((err) => {
672
+ console.warn(
673
+ `Read ${colors.yellowBright(
674
+ intermediateWasiRegisterFile,
675
+ )} failed, reason: ${err.message}`,
676
+ )
677
+ return ``
678
+ })
679
+ return fileContent
680
+ .split('\n')
681
+ .map((l) => l.trim())
682
+ .filter((l) => l.length)
683
+ .map((line) => {
684
+ const [_, fn] = line.split(':')
685
+ return fn.trim()
686
+ })
687
+ })()
688
+ : []
689
+ const jsOutput = await this.writeJsBinding(idents)
690
+ const wasmBindingsOutput = await this.writeWasiBinding(
691
+ wasiRegisterFunctions,
692
+ dest ?? 'index.wasm',
693
+ idents,
694
+ )
695
+ if (jsOutput) {
696
+ this.outputs.push(jsOutput)
697
+ }
698
+ if (wasmBindingsOutput) {
699
+ this.outputs.push(...wasmBindingsOutput)
700
+ }
457
701
  }
458
702
 
459
703
  return this.outputs
@@ -483,6 +727,7 @@ class Builder {
483
727
  kind: dest.endsWith('.node') ? 'node' : 'exe',
484
728
  path: dest,
485
729
  })
730
+ return dest
486
731
  } catch (e) {
487
732
  throw new Error('Failed to copy artifact', {
488
733
  cause: e,
@@ -498,8 +743,10 @@ class Builder {
498
743
  this.target.platform === 'darwin'
499
744
  ? `lib${cdyLib}.dylib`
500
745
  : this.target.platform === 'win32'
501
- ? `${cdyLib}.dll`
502
- : `lib${cdyLib}.so`
746
+ ? `${cdyLib}.dll`
747
+ : this.target.platform === 'wasi' || this.target.platform === 'wasm'
748
+ ? `${cdyLib}.wasm`
749
+ : `lib${cdyLib}.so`
503
750
 
504
751
  let destName = this.config.binaryName
505
752
  // add platform suffix to binary name
@@ -508,7 +755,11 @@ class Builder {
508
755
  if (this.options.platform) {
509
756
  destName += `.${this.target.platformArchABI}`
510
757
  }
511
- destName += '.node'
758
+ if (srcName.endsWith('.wasm')) {
759
+ destName += '.wasm'
760
+ } else {
761
+ destName += '.node'
762
+ }
512
763
 
513
764
  return [srcName, destName]
514
765
  } else if (this.binName) {
@@ -523,13 +774,14 @@ class Builder {
523
774
 
524
775
  private async generateTypeDef() {
525
776
  if (!(await fileExists(this.envs.TYPE_DEF_TMP_PATH))) {
526
- return
777
+ return []
527
778
  }
528
779
 
529
780
  const dest = join(this.outputDir, this.options.dts ?? 'index.d.ts')
530
781
 
531
- const dts = await processTypeDef(
782
+ const { dts, exports } = await processTypeDef(
532
783
  this.envs.TYPE_DEF_TMP_PATH,
784
+ this.options.constEnum ?? true,
533
785
  !this.options.noDtsHeader
534
786
  ? this.options.dtsHeader ?? DEFAULT_TYPE_DEF_HEADER
535
787
  : '',
@@ -547,27 +799,124 @@ class Builder {
547
799
  debug.error('Failed to write type def file')
548
800
  debug.error(e as Error)
549
801
  }
802
+
803
+ return exports
550
804
  }
551
805
 
552
- private async writeJsBinding() {
553
- if (!this.options.platform || this.options.noJsBinding) {
806
+ private async writeJsBinding(idents: string[]) {
807
+ if (
808
+ !this.options.platform ||
809
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
810
+ this.options.noJsBinding ||
811
+ idents.length === 0
812
+ ) {
554
813
  return
555
814
  }
556
815
 
557
- const dest = join(this.outputDir, this.options.jsBinding ?? 'index.js')
816
+ const name = this.options.jsBinding ?? 'index.js'
558
817
 
559
- const js = createJsBinding(this.config.binaryName, this.config.packageName)
818
+ const cjs = createCjsBinding(
819
+ this.config.binaryName,
820
+ this.config.packageName,
821
+ idents,
822
+ )
560
823
 
561
824
  try {
825
+ const dest = join(this.outputDir, name)
562
826
  debug('Writing js binding to:')
563
827
  debug(' %i', dest)
564
- await writeFileAsync(dest, js, 'utf-8')
565
- this.outputs.push({
828
+ await writeFileAsync(dest, cjs, 'utf-8')
829
+ return {
566
830
  kind: 'js',
567
831
  path: dest,
568
- })
832
+ } satisfies Output
569
833
  } catch (e) {
570
834
  throw new Error('Failed to write js binding file', { cause: e })
571
835
  }
572
836
  }
837
+
838
+ private async writeWasiBinding(
839
+ wasiRegisterFunctions: string[],
840
+ distFileName: string | undefined,
841
+ idents: string[],
842
+ ) {
843
+ if (distFileName && wasiRegisterFunctions.length) {
844
+ const { name, dir } = parse(distFileName)
845
+ const bindingPath = join(dir, `${this.config.binaryName}.wasi.cjs`)
846
+ const browserBindingPath = join(
847
+ dir,
848
+ `${this.config.binaryName}.wasi-browser.js`,
849
+ )
850
+ const workerPath = join(dir, 'wasi-worker.mjs')
851
+ const browserWorkerPath = join(dir, 'wasi-worker-browser.mjs')
852
+ const browserEntryPath = join(dir, 'browser.js')
853
+ const exportsCode = idents
854
+ .map(
855
+ (ident) => `module.exports.${ident} = __napiModule.exports.${ident}`,
856
+ )
857
+ .join('\n')
858
+ await writeFileAsync(
859
+ bindingPath,
860
+ createWasiBinding(
861
+ name,
862
+ this.config.packageName,
863
+ wasiRegisterFunctions,
864
+ ) +
865
+ exportsCode +
866
+ '\n',
867
+ 'utf8',
868
+ )
869
+ await writeFileAsync(
870
+ browserBindingPath,
871
+ createWasiBrowserBinding(name, wasiRegisterFunctions) +
872
+ idents
873
+ .map(
874
+ (ident) =>
875
+ `export const ${ident} = __napiModule.exports.${ident}`,
876
+ )
877
+ .join('\n') +
878
+ '\n',
879
+ 'utf8',
880
+ )
881
+ await writeFileAsync(workerPath, WASI_WORKER_TEMPLATE, 'utf8')
882
+ await writeFileAsync(
883
+ browserWorkerPath,
884
+ WASI_WORKER_BROWSER_TEMPLATE,
885
+ 'utf8',
886
+ )
887
+ await writeFileAsync(
888
+ browserEntryPath,
889
+ `export * from '${this.config.packageName}-wasm32-wasi'\n`,
890
+ )
891
+ return [
892
+ {
893
+ kind: 'js',
894
+ path: bindingPath,
895
+ },
896
+ {
897
+ kind: 'js',
898
+ path: browserBindingPath,
899
+ },
900
+ {
901
+ kind: 'js',
902
+ path: workerPath,
903
+ },
904
+ {
905
+ kind: 'js',
906
+ path: browserWorkerPath,
907
+ },
908
+ {
909
+ kind: 'js',
910
+ path: browserEntryPath,
911
+ },
912
+ ] satisfies Output[]
913
+ }
914
+ return []
915
+ }
916
+
917
+ private setEnvIfNotExists(env: string, value: string) {
918
+ if (!process.env[env]) {
919
+ this.envs[env] = value
920
+ }
921
+ }
573
922
  }
@@ -1,4 +1,6 @@
1
- import { join, resolve } from 'path'
1
+ import { join, resolve } from 'node:path'
2
+
3
+ import { parse } from 'semver'
2
4
 
3
5
  import {
4
6
  applyDefaultCreateNpmDirsOptions,
@@ -13,6 +15,8 @@ import {
13
15
  Target,
14
16
  } from '../utils/index.js'
15
17
 
18
+ import type { PackageMeta } from './templates/package.json.js'
19
+
16
20
  const debug = debugFactory('create-npm-dirs')
17
21
 
18
22
  export async function createNpmDirs(userOptions: CreateNpmDirsOptions) {
@@ -43,20 +47,25 @@ export async function createNpmDirs(userOptions: CreateNpmDirsOptions) {
43
47
  const packageJsonPath = resolve(options.cwd, options.packageJsonPath)
44
48
  const npmPath = resolve(options.cwd, options.npmDir)
45
49
 
46
- debug(`Read content from [${packageJsonPath}]`)
50
+ debug(`Read content from [${options.configPath ?? packageJsonPath}]`)
47
51
 
48
52
  const { targets, binaryName, packageName, packageJson } =
49
- await readNapiConfig(packageJsonPath)
53
+ await readNapiConfig(
54
+ packageJsonPath,
55
+ options.configPath ? resolve(options.cwd, options.configPath) : undefined,
56
+ )
50
57
 
51
58
  for (const target of targets) {
52
59
  const targetDir = join(npmPath, `${target.platformArchABI}`)
53
60
  await mkdirAsync(targetDir)
54
61
 
55
- const binaryFileName = `${binaryName}.${target.platformArchABI}.node`
62
+ const binaryFileName =
63
+ target.arch === 'wasm32'
64
+ ? `${binaryName}.${target.platformArchABI}.wasm`
65
+ : `${binaryName}.${target.platformArchABI}.node`
56
66
  const scopedPackageJson = {
57
67
  name: `${packageName}-${target.platformArchABI}`,
58
68
  version: packageJson.version,
59
- os: [target.platform],
60
69
  cpu: target.arch !== 'universal' ? [target.arch] : undefined,
61
70
  main: binaryFileName,
62
71
  files: [binaryFileName],
@@ -74,9 +83,48 @@ export async function createNpmDirs(userOptions: CreateNpmDirsOptions) {
74
83
  'bugs',
75
84
  ),
76
85
  }
86
+ if (target.arch !== 'wasm32') {
87
+ // @ts-expect-error
88
+ scopedPackageJson.os = [target.platform]
89
+ } else {
90
+ const entry = `${binaryName}.wasi.cjs`
91
+ scopedPackageJson.main = entry
92
+ // @ts-expect-error
93
+ scopedPackageJson.browser = `${binaryName}.wasi-browser.js`
94
+ scopedPackageJson.files.push(
95
+ entry,
96
+ // @ts-expect-error
97
+ scopedPackageJson.browser,
98
+ `wasi-worker.mjs`,
99
+ `wasi-worker-browser.mjs`,
100
+ )
101
+ let needRestrictNodeVersion = true
102
+ if (scopedPackageJson.engines?.node) {
103
+ try {
104
+ const { major } = parse(scopedPackageJson.engines.node) ?? {
105
+ major: 0,
106
+ }
107
+ if (major >= 14) {
108
+ needRestrictNodeVersion = false
109
+ }
110
+ } catch {
111
+ // ignore
112
+ }
113
+ }
114
+ if (needRestrictNodeVersion) {
115
+ scopedPackageJson.engines = {
116
+ node: '>=14.0.0',
117
+ }
118
+ }
119
+ const wasmRuntime = await fetch(
120
+ `https://registry.npmjs.org/@napi-rs/wasm-runtime`,
121
+ ).then((res) => res.json() as Promise<PackageMeta>)
122
+ // @ts-expect-error
123
+ scopedPackageJson.dependencies = {
124
+ '@napi-rs/wasm-runtime': `^${wasmRuntime['dist-tags'].latest}`,
125
+ }
126
+ }
77
127
 
78
- // Only works with yarn 3.1+
79
- // https://github.com/yarnpkg/berry/pull/3981
80
128
  if (target.abi === 'gnu') {
81
129
  // @ts-expect-error
82
130
  scopedPackageJson.libc = ['glibc']
@@ -93,7 +141,7 @@ export async function createNpmDirs(userOptions: CreateNpmDirsOptions) {
93
141
  const targetReadme = join(targetDir, 'README.md')
94
142
  await writeFileAsync(targetReadme, readme(packageName, target))
95
143
 
96
- debug.info(`${packageName}-${target.platformArchABI} created`)
144
+ debug.info(`${packageName} -${target.platformArchABI} created`)
97
145
  }
98
146
  }
99
147