@napi-rs/cli 3.0.0-alpha.1 → 3.0.0-alpha.11

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/dist/api/artifacts.js +5 -2
  2. package/dist/api/artifacts.js.map +1 -0
  3. package/dist/api/build.d.ts +1 -1
  4. package/dist/api/build.js +185 -35
  5. package/dist/api/build.js.map +1 -0
  6. package/dist/api/create-npm-dirs.js +1 -0
  7. package/dist/api/create-npm-dirs.js.map +1 -0
  8. package/dist/api/load-wasi-template.d.ts +1 -0
  9. package/dist/api/load-wasi-template.js +60 -0
  10. package/dist/api/load-wasi-template.js.map +1 -0
  11. package/dist/api/new.js +31 -12
  12. package/dist/api/new.js.map +1 -0
  13. package/dist/api/pre-publish.js +1 -0
  14. package/dist/api/pre-publish.js.map +1 -0
  15. package/dist/api/rename.js +1 -0
  16. package/dist/api/rename.js.map +1 -0
  17. package/dist/api/templates/.gitignore.d.ts +1 -1
  18. package/dist/api/templates/.gitignore.js +2 -0
  19. package/dist/api/templates/.gitignore.js.map +1 -0
  20. package/dist/api/templates/.npmignore.js +1 -0
  21. package/dist/api/templates/.npmignore.js.map +1 -0
  22. package/dist/api/templates/build.rs.js +1 -0
  23. package/dist/api/templates/build.rs.js.map +1 -0
  24. package/dist/api/templates/cargo.toml.js +1 -0
  25. package/dist/api/templates/cargo.toml.js.map +1 -0
  26. package/dist/api/templates/ci-template.js +64 -57
  27. package/dist/api/templates/ci-template.js.map +1 -0
  28. package/dist/api/templates/ci.yml.d.ts +1 -1
  29. package/dist/api/templates/ci.yml.js +10 -3
  30. package/dist/api/templates/ci.yml.js.map +1 -0
  31. package/dist/api/templates/index.js +1 -0
  32. package/dist/api/templates/index.js.map +1 -0
  33. package/dist/api/templates/js-binding.d.ts +1 -1
  34. package/dist/api/templates/js-binding.js +278 -109
  35. package/dist/api/templates/js-binding.js.map +1 -0
  36. package/dist/api/templates/lib.rs.js +2 -4
  37. package/dist/api/templates/lib.rs.js.map +1 -0
  38. package/dist/api/templates/package.json.js +30 -28
  39. package/dist/api/templates/package.json.js.map +1 -0
  40. package/dist/api/universalize.js +1 -0
  41. package/dist/api/universalize.js.map +1 -0
  42. package/dist/api/version.js +1 -0
  43. package/dist/api/version.js.map +1 -0
  44. package/dist/api/wasi-worker-template.d.ts +1 -0
  45. package/dist/api/wasi-worker-template.js +53 -0
  46. package/dist/api/wasi-worker-template.js.map +1 -0
  47. package/dist/cli.js +1 -0
  48. package/dist/cli.js.map +1 -0
  49. package/dist/commands/artifacts.js +1 -0
  50. package/dist/commands/artifacts.js.map +1 -0
  51. package/dist/commands/build.js +1 -0
  52. package/dist/commands/build.js.map +1 -0
  53. package/dist/commands/create-npm-dirs.js +1 -0
  54. package/dist/commands/create-npm-dirs.js.map +1 -0
  55. package/dist/commands/help.js +1 -0
  56. package/dist/commands/help.js.map +1 -0
  57. package/dist/commands/new.js +2 -4
  58. package/dist/commands/new.js.map +1 -0
  59. package/dist/commands/pre-publish.js +1 -0
  60. package/dist/commands/pre-publish.js.map +1 -0
  61. package/dist/commands/rename.js +1 -0
  62. package/dist/commands/rename.js.map +1 -0
  63. package/dist/commands/universalize.js +1 -0
  64. package/dist/commands/universalize.js.map +1 -0
  65. package/dist/commands/version.js +1 -0
  66. package/dist/commands/version.js.map +1 -0
  67. package/dist/def/artifacts.js +1 -0
  68. package/dist/def/artifacts.js.map +1 -0
  69. package/dist/def/build.d.ts +24 -0
  70. package/dist/def/build.js +17 -0
  71. package/dist/def/build.js.map +1 -0
  72. package/dist/def/create-npm-dirs.js +1 -0
  73. package/dist/def/create-npm-dirs.js.map +1 -0
  74. package/dist/def/new.js +1 -0
  75. package/dist/def/new.js.map +1 -0
  76. package/dist/def/pre-publish.js +1 -0
  77. package/dist/def/pre-publish.js.map +1 -0
  78. package/dist/def/rename.js +1 -0
  79. package/dist/def/rename.js.map +1 -0
  80. package/dist/def/universalize.js +1 -0
  81. package/dist/def/universalize.js.map +1 -0
  82. package/dist/def/version.js +1 -0
  83. package/dist/def/version.js.map +1 -0
  84. package/dist/index.cjs +3307 -8030
  85. package/dist/index.d.ts +1 -0
  86. package/dist/index.js +2 -0
  87. package/dist/index.js.map +1 -0
  88. package/dist/utils/__tests__/target.spec.js +1 -0
  89. package/dist/utils/__tests__/target.spec.js.map +1 -0
  90. package/dist/utils/__tests__/typegen.spec.js +9 -4
  91. package/dist/utils/__tests__/typegen.spec.js.map +1 -0
  92. package/dist/utils/__tests__/version.spec.js +1 -0
  93. package/dist/utils/__tests__/version.spec.js.map +1 -0
  94. package/dist/utils/cargo.js +1 -0
  95. package/dist/utils/cargo.js.map +1 -0
  96. package/dist/utils/ci.js +10 -0
  97. package/dist/utils/ci.js.map +1 -0
  98. package/dist/utils/config.d.ts +14 -2
  99. package/dist/utils/config.js +4 -3
  100. package/dist/utils/config.js.map +1 -0
  101. package/dist/utils/index.js +1 -0
  102. package/dist/utils/index.js.map +1 -0
  103. package/dist/utils/log.js +1 -0
  104. package/dist/utils/log.js.map +1 -0
  105. package/dist/utils/metadata.js +1 -0
  106. package/dist/utils/metadata.js.map +1 -0
  107. package/dist/utils/misc.js +7 -1
  108. package/dist/utils/misc.js.map +1 -0
  109. package/dist/utils/target.d.ts +6 -5
  110. package/dist/utils/target.js +15 -3
  111. package/dist/utils/target.js.map +1 -0
  112. package/dist/utils/typegen.d.ts +4 -1
  113. package/dist/utils/typegen.js +27 -6
  114. package/dist/utils/typegen.js.map +1 -0
  115. package/dist/utils/version.js +3 -2
  116. package/dist/utils/version.js.map +1 -0
  117. package/package.json +48 -15
  118. package/src/api/artifacts.ts +7 -4
  119. package/src/api/build.ts +278 -44
  120. package/src/api/load-wasi-template.ts +62 -0
  121. package/src/api/new.ts +37 -14
  122. package/src/api/pre-publish.ts +8 -5
  123. package/src/api/templates/.gitignore.ts +1 -0
  124. package/src/api/templates/ci-template.ts +63 -57
  125. package/src/api/templates/ci.yml.ts +9 -6
  126. package/src/api/templates/js-binding.ts +270 -104
  127. package/src/api/templates/lib.rs.ts +1 -4
  128. package/src/api/templates/package.json.ts +31 -27
  129. package/src/api/wasi-worker-template.ts +52 -0
  130. package/src/commands/new.ts +1 -5
  131. package/src/def/build.ts +38 -0
  132. package/src/index.ts +2 -0
  133. package/src/utils/__tests__/__snapshots__/target.spec.ts.md +7 -0
  134. package/src/utils/__tests__/__snapshots__/target.spec.ts.snap +0 -0
  135. package/src/utils/__tests__/__snapshots__/typegen.spec.ts.md +555 -0
  136. package/src/utils/__tests__/__snapshots__/typegen.spec.ts.snap +0 -0
  137. package/src/utils/__tests__/__snapshots__/version.spec.ts.md +7 -7
  138. package/src/utils/__tests__/__snapshots__/version.spec.ts.snap +0 -0
  139. package/src/utils/__tests__/typegen.spec.ts +19 -4
  140. package/src/utils/ci.ts +9 -0
  141. package/src/utils/config.ts +21 -5
  142. package/src/utils/misc.ts +6 -1
  143. package/src/utils/target.ts +22 -10
  144. package/src/utils/typegen.ts +30 -5
  145. package/src/utils/version.ts +1 -1
  146. package/dist/def/commands/artifacts.d.ts +0 -15
  147. package/dist/def/commands/artifacts.js +0 -29
  148. package/dist/def/commands/build.d.ts +0 -53
  149. package/dist/def/commands/build.js +0 -105
  150. package/dist/def/commands/create-npm-dirs.d.ts +0 -15
  151. package/dist/def/commands/create-npm-dirs.js +0 -29
  152. package/dist/def/commands/new.d.ts +0 -27
  153. package/dist/def/commands/new.js +0 -53
  154. package/dist/def/commands/pre-publish.d.ts +0 -23
  155. package/dist/def/commands/pre-publish.js +0 -45
  156. package/dist/def/commands/rename.d.ts +0 -25
  157. package/dist/def/commands/rename.js +0 -49
  158. package/dist/def/commands/universalize.d.ts +0 -13
  159. package/dist/def/commands/universalize.js +0 -25
  160. package/dist/def/commands/version.d.ts +0 -13
  161. package/dist/def/commands/version.js +0 -25
  162. package/dist/def/options/artifacts.d.ts +0 -35
  163. package/dist/def/options/artifacts.js +0 -11
  164. package/dist/def/options/build.d.ts +0 -97
  165. package/dist/def/options/build.js +0 -3
  166. package/dist/def/options/create-npm-dirs.d.ts +0 -35
  167. package/dist/def/options/create-npm-dirs.js +0 -11
  168. package/dist/def/options/new.d.ts +0 -79
  169. package/dist/def/options/new.js +0 -15
  170. package/dist/def/options/pre-publish.d.ts +0 -65
  171. package/dist/def/options/pre-publish.js +0 -13
  172. package/dist/def/options/rename.d.ts +0 -75
  173. package/dist/def/options/rename.js +0 -11
  174. package/dist/def/options/universalize.d.ts +0 -28
  175. package/dist/def/options/universalize.js +0 -10
  176. package/dist/def/options/version.d.ts +0 -28
  177. package/dist/def/options/version.js +0 -10
package/src/api/build.ts CHANGED
@@ -1,7 +1,9 @@
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'
7
9
 
@@ -20,6 +22,7 @@ import {
20
22
  parseMetadata,
21
23
  parseTriple,
22
24
  processTypeDef,
25
+ readFileAsync,
23
26
  readNapiConfig,
24
27
  Target,
25
28
  targetToEnvVar,
@@ -28,11 +31,14 @@ import {
28
31
  writeFileAsync,
29
32
  } from '../utils/index.js'
30
33
 
31
- import { createJsBinding } from './templates/index.js'
34
+ import { createWasiBinding } from './load-wasi-template.js'
35
+ import { createCjsBinding } from './templates/index.js'
36
+ import { WASI_WORKER_TEMPLATE } from './wasi-worker-template.js'
32
37
 
33
38
  const debug = debugFactory('build')
39
+ const require = createRequire(import.meta.url)
34
40
 
35
- type OutputKind = 'js' | 'dts' | 'node' | 'exe'
41
+ type OutputKind = 'js' | 'dts' | 'node' | 'exe' | 'wasm'
36
42
  type Output = {
37
43
  kind: OutputKind
38
44
  path: string
@@ -76,8 +82,8 @@ export async function buildProject(options: BuildOptions) {
76
82
  options.target
77
83
  ? parseTriple(options.target)
78
84
  : process.env.CARGO_BUILD_TARGET
79
- ? parseTriple(process.env.CARGO_BUILD_TARGET)
80
- : getSystemDefaultTarget(),
85
+ ? parseTriple(process.env.CARGO_BUILD_TARGET)
86
+ : getSystemDefaultTarget(),
81
87
  crateDir,
82
88
  resolvePath(options.outputDir ?? crateDir),
83
89
  options.targetDir ??
@@ -138,24 +144,124 @@ class Builder {
138
144
  .setPackage()
139
145
  .setFeatures()
140
146
  .setTarget()
147
+ .pickCrossToolchain()
141
148
  .setEnvs()
142
149
  .setBypassArgs()
143
150
  .exec()
144
151
  }
145
152
 
153
+ private pickCrossToolchain() {
154
+ if (!this.options.useNapiCross) {
155
+ return this
156
+ }
157
+ if (this.options.useCross) {
158
+ debug.warn(
159
+ 'You are trying to use both `--cross` and `--use-napi-cross` options, `--use-cross` will be ignored.',
160
+ )
161
+ }
162
+
163
+ if (this.options.crossCompile) {
164
+ debug.warn(
165
+ 'You are trying to use both `--cross-compile` and `--use-napi-cross` options, `--cross-compile` will be ignored.',
166
+ )
167
+ }
168
+
169
+ try {
170
+ const { version, download } = require('@napi-rs/cross-toolchain')
171
+
172
+ const toolchainPath = join(
173
+ homedir(),
174
+ '.napi-rs',
175
+ 'cross-toolchain',
176
+ version,
177
+ this.target.triple,
178
+ )
179
+ mkdirSync(toolchainPath, { recursive: true })
180
+ if (existsSync(join(toolchainPath, 'package.json'))) {
181
+ debug(`Toolchain ${toolchainPath} exists, skip extracting`)
182
+ } else {
183
+ const tarArchive = download(process.arch, this.target.triple)
184
+ tarArchive.unpack(toolchainPath)
185
+ }
186
+ const upperCaseTarget = targetToEnvVar(this.target.triple)
187
+ const linkerEnv = `CARGO_TARGET_${upperCaseTarget}_LINKER`
188
+ this.envs[linkerEnv] = join(
189
+ toolchainPath,
190
+ 'bin',
191
+ `${this.target.triple}-gcc`,
192
+ )
193
+ if (!process.env.TARGET_SYSROOT) {
194
+ this.envs[`TARGET_SYSROOT`] = join(
195
+ toolchainPath,
196
+ this.target.triple,
197
+ 'sysroot',
198
+ )
199
+ }
200
+ if (!process.env.TARGET_AR) {
201
+ this.envs[`TARGET_AR`] = join(
202
+ toolchainPath,
203
+ 'bin',
204
+ `${this.target.triple}-ar`,
205
+ )
206
+ }
207
+ if (!process.env.TARGET_RANLIB) {
208
+ this.envs[`TARGET_RANLIB`] = join(
209
+ toolchainPath,
210
+ 'bin',
211
+ `${this.target.triple}-ranlib`,
212
+ )
213
+ }
214
+ if (!process.env.TARGET_READELF) {
215
+ this.envs[`TARGET_READELF`] = join(
216
+ toolchainPath,
217
+ 'bin',
218
+ `${this.target.triple}-readelf`,
219
+ )
220
+ }
221
+ if (!process.env.TARGET_C_INCLUDE_PATH) {
222
+ this.envs[`TARGET_C_INCLUDE_PATH`] = join(
223
+ toolchainPath,
224
+ this.target.triple,
225
+ 'sysroot',
226
+ 'usr',
227
+ 'include/',
228
+ )
229
+ }
230
+ if (
231
+ (process.env.CC === 'clang' &&
232
+ (process.env.TARGET_CC === 'clang' || !process.env.TARGET_CC)) ||
233
+ process.env.TARGET_CC === 'clang'
234
+ ) {
235
+ this.envs.C_FLAGS = `--sysroot=${this.envs.TARGET_SYSROOT}`
236
+ }
237
+ } catch (e) {
238
+ debug.warn('Pick cross toolchain failed', e as Error)
239
+ // ignore, do nothing
240
+ }
241
+ return this
242
+ }
243
+
146
244
  private exec() {
147
245
  debug(`Start building crate: ${this.crate.name}`)
148
246
  debug(' %i', `cargo ${this.args.join(' ')}`)
149
247
 
150
248
  const controller = new AbortController()
151
249
 
250
+ const watch = this.options.watch
152
251
  const buildTask = new Promise<void>((resolve, reject) => {
153
- const buildProcess = spawn('cargo', this.args, {
252
+ if (this.options.useCross && this.options.crossCompile) {
253
+ throw new Error(
254
+ '`--use-cross` and `--cross-compile` can not be used together',
255
+ )
256
+ }
257
+ const command =
258
+ process.env.CARGO ?? (this.options.useCross ? 'cross' : 'cargo')
259
+ const buildProcess = spawn(command, this.args, {
154
260
  env: {
155
261
  ...process.env,
156
262
  ...this.envs,
157
263
  },
158
- stdio: 'inherit',
264
+ stdio: watch ? ['inherit', 'inherit', 'pipe'] : 'inherit',
159
265
  cwd: this.cwd,
160
266
  signal: controller.signal,
161
267
  })
@@ -176,6 +282,15 @@ class Builder {
176
282
  }),
177
283
  )
178
284
  })
285
+
286
+ // watch mode only, they are piped through stderr
287
+ buildProcess.stderr?.on('data', (data) => {
288
+ const output = data.toString()
289
+ console.error(output)
290
+ if (/Finished\s(dev|release)/.test(output)) {
291
+ this.postBuild().catch(() => {})
292
+ }
293
+ })
179
294
  })
180
295
 
181
296
  return {
@@ -205,6 +320,7 @@ class Builder {
205
320
  this.crateDir,
206
321
  '--',
207
322
  'cargo',
323
+ 'build',
208
324
  )
209
325
  set = true
210
326
  }
@@ -297,6 +413,10 @@ class Builder {
297
413
  private setEnvs() {
298
414
  // type definition intermediate file
299
415
  this.envs.TYPE_DEF_TMP_PATH = this.getIntermediateTypeFile()
416
+ // WASI register intermediate file
417
+ this.envs.WASI_REGISTER_TMP_PATH = this.getIntermediateWasiRegisterFile()
418
+ // TODO:
419
+ // remove after napi-derive@v3 release
300
420
  this.envs.CARGO_CFG_NAPI_RS_CLI_VERSION = CLI_VERSION
301
421
 
302
422
  // RUSTFLAGS
@@ -320,13 +440,18 @@ class Builder {
320
440
  // END RUSTFLAGS
321
441
 
322
442
  // LINKER
323
- const linker = getTargetLinker(this.target.triple)
324
- if (
325
- linker &&
326
- !process.env.RUSTC_LINKER &&
327
- !process.env[`CARGET_TARGET_${targetToEnvVar(this.target.triple)}_LINKER`]
328
- ) {
329
- this.envs.RUSTC_LINKER = linker
443
+ const linker = this.options.crossCompile
444
+ ? void 0
445
+ : getTargetLinker(this.target.triple)
446
+ // TODO:
447
+ // directly set CARGO_TARGET_<target>_LINKER will cover .cargo/config.toml
448
+ // will detect by cargo config when it becomes stable
449
+ // see: https://github.com/rust-lang/cargo/issues/9301
450
+ const linkerEnv = `CARGO_TARGET_${targetToEnvVar(
451
+ this.target.triple,
452
+ )}_LINKER`
453
+ if (linker && !process.env[linkerEnv]) {
454
+ this.envs[linkerEnv] = linker
330
455
  }
331
456
 
332
457
  if (this.target.platform === 'android') {
@@ -342,18 +467,35 @@ class Builder {
342
467
  const targetArch = this.target.arch === 'arm' ? 'armv7a' : 'aarch64'
343
468
  const targetPlatform =
344
469
  this.target.arch === 'arm' ? 'androideabi24' : 'android24'
470
+ const hostPlatform =
471
+ process.platform === 'darwin'
472
+ ? 'darwin'
473
+ : process.platform === 'win32'
474
+ ? 'windows'
475
+ : 'linux'
345
476
  Object.assign(this.envs, {
346
- CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/${targetArch}-linux-android24-clang`,
347
- CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/${targetArch}-linux-androideabi24-clang`,
348
- CC: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/${targetArch}-linux-${targetPlatform}-clang`,
349
- CXX: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/${targetArch}-linux-${targetPlatform}-clang++`,
350
- AR: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar`,
477
+ CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/${hostPlatform}-x86_64/bin/${targetArch}-linux-android24-clang`,
478
+ CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/${hostPlatform}-x86_64/bin/${targetArch}-linux-androideabi24-clang`,
479
+ CC: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/${hostPlatform}-x86_64/bin/${targetArch}-linux-${targetPlatform}-clang`,
480
+ CXX: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/${hostPlatform}-x86_64/bin/${targetArch}-linux-${targetPlatform}-clang++`,
481
+ AR: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/${hostPlatform}-x86_64/bin/llvm-ar`,
482
+ RANLIB: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/${hostPlatform}-x86_64/bin/llvm-ranlib`,
351
483
  ANDROID_NDK: ANDROID_NDK_LATEST_HOME,
352
- PATH: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin:${process.env.PATH}`,
484
+ PATH: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/${hostPlatform}-x86_64/bin:${process.env.PATH}`,
353
485
  })
354
486
  }
355
487
  // END LINKER
356
488
 
489
+ if (this.target.platform === 'wasi') {
490
+ const emnapi = join(
491
+ require.resolve('emnapi'),
492
+ '..',
493
+ 'lib',
494
+ 'wasm32-wasi-threads',
495
+ )
496
+ this.envs.EMNAPI_LINK_DIR = emnapi
497
+ }
498
+
357
499
  debug('Set envs: ')
358
500
  Object.entries(this.envs).forEach(([k, v]) => {
359
501
  debug(' %i', `${k}=${v}`)
@@ -392,6 +534,10 @@ class Builder {
392
534
  this.args.push('--target-dir', this.options.targetDir)
393
535
  }
394
536
 
537
+ if (this.options.profile) {
538
+ this.args.push('--profile', this.options.profile)
539
+ }
540
+
395
541
  if (this.options.cargoOptions?.length) {
396
542
  this.args.push(...this.options.cargoOptions)
397
543
  }
@@ -410,6 +556,17 @@ class Builder {
410
556
  )
411
557
  }
412
558
 
559
+ private getIntermediateWasiRegisterFile() {
560
+ return join(
561
+ tmpdir(),
562
+ `${this.crate.name}-${createHash('sha256')
563
+ .update(this.crate.manifest_path)
564
+ .update(CLI_VERSION)
565
+ .digest('hex')
566
+ .substring(0, 8)}.napi_wasi_register.tmp`,
567
+ )
568
+ }
569
+
413
570
  private async postBuild() {
414
571
  try {
415
572
  debug(`Try to create output directory:`)
@@ -422,12 +579,39 @@ class Builder {
422
579
  })
423
580
  }
424
581
 
425
- await this.copyArtifact()
582
+ const dest = await this.copyArtifact()
426
583
 
427
584
  // only for cdylib
428
585
  if (this.cdyLibName) {
429
- await this.generateTypeDef()
430
- await this.writeJsBinding()
586
+ const idents = await this.generateTypeDef()
587
+ const intermediateWasiRegisterFile = this.envs.WASI_REGISTER_TMP_PATH
588
+ const wasiRegisterFunctions =
589
+ this.target.arch === 'wasm32'
590
+ ? JSON.parse(
591
+ await readFileAsync(intermediateWasiRegisterFile, 'utf8').catch(
592
+ (err) => {
593
+ console.warn(
594
+ `Read ${colors.yellowBright(
595
+ intermediateWasiRegisterFile,
596
+ )} failed, reason: ${err.message}`,
597
+ )
598
+ return `[]`
599
+ },
600
+ ),
601
+ )
602
+ : []
603
+ const jsOutput = await this.writeJsBinding(idents)
604
+ const wasmOutput = await this.writeWasiBinding(
605
+ wasiRegisterFunctions,
606
+ dest ?? 'index.wasm',
607
+ idents,
608
+ )
609
+ if (jsOutput) {
610
+ this.outputs.push(jsOutput)
611
+ }
612
+ if (wasmOutput) {
613
+ this.outputs.push(wasmOutput)
614
+ }
431
615
  }
432
616
 
433
617
  return this.outputs
@@ -439,12 +623,10 @@ class Builder {
439
623
  return
440
624
  }
441
625
 
442
- const src = join(
443
- this.targetDir,
444
- this.target.triple,
445
- this.options.release ? 'release' : 'debug',
446
- srcName,
447
- )
626
+ const profile =
627
+ this.options.profile ?? (this.options.release ? 'release' : 'debug')
628
+ const src = join(this.targetDir, this.target.triple, profile, srcName)
629
+ debug(`Copy artifact from: [${src}]`)
448
630
  const dest = join(this.outputDir, destName)
449
631
 
450
632
  try {
@@ -459,6 +641,7 @@ class Builder {
459
641
  kind: dest.endsWith('.node') ? 'node' : 'exe',
460
642
  path: dest,
461
643
  })
644
+ return dest
462
645
  } catch (e) {
463
646
  throw new Error('Failed to copy artifact', {
464
647
  cause: e,
@@ -474,8 +657,10 @@ class Builder {
474
657
  this.target.platform === 'darwin'
475
658
  ? `lib${cdyLib}.dylib`
476
659
  : this.target.platform === 'win32'
477
- ? `${cdyLib}.dll`
478
- : `lib${cdyLib}.so`
660
+ ? `${cdyLib}.dll`
661
+ : this.target.platform === 'wasi' || this.target.platform === 'wasm'
662
+ ? `${cdyLib}.wasm`
663
+ : `lib${cdyLib}.so`
479
664
 
480
665
  let destName = this.config.binaryName
481
666
  // add platform suffix to binary name
@@ -484,7 +669,11 @@ class Builder {
484
669
  if (this.options.platform) {
485
670
  destName += `.${this.target.platformArchABI}`
486
671
  }
487
- destName += '.node'
672
+ if (srcName.endsWith('.wasm')) {
673
+ destName += '.wasi-wasm32.wasm'
674
+ } else {
675
+ destName += '.node'
676
+ }
488
677
 
489
678
  return [srcName, destName]
490
679
  } else if (this.binName) {
@@ -499,13 +688,14 @@ class Builder {
499
688
 
500
689
  private async generateTypeDef() {
501
690
  if (!(await fileExists(this.envs.TYPE_DEF_TMP_PATH))) {
502
- return
691
+ return []
503
692
  }
504
693
 
505
694
  const dest = join(this.outputDir, this.options.dts ?? 'index.d.ts')
506
695
 
507
- const dts = await processTypeDef(
696
+ const { dts, exports } = await processTypeDef(
508
697
  this.envs.TYPE_DEF_TMP_PATH,
698
+ this.options.constEnum ?? true,
509
699
  !this.options.noDtsHeader
510
700
  ? this.options.dtsHeader ?? DEFAULT_TYPE_DEF_HEADER
511
701
  : '',
@@ -523,27 +713,71 @@ class Builder {
523
713
  debug.error('Failed to write type def file')
524
714
  debug.error(e as Error)
525
715
  }
716
+
717
+ return exports
526
718
  }
527
719
 
528
- private async writeJsBinding() {
529
- if (!this.options.platform || this.options.noJsBinding) {
720
+ private async writeJsBinding(idents: string[]) {
721
+ if (
722
+ !this.options.platform ||
723
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
724
+ this.options.noJsBinding ||
725
+ idents.length === 0
726
+ ) {
530
727
  return
531
728
  }
532
729
 
533
- const dest = join(this.outputDir, this.options.jsBinding ?? 'index.js')
730
+ const name = parse(this.options.jsBinding ?? 'index.js').name
534
731
 
535
- const js = createJsBinding(this.config.binaryName, this.config.packageName)
732
+ const cjs = createCjsBinding(
733
+ this.config.binaryName,
734
+ this.config.packageName,
735
+ idents,
736
+ )
536
737
 
537
738
  try {
739
+ const dest = join(this.outputDir, `${name}.js`)
538
740
  debug('Writing js binding to:')
539
741
  debug(' %i', dest)
540
- await writeFileAsync(dest, js, 'utf-8')
541
- this.outputs.push({
742
+ await writeFileAsync(dest, cjs, 'utf-8')
743
+ return {
542
744
  kind: 'js',
543
745
  path: dest,
544
- })
746
+ } satisfies Output
545
747
  } catch (e) {
546
748
  throw new Error('Failed to write js binding file', { cause: e })
547
749
  }
548
750
  }
751
+
752
+ private async writeWasiBinding(
753
+ wasiRegisterFunctions: string[],
754
+ distFileName: string | undefined,
755
+ idents: string[],
756
+ ) {
757
+ if (distFileName && wasiRegisterFunctions.length) {
758
+ const { name, dir } = parse(distFileName)
759
+ const newPath = join(dir, `${this.config.binaryName}.wasi.cjs`)
760
+ const workerPath = join(dir, 'wasi-worker.mjs')
761
+ const declareCodes = `const { ${idents.join(
762
+ ', ',
763
+ )} } = __napiModule.exports\n`
764
+ const exportsCode = idents
765
+ .map((ident) => `module.exports.${ident} = ${ident}`)
766
+ .join(',\n')
767
+ await writeFileAsync(
768
+ newPath,
769
+ createWasiBinding(name, wasiRegisterFunctions) +
770
+ declareCodes +
771
+ exportsCode +
772
+ '\n',
773
+ 'utf8',
774
+ )
775
+ await writeFileAsync(workerPath, WASI_WORKER_TEMPLATE, 'utf8')
776
+ return {
777
+ kind: 'wasm',
778
+ path: newPath,
779
+ } satisfies Output
780
+ }
781
+ return null
782
+ }
549
783
  }
@@ -0,0 +1,62 @@
1
+ export const createWasiBinding = (
2
+ wasmFileName: string,
3
+ wasiRegisterFunctions: string[],
4
+ ) => `/* eslint-disable */
5
+ /* prettier-ignore */
6
+
7
+ /* auto-generated by NAPI-RS */
8
+
9
+ const __nodeFs= require('node:fs')
10
+ const __nodePath = require('node:path')
11
+ const { WASI: __nodeWASI } = require('node:wasi')
12
+ const { Worker } = require('node:worker_threads')
13
+
14
+ const { instantiateNapiModuleSync: __emnapiInstantiateNapiModuleSync } = require('@emnapi/core')
15
+ const { getDefaultContext: __emnapiGetDefaultContext } = require('@emnapi/runtime')
16
+
17
+ const __wasi = new __nodeWASI({
18
+ version: 'preview1',
19
+ env: process.env,
20
+ preopens: {
21
+ '/': __dirname,
22
+ }
23
+ })
24
+
25
+ const __emnapiContext = __emnapiGetDefaultContext()
26
+
27
+ const __sharedMemory = new WebAssembly.Memory({
28
+ initial: 1024,
29
+ maximum: 10240,
30
+ shared: true,
31
+ })
32
+
33
+ const { instance: __napiInstance, module: __wasiModule, napiModule: __napiModule } = __emnapiInstantiateNapiModuleSync(__nodeFs.readFileSync(__nodePath.join(__dirname, '${wasmFileName}.wasm')), {
34
+ context: __emnapiContext,
35
+ asyncWorkPoolSize: 4,
36
+ wasi: __wasi,
37
+ onCreateWorker() {
38
+ return new Worker(__nodePath.join(__dirname, 'wasi-worker.mjs'), {
39
+ env: process.env,
40
+ execArgv: ['--experimental-wasi-unstable-preview1'],
41
+ })
42
+ },
43
+ overwriteImports(importObject) {
44
+ importObject.env = {
45
+ ...importObject.env,
46
+ ...importObject.napi,
47
+ ...importObject.emnapi,
48
+ memory: __sharedMemory,
49
+ }
50
+ return importObject
51
+ },
52
+ beforeInit({ instance }) {
53
+ __napi_rs_initialize_modules(instance)
54
+ }
55
+ })
56
+
57
+ function __napi_rs_initialize_modules(__napiInstance) {
58
+ ${wasiRegisterFunctions
59
+ .map((name) => ` __napiInstance.exports['${name}']()`)
60
+ .join('\n')}
61
+ }
62
+ `
package/src/api/new.ts CHANGED
@@ -10,6 +10,8 @@ import {
10
10
  debugFactory,
11
11
  DEFAULT_TARGETS,
12
12
  mkdirAsync,
13
+ readdirAsync,
14
+ statAsync,
13
15
  writeFileAsync,
14
16
  } from '../utils/index.js'
15
17
  import { napiEngineRequirement } from '../utils/version.js'
@@ -69,21 +71,45 @@ export async function newProject(userOptions: RawNewOptions) {
69
71
 
70
72
  const outputs = generateFiles(options)
71
73
 
72
- try {
73
- debug(`Try to create target directory: ${options.path}`)
74
- if (!options.dryRun) {
75
- await mkdirAsync(options.path, { recursive: true })
76
- }
77
- } catch (e) {
78
- throw new Error(`Failed to create target directory: ${options.path}`, {
79
- cause: e,
80
- })
81
- }
74
+ await ensurePath(options.path, options.dryRun)
82
75
 
83
76
  await dumpOutputs(outputs, options.dryRun)
84
77
  debug(`Project created at: ${options.path}`)
85
78
  }
86
79
 
80
+ async function ensurePath(path: string, dryRun = false) {
81
+ const stat = await statAsync(path, {}).catch(() => undefined)
82
+
83
+ // file descriptor exists
84
+ if (stat) {
85
+ if (stat.isFile()) {
86
+ throw new Error(
87
+ `Path ${path} for creating new napi-rs project already exists and it's not a directory.`,
88
+ )
89
+ } else if (stat.isDirectory()) {
90
+ const files = await readdirAsync(path)
91
+ if (files.length) {
92
+ throw new Error(
93
+ `Path ${path} for creating new napi-rs project already exists and it's not empty.`,
94
+ )
95
+ }
96
+ }
97
+ }
98
+
99
+ if (!dryRun) {
100
+ try {
101
+ debug(`Try to create target directory: ${path}`)
102
+ if (!dryRun) {
103
+ await mkdirAsync(path, { recursive: true })
104
+ }
105
+ } catch (e) {
106
+ throw new Error(`Failed to create target directory: ${path}`, {
107
+ cause: e,
108
+ })
109
+ }
110
+ }
111
+ }
112
+
87
113
  function generateFiles(options: NewOptions): Output[] {
88
114
  return [
89
115
  generateCargoToml,
@@ -157,10 +183,7 @@ function generateGithubWorkflow(options: NewOptions): Output | null {
157
183
 
158
184
  return {
159
185
  target: './.github/workflows/ci.yml',
160
- content: createGithubActionsCIYml(
161
- getBinaryName(options.name),
162
- options.targets,
163
- ),
186
+ content: createGithubActionsCIYml(options.targets),
164
187
  }
165
188
  }
166
189
 
@@ -129,11 +129,14 @@ export async function prePublish(userOptions: PrePublishOptions) {
129
129
  if (!options.dryRun) {
130
130
  await version(userOptions)
131
131
  await updatePackageJson(packageJsonPath, {
132
- optionalDependencies: targets.reduce((deps, target) => {
133
- deps[`${packageName}-${target.platformArchABI}`] = packageJson.version
134
-
135
- return deps
136
- }, {} as Record<string, string>),
132
+ optionalDependencies: targets.reduce(
133
+ (deps, target) => {
134
+ deps[`${packageName}-${target.platformArchABI}`] = packageJson.version
135
+
136
+ return deps
137
+ },
138
+ {} as Record<string, string>,
139
+ ),
137
140
  })
138
141
  }
139
142
 
@@ -195,4 +195,5 @@ Cargo.lock
195
195
  !.yarn/versions
196
196
 
197
197
  *.node
198
+ *.wasm
198
199
  `