@napi-rs/cli 3.0.0-alpha.5 → 3.0.0-alpha.50

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 (178) hide show
  1. package/dist/api/artifacts.js +32 -8
  2. package/dist/api/artifacts.js.map +1 -0
  3. package/dist/api/build.d.ts +1 -1
  4. package/dist/api/build.js +249 -25
  5. package/dist/api/build.js.map +1 -0
  6. package/dist/api/create-npm-dirs.js +46 -8
  7. package/dist/api/create-npm-dirs.js.map +1 -0
  8. package/dist/api/new.js +58 -20
  9. package/dist/api/new.js.map +1 -0
  10. package/dist/api/pre-publish.js +10 -7
  11. package/dist/api/pre-publish.js.map +1 -0
  12. package/dist/api/rename.js +10 -1
  13. package/dist/api/rename.js.map +1 -0
  14. package/dist/api/templates/.gitignore.d.ts +1 -1
  15. package/dist/api/templates/.gitignore.js +2 -0
  16. package/dist/api/templates/.gitignore.js.map +1 -0
  17. package/dist/api/templates/.npmignore.js +1 -0
  18. package/dist/api/templates/.npmignore.js.map +1 -0
  19. package/dist/api/templates/build.rs.js +1 -0
  20. package/dist/api/templates/build.rs.js.map +1 -0
  21. package/dist/api/templates/cargo.toml.js +1 -0
  22. package/dist/api/templates/cargo.toml.js.map +1 -0
  23. package/dist/api/templates/ci-template.d.ts +3 -1
  24. package/dist/api/templates/ci-template.js +167 -150
  25. package/dist/api/templates/ci-template.js.map +1 -0
  26. package/dist/api/templates/ci.yml.d.ts +3 -1
  27. package/dist/api/templates/ci.yml.js +13 -2
  28. package/dist/api/templates/ci.yml.js.map +1 -0
  29. package/dist/api/templates/index.js +1 -0
  30. package/dist/api/templates/index.js.map +1 -0
  31. package/dist/api/templates/js-binding.js +150 -225
  32. package/dist/api/templates/js-binding.js.map +1 -0
  33. package/dist/api/templates/lib.rs.js +2 -4
  34. package/dist/api/templates/lib.rs.js.map +1 -0
  35. package/dist/api/templates/load-wasi-template.d.ts +2 -0
  36. package/dist/api/templates/load-wasi-template.js +146 -0
  37. package/dist/api/templates/load-wasi-template.js.map +1 -0
  38. package/dist/api/templates/package.json.d.ts +9 -2
  39. package/dist/api/templates/package.json.js +27 -4
  40. package/dist/api/templates/package.json.js.map +1 -0
  41. package/dist/api/templates/wasi-worker-template.d.ts +2 -0
  42. package/dist/api/templates/wasi-worker-template.js +103 -0
  43. package/dist/api/templates/wasi-worker-template.js.map +1 -0
  44. package/dist/api/universalize.js +6 -5
  45. package/dist/api/universalize.js.map +1 -0
  46. package/dist/api/version.js +3 -2
  47. package/dist/api/version.js.map +1 -0
  48. package/dist/cli.js +1 -0
  49. package/dist/cli.js.map +1 -0
  50. package/dist/commands/artifacts.js +3 -2
  51. package/dist/commands/artifacts.js.map +1 -0
  52. package/dist/commands/build.js +2 -1
  53. package/dist/commands/build.js.map +1 -0
  54. package/dist/commands/create-npm-dirs.js +1 -0
  55. package/dist/commands/create-npm-dirs.js.map +1 -0
  56. package/dist/commands/help.js +1 -0
  57. package/dist/commands/help.js.map +1 -0
  58. package/dist/commands/new.js +17 -6
  59. package/dist/commands/new.js.map +1 -0
  60. package/dist/commands/pre-publish.js +1 -0
  61. package/dist/commands/pre-publish.js.map +1 -0
  62. package/dist/commands/rename.js +1 -0
  63. package/dist/commands/rename.js.map +1 -0
  64. package/dist/commands/universalize.js +1 -0
  65. package/dist/commands/universalize.js.map +1 -0
  66. package/dist/commands/version.js +1 -0
  67. package/dist/commands/version.js.map +1 -0
  68. package/dist/def/artifacts.d.ts +21 -1
  69. package/dist/def/artifacts.js +11 -2
  70. package/dist/def/artifacts.js.map +1 -0
  71. package/dist/def/build.d.ts +20 -2
  72. package/dist/def/build.js +15 -2
  73. package/dist/def/build.js.map +1 -0
  74. package/dist/def/create-npm-dirs.d.ts +10 -0
  75. package/dist/def/create-npm-dirs.js +5 -0
  76. package/dist/def/create-npm-dirs.js.map +1 -0
  77. package/dist/def/new.d.ts +24 -6
  78. package/dist/def/new.js +12 -1
  79. package/dist/def/new.js.map +1 -0
  80. package/dist/def/pre-publish.d.ts +10 -0
  81. package/dist/def/pre-publish.js +8 -3
  82. package/dist/def/pre-publish.js.map +1 -0
  83. package/dist/def/rename.d.ts +11 -1
  84. package/dist/def/rename.js +6 -1
  85. package/dist/def/rename.js.map +1 -0
  86. package/dist/def/universalize.d.ts +10 -0
  87. package/dist/def/universalize.js +5 -0
  88. package/dist/def/universalize.js.map +1 -0
  89. package/dist/def/version.d.ts +10 -0
  90. package/dist/def/version.js +5 -0
  91. package/dist/def/version.js.map +1 -0
  92. package/dist/index.cjs +3918 -715
  93. package/dist/index.d.ts +1 -0
  94. package/dist/index.js +2 -0
  95. package/dist/index.js.map +1 -0
  96. package/dist/utils/__tests__/config.spec.d.ts +1 -0
  97. package/dist/utils/__tests__/config.spec.js +63 -0
  98. package/dist/utils/__tests__/config.spec.js.map +1 -0
  99. package/dist/utils/__tests__/target.spec.js +1 -0
  100. package/dist/utils/__tests__/target.spec.js.map +1 -0
  101. package/dist/utils/__tests__/typegen.spec.js +9 -4
  102. package/dist/utils/__tests__/typegen.spec.js.map +1 -0
  103. package/dist/utils/__tests__/version.spec.js +1 -0
  104. package/dist/utils/__tests__/version.spec.js.map +1 -0
  105. package/dist/utils/cargo.js +2 -1
  106. package/dist/utils/cargo.js.map +1 -0
  107. package/dist/utils/config.d.ts +40 -5
  108. package/dist/utils/config.js +39 -6
  109. package/dist/utils/config.js.map +1 -0
  110. package/dist/utils/index.d.ts +0 -1
  111. package/dist/utils/index.js +1 -1
  112. package/dist/utils/index.js.map +1 -0
  113. package/dist/utils/log.js +1 -0
  114. package/dist/utils/log.js.map +1 -0
  115. package/dist/utils/metadata.js +3 -2
  116. package/dist/utils/metadata.js.map +1 -0
  117. package/dist/utils/misc.d.ts +1 -1
  118. package/dist/utils/misc.js +4 -3
  119. package/dist/utils/misc.js.map +1 -0
  120. package/dist/utils/target.d.ts +9 -5
  121. package/dist/utils/target.js +25 -1
  122. package/dist/utils/target.js.map +1 -0
  123. package/dist/utils/typegen.d.ts +1 -1
  124. package/dist/utils/typegen.js +8 -6
  125. package/dist/utils/typegen.js.map +1 -0
  126. package/dist/utils/version.d.ts +2 -1
  127. package/dist/utils/version.js +11 -8
  128. package/dist/utils/version.js.map +1 -0
  129. package/package.json +46 -13
  130. package/src/api/artifacts.ts +83 -8
  131. package/src/api/build.ts +416 -27
  132. package/src/api/create-npm-dirs.ts +56 -8
  133. package/src/api/new.ts +82 -31
  134. package/src/api/pre-publish.ts +13 -7
  135. package/src/api/rename.ts +10 -1
  136. package/src/api/templates/.gitignore.ts +1 -0
  137. package/src/api/templates/ci-template.ts +176 -150
  138. package/src/api/templates/ci.yml.ts +16 -3
  139. package/src/api/templates/js-binding.ts +150 -225
  140. package/src/api/templates/lib.rs.ts +1 -4
  141. package/src/api/templates/load-wasi-template.ts +157 -0
  142. package/src/api/templates/package.json.ts +47 -5
  143. package/src/api/templates/wasi-worker-template.ts +103 -0
  144. package/src/api/universalize.ts +8 -5
  145. package/src/api/version.ts +5 -2
  146. package/src/commands/artifacts.ts +2 -2
  147. package/src/commands/build.ts +1 -1
  148. package/src/commands/new.ts +16 -6
  149. package/src/def/artifacts.ts +22 -3
  150. package/src/def/build.ts +32 -4
  151. package/src/def/create-npm-dirs.ts +9 -0
  152. package/src/def/new.ts +28 -3
  153. package/src/def/pre-publish.ts +12 -3
  154. package/src/def/rename.ts +11 -2
  155. package/src/def/universalize.ts +9 -0
  156. package/src/def/version.ts +9 -0
  157. package/src/index.ts +2 -0
  158. package/src/utils/__tests__/__snapshots__/config.spec.ts.md +97 -0
  159. package/src/utils/__tests__/__snapshots__/config.spec.ts.snap +0 -0
  160. package/src/utils/__tests__/__snapshots__/target.spec.ts.md +28 -0
  161. package/src/utils/__tests__/__snapshots__/target.spec.ts.snap +0 -0
  162. package/src/utils/__tests__/__snapshots__/typegen.spec.ts.md +555 -0
  163. package/src/utils/__tests__/__snapshots__/typegen.spec.ts.snap +0 -0
  164. package/src/utils/__tests__/__snapshots__/version.spec.ts.md +8 -7
  165. package/src/utils/__tests__/__snapshots__/version.spec.ts.snap +0 -0
  166. package/src/utils/__tests__/config.spec.ts +81 -0
  167. package/src/utils/__tests__/typegen.spec.ts +18 -3
  168. package/src/utils/cargo.ts +1 -1
  169. package/src/utils/config.ts +104 -12
  170. package/src/utils/index.ts +0 -1
  171. package/src/utils/metadata.ts +2 -2
  172. package/src/utils/misc.ts +11 -3
  173. package/src/utils/target.ts +34 -7
  174. package/src/utils/typegen.ts +11 -5
  175. package/src/utils/version.ts +10 -8
  176. package/dist/utils/ci.d.ts +0 -11
  177. package/dist/utils/ci.js +0 -95
  178. package/src/utils/ci.ts +0 -111
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,
@@ -29,10 +33,19 @@ import {
29
33
  } from '../utils/index.js'
30
34
 
31
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,11 +154,134 @@ 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(' ')}`)
@@ -151,6 +290,11 @@ class Builder {
151
290
 
152
291
  const watch = this.options.watch
153
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
+ }
154
298
  const command =
155
299
  process.env.CARGO ?? (this.options.useCross ? 'cross' : 'cargo')
156
300
  const buildProcess = spawn(command, this.args, {
@@ -364,18 +508,76 @@ class Builder {
364
508
  const targetArch = this.target.arch === 'arm' ? 'armv7a' : 'aarch64'
365
509
  const targetPlatform =
366
510
  this.target.arch === 'arm' ? 'androideabi24' : 'android24'
511
+ const hostPlatform =
512
+ process.platform === 'darwin'
513
+ ? 'darwin'
514
+ : process.platform === 'win32'
515
+ ? 'windows'
516
+ : 'linux'
367
517
  Object.assign(this.envs, {
368
- CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/${targetArch}-linux-android24-clang`,
369
- CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/${targetArch}-linux-androideabi24-clang`,
370
- CC: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/${targetArch}-linux-${targetPlatform}-clang`,
371
- CXX: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/${targetArch}-linux-${targetPlatform}-clang++`,
372
- 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`,
373
524
  ANDROID_NDK: ANDROID_NDK_LATEST_HOME,
374
- 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}`,
375
526
  })
376
527
  }
377
528
  // END LINKER
378
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.envs.CARGO_TARGET_WASM32_WASIP1_LINKER = join(
548
+ WASI_SDK_PATH,
549
+ 'bin',
550
+ 'wasm-ld',
551
+ )
552
+ this.envs.CARGO_TARGET_WASM32_WASIP1_THREADS_LINKER = join(
553
+ WASI_SDK_PATH,
554
+ 'bin',
555
+ 'wasm-ld',
556
+ )
557
+ this.envs.CARGO_TARGET_WASM32_WASIP2_LINKER = join(
558
+ WASI_SDK_PATH,
559
+ 'bin',
560
+ 'wasm-ld',
561
+ )
562
+ this.setEnvIfNotExists('CC', join(WASI_SDK_PATH, 'bin', 'clang'))
563
+ this.setEnvIfNotExists('CXX', join(WASI_SDK_PATH, 'bin', 'clang++'))
564
+ this.setEnvIfNotExists('AR', join(WASI_SDK_PATH, 'bin', 'ar'))
565
+ this.setEnvIfNotExists('RANLIB', join(WASI_SDK_PATH, 'bin', 'ranlib'))
566
+ this.setEnvIfNotExists(
567
+ 'CFLAGS',
568
+ `--target=wasm32-wasi-threads --sysroot=${WASI_SDK_PATH}/share/wasi-sysroot -pthread -mllvm -wasm-enable-sjlj -I${setjmpInclude}`,
569
+ )
570
+ this.setEnvIfNotExists(
571
+ 'CXXFLAGS',
572
+ `--target=wasm32-wasi-threads --sysroot=${WASI_SDK_PATH}/share/wasi-sysroot -pthread -mllvm -wasm-enable-sjlj -I${setjmpInclude}`,
573
+ )
574
+ this.setEnvIfNotExists(
575
+ `LDFLAGS`,
576
+ `-fuse-ld=${WASI_SDK_PATH}/bin/wasm-ld --target=wasm32-wasi-threads`,
577
+ )
578
+ }
579
+ }
580
+
379
581
  debug('Set envs: ')
380
582
  Object.entries(this.envs).forEach(([k, v]) => {
381
583
  debug(' %i', `${k}=${v}`)
@@ -386,11 +588,17 @@ class Builder {
386
588
 
387
589
  private setFeatures() {
388
590
  const args = []
591
+ if (this.options.allFeatures && this.options.allFeatures) {
592
+ throw new Error(
593
+ 'Cannot specify --all-features and --no-default-features together',
594
+ )
595
+ }
389
596
  if (this.options.allFeatures) {
390
597
  args.push('--all-features')
391
598
  } else if (this.options.noDefaultFeatures) {
392
599
  args.push('--no-default-features')
393
- } else if (this.options.features) {
600
+ }
601
+ if (this.options.features) {
394
602
  args.push('--features', ...this.options.features)
395
603
  }
396
604
 
@@ -418,6 +626,10 @@ class Builder {
418
626
  this.args.push('--profile', this.options.profile)
419
627
  }
420
628
 
629
+ if (this.options.manifestPath) {
630
+ this.args.push('--manifest-path', this.options.manifestPath)
631
+ }
632
+
421
633
  if (this.options.cargoOptions?.length) {
422
634
  this.args.push(...this.options.cargoOptions)
423
635
  }
@@ -459,12 +671,48 @@ class Builder {
459
671
  })
460
672
  }
461
673
 
462
- await this.copyArtifact()
674
+ const dest = await this.copyArtifact()
463
675
 
464
676
  // only for cdylib
465
677
  if (this.cdyLibName) {
466
678
  const idents = await this.generateTypeDef()
467
- await this.writeJsBinding(idents)
679
+ const intermediateWasiRegisterFile = this.envs.WASI_REGISTER_TMP_PATH
680
+ const wasiRegisterFunctions =
681
+ this.target.arch === 'wasm32'
682
+ ? await (async function readIntermediateWasiRegisterFile() {
683
+ const fileContent = await readFileAsync(
684
+ intermediateWasiRegisterFile,
685
+ 'utf8',
686
+ ).catch((err) => {
687
+ console.warn(
688
+ `Read ${colors.yellowBright(
689
+ intermediateWasiRegisterFile,
690
+ )} failed, reason: ${err.message}`,
691
+ )
692
+ return ``
693
+ })
694
+ return fileContent
695
+ .split('\n')
696
+ .map((l) => l.trim())
697
+ .filter((l) => l.length)
698
+ .map((line) => {
699
+ const [_, fn] = line.split(':')
700
+ return fn.trim()
701
+ })
702
+ })()
703
+ : []
704
+ const jsOutput = await this.writeJsBinding(idents)
705
+ const wasmBindingsOutput = await this.writeWasiBinding(
706
+ wasiRegisterFunctions,
707
+ dest ?? 'index.wasm',
708
+ idents,
709
+ )
710
+ if (jsOutput) {
711
+ this.outputs.push(jsOutput)
712
+ }
713
+ if (wasmBindingsOutput) {
714
+ this.outputs.push(...wasmBindingsOutput)
715
+ }
468
716
  }
469
717
 
470
718
  return this.outputs
@@ -481,6 +729,7 @@ class Builder {
481
729
  const src = join(this.targetDir, this.target.triple, profile, srcName)
482
730
  debug(`Copy artifact from: [${src}]`)
483
731
  const dest = join(this.outputDir, destName)
732
+ const isWasm = dest.endsWith('.wasm')
484
733
 
485
734
  try {
486
735
  if (await fileExists(dest)) {
@@ -489,11 +738,39 @@ class Builder {
489
738
  }
490
739
  debug('Copy artifact to:')
491
740
  debug(' %i', dest)
492
- await copyFileAsync(src, dest)
741
+ if (isWasm) {
742
+ const { ModuleConfig } = await import('@napi-rs/wasm-tools')
743
+ debug('Generate debug wasm module')
744
+ const debugWasmModule = new ModuleConfig()
745
+ .generateDwarf(true)
746
+ .generateNameSection(true)
747
+ .generateProducersSection(true)
748
+ .preserveCodeTransform(true)
749
+ .strictValidate(false)
750
+ .parse(await readFileAsync(src))
751
+ const debugWasmBinary = debugWasmModule.emitWasm(true)
752
+ await writeFileAsync(
753
+ dest.replace('.wasm', '.debug.wasm'),
754
+ debugWasmBinary,
755
+ )
756
+ debug('Generate release wasm module')
757
+ const releaseWasmModule = new ModuleConfig()
758
+ .generateDwarf(false)
759
+ .generateNameSection(false)
760
+ .generateProducersSection(false)
761
+ .preserveCodeTransform(false)
762
+ .strictValidate(false)
763
+ .parse(debugWasmBinary)
764
+ const releaseWasmBinary = releaseWasmModule.emitWasm(false)
765
+ await writeFileAsync(dest, releaseWasmBinary)
766
+ } else {
767
+ await copyFileAsync(src, dest)
768
+ }
493
769
  this.outputs.push({
494
- kind: dest.endsWith('.node') ? 'node' : 'exe',
770
+ kind: dest.endsWith('.node') ? 'node' : isWasm ? 'wasm' : 'exe',
495
771
  path: dest,
496
772
  })
773
+ return dest
497
774
  } catch (e) {
498
775
  throw new Error('Failed to copy artifact', {
499
776
  cause: e,
@@ -509,8 +786,10 @@ class Builder {
509
786
  this.target.platform === 'darwin'
510
787
  ? `lib${cdyLib}.dylib`
511
788
  : this.target.platform === 'win32'
512
- ? `${cdyLib}.dll`
513
- : `lib${cdyLib}.so`
789
+ ? `${cdyLib}.dll`
790
+ : this.target.platform === 'wasi' || this.target.platform === 'wasm'
791
+ ? `${cdyLib}.wasm`
792
+ : `lib${cdyLib}.so`
514
793
 
515
794
  let destName = this.config.binaryName
516
795
  // add platform suffix to binary name
@@ -519,7 +798,11 @@ class Builder {
519
798
  if (this.options.platform) {
520
799
  destName += `.${this.target.platformArchABI}`
521
800
  }
522
- destName += '.node'
801
+ if (srcName.endsWith('.wasm')) {
802
+ destName += '.wasm'
803
+ } else {
804
+ destName += '.node'
805
+ }
523
806
 
524
807
  return [srcName, destName]
525
808
  } else if (this.binName) {
@@ -541,8 +824,22 @@ class Builder {
541
824
 
542
825
  const { dts, exports } = await processTypeDef(
543
826
  this.envs.TYPE_DEF_TMP_PATH,
827
+ this.options.constEnum ?? true,
544
828
  !this.options.noDtsHeader
545
- ? this.options.dtsHeader ?? DEFAULT_TYPE_DEF_HEADER
829
+ ? this.options.dtsHeader ??
830
+ (this.config.dtsHeaderFile
831
+ ? await readFileAsync(
832
+ join(this.cwd, this.config.dtsHeaderFile),
833
+ 'utf-8',
834
+ ).catch(() => {
835
+ debug.warn(
836
+ `Failed to read dts header file ${this.config.dtsHeaderFile}`,
837
+ )
838
+ return null
839
+ })
840
+ : null) ??
841
+ this.config.dtsHeader ??
842
+ DEFAULT_TYPE_DEF_HEADER
546
843
  : '',
547
844
  )
548
845
 
@@ -572,7 +869,7 @@ class Builder {
572
869
  return
573
870
  }
574
871
 
575
- const name = parse(this.options.jsBinding ?? 'index.js').name
872
+ const name = this.options.jsBinding ?? 'index.js'
576
873
 
577
874
  const cjs = createCjsBinding(
578
875
  this.config.binaryName,
@@ -581,16 +878,108 @@ class Builder {
581
878
  )
582
879
 
583
880
  try {
584
- const dest = join(this.outputDir, `${name}.js`)
881
+ const dest = join(this.outputDir, name)
585
882
  debug('Writing js binding to:')
586
883
  debug(' %i', dest)
587
884
  await writeFileAsync(dest, cjs, 'utf-8')
588
- this.outputs.push({
885
+ return {
589
886
  kind: 'js',
590
887
  path: dest,
591
- })
888
+ } satisfies Output
592
889
  } catch (e) {
593
890
  throw new Error('Failed to write js binding file', { cause: e })
594
891
  }
595
892
  }
893
+
894
+ private async writeWasiBinding(
895
+ wasiRegisterFunctions: string[],
896
+ distFileName: string | undefined,
897
+ idents: string[],
898
+ ) {
899
+ if (distFileName && wasiRegisterFunctions.length) {
900
+ const { name, dir } = parse(distFileName)
901
+ const bindingPath = join(dir, `${this.config.binaryName}.wasi.cjs`)
902
+ const browserBindingPath = join(
903
+ dir,
904
+ `${this.config.binaryName}.wasi-browser.js`,
905
+ )
906
+ const workerPath = join(dir, 'wasi-worker.mjs')
907
+ const browserWorkerPath = join(dir, 'wasi-worker-browser.mjs')
908
+ const browserEntryPath = join(dir, 'browser.js')
909
+ const exportsCode = idents
910
+ .map(
911
+ (ident) => `module.exports.${ident} = __napiModule.exports.${ident}`,
912
+ )
913
+ .join('\n')
914
+ await writeFileAsync(
915
+ bindingPath,
916
+ createWasiBinding(
917
+ name,
918
+ this.config.packageName,
919
+ wasiRegisterFunctions,
920
+ this.config.wasm?.initialMemory,
921
+ this.config.wasm?.maximumMemory,
922
+ ) +
923
+ exportsCode +
924
+ '\n',
925
+ 'utf8',
926
+ )
927
+ await writeFileAsync(
928
+ browserBindingPath,
929
+ createWasiBrowserBinding(
930
+ name,
931
+ wasiRegisterFunctions,
932
+ this.config.wasm?.initialMemory,
933
+ this.config.wasm?.maximumMemory,
934
+ ) +
935
+ idents
936
+ .map(
937
+ (ident) =>
938
+ `export const ${ident} = __napiModule.exports.${ident}`,
939
+ )
940
+ .join('\n') +
941
+ '\n',
942
+ 'utf8',
943
+ )
944
+ await writeFileAsync(workerPath, WASI_WORKER_TEMPLATE, 'utf8')
945
+ await writeFileAsync(
946
+ browserWorkerPath,
947
+ WASI_WORKER_BROWSER_TEMPLATE,
948
+ 'utf8',
949
+ )
950
+ await writeFileAsync(
951
+ browserEntryPath,
952
+ `export * from '${this.config.packageName}-wasm32-wasi'\n`,
953
+ )
954
+ return [
955
+ {
956
+ kind: 'js',
957
+ path: bindingPath,
958
+ },
959
+ {
960
+ kind: 'js',
961
+ path: browserBindingPath,
962
+ },
963
+ {
964
+ kind: 'js',
965
+ path: workerPath,
966
+ },
967
+ {
968
+ kind: 'js',
969
+ path: browserWorkerPath,
970
+ },
971
+ {
972
+ kind: 'js',
973
+ path: browserEntryPath,
974
+ },
975
+ ] satisfies Output[]
976
+ }
977
+ return []
978
+ }
979
+
980
+ private setEnvIfNotExists(env: string, value: string) {
981
+ if (!process.env[env]) {
982
+ this.envs[env] = value
983
+ }
984
+ }
596
985
  }
@@ -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