@napi-rs/cli 3.0.0-alpha.9 → 3.0.0-alpha.90

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 (163) hide show
  1. package/cli.mjs +1 -1
  2. package/dist/api/__tests__/new.spec.d.ts +1 -0
  3. package/dist/api/__tests__/new.spec.js +283 -0
  4. package/dist/api/__tests__/new.spec.js.map +1 -0
  5. package/dist/api/artifacts.js +28 -7
  6. package/dist/api/artifacts.js.map +1 -1
  7. package/dist/api/build.d.ts +1 -1
  8. package/dist/api/build.js +324 -115
  9. package/dist/api/build.js.map +1 -1
  10. package/dist/api/create-npm-dirs.js +46 -9
  11. package/dist/api/create-npm-dirs.js.map +1 -1
  12. package/dist/api/new.js +312 -107
  13. package/dist/api/new.js.map +1 -1
  14. package/dist/api/pre-publish.js +9 -7
  15. package/dist/api/pre-publish.js.map +1 -1
  16. package/dist/api/rename.js +74 -12
  17. package/dist/api/rename.js.map +1 -1
  18. package/dist/api/templates/cargo.toml.js +1 -0
  19. package/dist/api/templates/cargo.toml.js.map +1 -1
  20. package/dist/api/templates/ci-template.d.ts +3 -1
  21. package/dist/api/templates/ci-template.js +140 -125
  22. package/dist/api/templates/ci-template.js.map +1 -1
  23. package/dist/api/templates/ci.yml.d.ts +3 -1
  24. package/dist/api/templates/ci.yml.js +21 -5
  25. package/dist/api/templates/ci.yml.js.map +1 -1
  26. package/dist/api/templates/js-binding.d.ts +1 -0
  27. package/dist/api/templates/js-binding.js +142 -222
  28. package/dist/api/templates/js-binding.js.map +1 -1
  29. package/dist/api/templates/load-wasi-template.d.ts +2 -0
  30. package/dist/api/templates/load-wasi-template.js +168 -0
  31. package/dist/api/templates/load-wasi-template.js.map +1 -0
  32. package/dist/api/templates/package.json.d.ts +9 -2
  33. package/dist/api/templates/package.json.js +26 -4
  34. package/dist/api/templates/package.json.js.map +1 -1
  35. package/dist/api/templates/wasi-worker-template.d.ts +2 -0
  36. package/dist/api/templates/wasi-worker-template.js +122 -0
  37. package/dist/api/templates/wasi-worker-template.js.map +1 -0
  38. package/dist/api/universalize.js +5 -5
  39. package/dist/api/universalize.js.map +1 -1
  40. package/dist/api/version.js +2 -2
  41. package/dist/api/version.js.map +1 -1
  42. package/dist/commands/artifacts.js +2 -2
  43. package/dist/commands/artifacts.js.map +1 -1
  44. package/dist/commands/build.js +1 -1
  45. package/dist/commands/build.js.map +1 -1
  46. package/dist/commands/new.js +34 -34
  47. package/dist/commands/new.js.map +1 -1
  48. package/dist/def/artifacts.d.ts +21 -1
  49. package/dist/def/artifacts.js +10 -2
  50. package/dist/def/artifacts.js.map +1 -1
  51. package/dist/def/build.d.ts +149 -4
  52. package/dist/def/build.js +26 -4
  53. package/dist/def/build.js.map +1 -1
  54. package/dist/def/create-npm-dirs.d.ts +10 -0
  55. package/dist/def/create-npm-dirs.js +4 -0
  56. package/dist/def/create-npm-dirs.js.map +1 -1
  57. package/dist/def/new.d.ts +25 -7
  58. package/dist/def/new.js +11 -1
  59. package/dist/def/new.js.map +1 -1
  60. package/dist/def/pre-publish.d.ts +12 -2
  61. package/dist/def/pre-publish.js +7 -3
  62. package/dist/def/pre-publish.js.map +1 -1
  63. package/dist/def/rename.d.ts +16 -6
  64. package/dist/def/rename.js +5 -1
  65. package/dist/def/rename.js.map +1 -1
  66. package/dist/def/universalize.d.ts +10 -0
  67. package/dist/def/universalize.js +4 -0
  68. package/dist/def/universalize.js.map +1 -1
  69. package/dist/def/version.d.ts +10 -0
  70. package/dist/def/version.js +4 -0
  71. package/dist/def/version.js.map +1 -1
  72. package/dist/index.cjs +8743 -14849
  73. package/dist/utils/__tests__/config.spec.d.ts +1 -0
  74. package/dist/utils/__tests__/config.spec.js +63 -0
  75. package/dist/utils/__tests__/config.spec.js.map +1 -0
  76. package/dist/utils/cargo.js +2 -2
  77. package/dist/utils/cargo.js.map +1 -1
  78. package/dist/utils/config.d.ts +48 -4
  79. package/dist/utils/config.js +36 -4
  80. package/dist/utils/config.js.map +1 -1
  81. package/dist/utils/index.d.ts +1 -1
  82. package/dist/utils/index.js +1 -1
  83. package/dist/utils/index.js.map +1 -1
  84. package/dist/utils/log.js +1 -1
  85. package/dist/utils/log.js.map +1 -1
  86. package/dist/utils/metadata.d.ts +13 -1
  87. package/dist/utils/metadata.js +31 -11
  88. package/dist/utils/metadata.js.map +1 -1
  89. package/dist/utils/misc.d.ts +3 -3
  90. package/dist/utils/misc.js +13 -11
  91. package/dist/utils/misc.js.map +1 -1
  92. package/dist/utils/read-config.d.ts +7 -0
  93. package/dist/utils/read-config.js +8 -0
  94. package/dist/utils/read-config.js.map +1 -0
  95. package/dist/utils/target.d.ts +7 -5
  96. package/dist/utils/target.js +17 -3
  97. package/dist/utils/target.js.map +1 -1
  98. package/dist/utils/typegen.d.ts +1 -1
  99. package/dist/utils/typegen.js +63 -32
  100. package/dist/utils/typegen.js.map +1 -1
  101. package/dist/utils/version.d.ts +2 -1
  102. package/dist/utils/version.js +10 -8
  103. package/dist/utils/version.js.map +1 -1
  104. package/package.json +34 -32
  105. package/src/api/__tests__/new.spec.ts +429 -0
  106. package/src/api/artifacts.ts +81 -9
  107. package/src/api/build.ts +498 -149
  108. package/src/api/create-npm-dirs.ts +57 -9
  109. package/src/api/new.ts +389 -122
  110. package/src/api/pre-publish.ts +13 -7
  111. package/src/api/rename.ts +103 -17
  112. package/src/api/templates/cargo.toml.ts +1 -0
  113. package/src/api/templates/ci-template.ts +150 -125
  114. package/src/api/templates/ci.yml.ts +27 -6
  115. package/src/api/templates/js-binding.ts +148 -220
  116. package/src/api/templates/load-wasi-template.ts +182 -0
  117. package/src/api/templates/package.json.ts +47 -5
  118. package/src/api/templates/wasi-worker-template.ts +122 -0
  119. package/src/api/universalize.ts +13 -8
  120. package/src/api/version.ts +5 -2
  121. package/src/commands/artifacts.ts +2 -2
  122. package/src/commands/build.ts +1 -1
  123. package/src/commands/new.ts +46 -45
  124. package/src/def/artifacts.ts +22 -3
  125. package/src/def/build.ts +55 -8
  126. package/src/def/create-npm-dirs.ts +9 -0
  127. package/src/def/new.ts +28 -3
  128. package/src/def/pre-publish.ts +12 -3
  129. package/src/def/rename.ts +11 -2
  130. package/src/def/universalize.ts +9 -0
  131. package/src/def/version.ts +9 -0
  132. package/src/utils/__tests__/__fixtures__/napi_type_def +130 -130
  133. package/src/utils/__tests__/__snapshots__/config.spec.ts.md +97 -0
  134. package/src/utils/__tests__/__snapshots__/config.spec.ts.snap +0 -0
  135. package/src/utils/__tests__/__snapshots__/target.spec.ts.md +28 -0
  136. package/src/utils/__tests__/__snapshots__/target.spec.ts.snap +0 -0
  137. package/src/utils/__tests__/__snapshots__/typegen.spec.ts.md +316 -332
  138. package/src/utils/__tests__/__snapshots__/typegen.spec.ts.snap +0 -0
  139. package/src/utils/__tests__/__snapshots__/version.spec.ts.md +8 -7
  140. package/src/utils/__tests__/__snapshots__/version.spec.ts.snap +0 -0
  141. package/src/utils/__tests__/config.spec.ts +81 -0
  142. package/src/utils/cargo.ts +2 -2
  143. package/src/utils/config.ts +110 -9
  144. package/src/utils/index.ts +1 -1
  145. package/src/utils/log.ts +1 -1
  146. package/src/utils/metadata.ts +52 -14
  147. package/src/utils/misc.ts +21 -12
  148. package/src/utils/read-config.ts +20 -0
  149. package/src/utils/target.ts +22 -4
  150. package/src/utils/typegen.ts +87 -52
  151. package/src/utils/version.ts +10 -8
  152. package/dist/api/load-wasi-template.d.ts +0 -1
  153. package/dist/api/load-wasi-template.js +0 -64
  154. package/dist/api/load-wasi-template.js.map +0 -1
  155. package/dist/api/wasi-worker-template.d.ts +0 -1
  156. package/dist/api/wasi-worker-template.js +0 -53
  157. package/dist/api/wasi-worker-template.js.map +0 -1
  158. package/dist/utils/ci.d.ts +0 -11
  159. package/dist/utils/ci.js +0 -96
  160. package/dist/utils/ci.js.map +0 -1
  161. package/src/api/load-wasi-template.ts +0 -66
  162. package/src/api/wasi-worker-template.ts +0 -52
  163. package/src/utils/ci.ts +0 -111
package/dist/api/build.js CHANGED
@@ -1,22 +1,28 @@
1
1
  import { spawn } from 'node:child_process';
2
2
  import { createHash } from 'node:crypto';
3
+ import { existsSync, mkdirSync, rmSync } from 'node:fs';
3
4
  import { createRequire } from 'node:module';
4
- import { tmpdir } from 'node:os';
5
+ import { homedir } from 'node:os';
5
6
  import { parse, join, resolve } from 'node:path';
6
7
  import * as colors from 'colorette';
7
- import { CLI_VERSION, copyFileAsync, debugFactory, DEFAULT_TYPE_DEF_HEADER, fileExists, getSystemDefaultTarget, getTargetLinker, mkdirAsync, parseMetadata, parseTriple, processTypeDef, readFileAsync, readNapiConfig, targetToEnvVar, tryInstallCargoBinary, unlinkAsync, writeFileAsync, } from '../utils/index.js';
8
- import { createWasiBinding } from './load-wasi-template.js';
9
- import { createCjsBinding } from './templates/index.js';
10
- import { WASI_WORKER_TEMPLATE } from './wasi-worker-template.js';
8
+ import { include as setjmpInclude, lib as setjmpLib } from 'wasm-sjlj';
9
+ import { CLI_VERSION, copyFileAsync, debugFactory, DEFAULT_TYPE_DEF_HEADER, fileExists, getSystemDefaultTarget, getTargetLinker, mkdirAsync, parseMetadata, parseTriple, processTypeDef, readFileAsync, readNapiConfig, targetToEnvVar, tryInstallCargoBinary, unlinkAsync, writeFileAsync, dirExistsAsync, readdirAsync, } from '../utils/index.js';
10
+ import { createCjsBinding, createEsmBinding } from './templates/index.js';
11
+ import { createWasiBinding, createWasiBrowserBinding, } from './templates/load-wasi-template.js';
12
+ import { createWasiBrowserWorkerBinding, WASI_WORKER_TEMPLATE, } from './templates/wasi-worker-template.js';
11
13
  const debug = debugFactory('build');
12
14
  const require = createRequire(import.meta.url);
13
- export async function buildProject(options) {
14
- debug('napi build command receive options: %O', options);
15
- const cwd = options.cwd ?? process.cwd();
16
- const resolvePath = (...paths) => resolve(cwd, ...paths);
15
+ export async function buildProject(rawOptions) {
16
+ debug('napi build command receive options: %O', rawOptions);
17
+ const options = {
18
+ dtsCache: true,
19
+ ...rawOptions,
20
+ cwd: rawOptions.cwd ?? process.cwd(),
21
+ };
22
+ const resolvePath = (...paths) => resolve(options.cwd, ...paths);
17
23
  const manifestPath = resolvePath(options.manifestPath ?? 'Cargo.toml');
18
- const metadata = parseMetadata(manifestPath);
19
- const pkg = metadata.packages.find((p) => {
24
+ const metadata = await parseMetadata(manifestPath);
25
+ const crate = metadata.packages.find((p) => {
20
26
  // package with given name
21
27
  if (options.package) {
22
28
  return p.name === options.package;
@@ -25,40 +31,54 @@ export async function buildProject(options) {
25
31
  return p.manifest_path === manifestPath;
26
32
  }
27
33
  });
28
- if (!pkg) {
34
+ if (!crate) {
29
35
  throw new Error('Unable to find crate to build. It seems you are trying to build a crate in a workspace, try using `--package` option to specify the package to build.');
30
36
  }
31
- const crateDir = parse(pkg.manifest_path).dir;
32
- const builder = new Builder(options, pkg, cwd, options.target
33
- ? parseTriple(options.target)
34
- : process.env.CARGO_BUILD_TARGET
35
- ? parseTriple(process.env.CARGO_BUILD_TARGET)
36
- : getSystemDefaultTarget(), crateDir, resolvePath(options.outputDir ?? crateDir), options.targetDir ??
37
- process.env.CARGO_BUILD_TARGET_DIR ??
38
- metadata.target_directory, await readNapiConfig(resolvePath(options.packageJsonPath ?? 'package.json')));
37
+ const config = await readNapiConfig(resolvePath(options.configPath ?? options.packageJsonPath ?? 'package.json'), options.configPath ? resolvePath(options.configPath) : undefined);
38
+ const builder = new Builder(metadata, crate, config, options);
39
39
  return builder.build();
40
40
  }
41
41
  class Builder {
42
- options;
42
+ metadata;
43
43
  crate;
44
- cwd;
45
- target;
46
- crateDir;
47
- outputDir;
48
- targetDir;
49
44
  config;
45
+ options;
50
46
  args = [];
51
47
  envs = {};
52
48
  outputs = [];
53
- constructor(options, crate, cwd, target, crateDir, outputDir, targetDir, config) {
54
- this.options = options;
49
+ target;
50
+ crateDir;
51
+ outputDir;
52
+ targetDir;
53
+ enableTypeDef = false;
54
+ constructor(metadata, crate, config, options) {
55
+ this.metadata = metadata;
55
56
  this.crate = crate;
56
- this.cwd = cwd;
57
- this.target = target;
58
- this.crateDir = crateDir;
59
- this.outputDir = outputDir;
60
- this.targetDir = targetDir;
61
57
  this.config = config;
58
+ this.options = options;
59
+ this.target = options.target
60
+ ? parseTriple(options.target)
61
+ : process.env.CARGO_BUILD_TARGET
62
+ ? parseTriple(process.env.CARGO_BUILD_TARGET)
63
+ : getSystemDefaultTarget();
64
+ this.crateDir = parse(crate.manifest_path).dir;
65
+ this.outputDir = resolve(this.options.cwd, options.outputDir ?? this.crateDir);
66
+ this.targetDir =
67
+ options.targetDir ??
68
+ process.env.CARGO_BUILD_TARGET_DIR ??
69
+ metadata.target_directory;
70
+ this.enableTypeDef = this.crate.dependencies.some((dep) => dep.name === 'napi-derive' &&
71
+ (dep.uses_default_features || dep.features.includes('type-def')));
72
+ if (!this.enableTypeDef) {
73
+ const requirementWarning = '`napi-derive` crate is not used or `type-def` feature is not enabled for `napi-derive` crate';
74
+ debug.warn(`${requirementWarning}. Will skip binding generation for \`.node\`, \`.wasi\` and \`.d.ts\` files.`);
75
+ if (this.options.dts ||
76
+ this.options.dtsHeader ||
77
+ this.config.dtsHeader ||
78
+ this.config.dtsHeaderFile) {
79
+ debug.warn(`${requirementWarning}. \`dts\` related options are enabled but will be ignored.`);
80
+ }
81
+ }
62
82
  }
63
83
  get cdyLibName() {
64
84
  return this.crate.targets.find((t) => t.crate_types.includes('cdylib'))
@@ -85,24 +105,100 @@ class Builder {
85
105
  .setPackage()
86
106
  .setFeatures()
87
107
  .setTarget()
108
+ .pickCrossToolchain()
88
109
  .setEnvs()
89
110
  .setBypassArgs()
90
111
  .exec();
91
112
  }
113
+ pickCrossToolchain() {
114
+ if (!this.options.useNapiCross) {
115
+ return this;
116
+ }
117
+ if (this.options.useCross) {
118
+ debug.warn('You are trying to use both `--cross` and `--use-napi-cross` options, `--use-cross` will be ignored.');
119
+ }
120
+ if (this.options.crossCompile) {
121
+ debug.warn('You are trying to use both `--cross-compile` and `--use-napi-cross` options, `--cross-compile` will be ignored.');
122
+ }
123
+ try {
124
+ const { version, download } = require('@napi-rs/cross-toolchain');
125
+ const alias = {
126
+ 's390x-unknown-linux-gnu': 's390x-ibm-linux-gnu',
127
+ };
128
+ const toolchainPath = join(homedir(), '.napi-rs', 'cross-toolchain', version, this.target.triple);
129
+ mkdirSync(toolchainPath, { recursive: true });
130
+ if (existsSync(join(toolchainPath, 'package.json'))) {
131
+ debug(`Toolchain ${toolchainPath} exists, skip extracting`);
132
+ }
133
+ else {
134
+ const tarArchive = download(process.arch, this.target.triple);
135
+ tarArchive.unpack(toolchainPath);
136
+ }
137
+ const upperCaseTarget = targetToEnvVar(this.target.triple);
138
+ const crossTargetName = alias[this.target.triple] ?? this.target.triple;
139
+ const linkerEnv = `CARGO_TARGET_${upperCaseTarget}_LINKER`;
140
+ this.envs[linkerEnv] = join(toolchainPath, 'bin', `${crossTargetName}-gcc`);
141
+ if (!process.env.TARGET_SYSROOT) {
142
+ this.envs[`TARGET_SYSROOT`] = join(toolchainPath, crossTargetName, 'sysroot');
143
+ }
144
+ if (!process.env.TARGET_AR) {
145
+ this.envs[`TARGET_AR`] = join(toolchainPath, 'bin', `${crossTargetName}-ar`);
146
+ }
147
+ if (!process.env.TARGET_RANLIB) {
148
+ this.envs[`TARGET_RANLIB`] = join(toolchainPath, 'bin', `${crossTargetName}-ranlib`);
149
+ }
150
+ if (!process.env.TARGET_READELF) {
151
+ this.envs[`TARGET_READELF`] = join(toolchainPath, 'bin', `${crossTargetName}-readelf`);
152
+ }
153
+ if (!process.env.TARGET_C_INCLUDE_PATH) {
154
+ this.envs[`TARGET_C_INCLUDE_PATH`] = join(toolchainPath, crossTargetName, 'sysroot', 'usr', 'include/');
155
+ }
156
+ if (!process.env.CC && !process.env.TARGET_CC) {
157
+ this.envs[`CC`] = join(toolchainPath, 'bin', `${crossTargetName}-gcc`);
158
+ this.envs[`TARGET_CC`] = join(toolchainPath, 'bin', `${crossTargetName}-gcc`);
159
+ }
160
+ if (!process.env.CXX && !process.env.TARGET_CXX) {
161
+ this.envs[`CXX`] = join(toolchainPath, 'bin', `${crossTargetName}-g++`);
162
+ this.envs[`TARGET_CXX`] = join(toolchainPath, 'bin', `${crossTargetName}-g++`);
163
+ }
164
+ if ((process.env.CC?.startsWith('clang') &&
165
+ (process.env.TARGET_CC?.startsWith('clang') ||
166
+ !process.env.TARGET_CC)) ||
167
+ process.env.TARGET_CC?.startsWith('clang')) {
168
+ const CFLAGS = process.env.CFLAGS ?? '';
169
+ this.envs.CFLAGS = `--sysroot=${this.envs.TARGET_SYSROOT} --gcc-toolchain=${toolchainPath} ${CFLAGS}`;
170
+ }
171
+ if ((process.env.CXX?.startsWith('clang++') &&
172
+ (process.env.TARGET_CXX?.startsWith('clang++') ||
173
+ !process.env.TARGET_CXX)) ||
174
+ process.env.TARGET_CXX?.startsWith('clang++')) {
175
+ const CXXFLAGS = process.env.CXXFLAGS ?? '';
176
+ this.envs.CXXFLAGS = `--sysroot=${this.envs.TARGET_SYSROOT} --gcc-toolchain=${toolchainPath} ${CXXFLAGS} ${CXXFLAGS}`;
177
+ }
178
+ this.envs.PATH = this.envs.PATH
179
+ ? `${toolchainPath}/bin:${this.envs.PATH}:${process.env.PATH}`
180
+ : `${toolchainPath}/bin:${process.env.PATH}`;
181
+ }
182
+ catch (e) {
183
+ debug.warn('Pick cross toolchain failed', e);
184
+ // ignore, do nothing
185
+ }
186
+ return this;
187
+ }
92
188
  exec() {
93
189
  debug(`Start building crate: ${this.crate.name}`);
94
190
  debug(' %i', `cargo ${this.args.join(' ')}`);
95
191
  const controller = new AbortController();
96
192
  const watch = this.options.watch;
97
193
  const buildTask = new Promise((resolve, reject) => {
194
+ if (this.options.useCross && this.options.crossCompile) {
195
+ throw new Error('`--use-cross` and `--cross-compile` can not be used together');
196
+ }
98
197
  const command = process.env.CARGO ?? (this.options.useCross ? 'cross' : 'cargo');
99
198
  const buildProcess = spawn(command, this.args, {
100
- env: {
101
- ...process.env,
102
- ...this.envs,
103
- },
199
+ env: { ...process.env, ...this.envs },
104
200
  stdio: watch ? ['inherit', 'inherit', 'pipe'] : 'inherit',
105
- cwd: this.cwd,
201
+ cwd: this.options.cwd,
106
202
  signal: controller.signal,
107
203
  });
108
204
  buildProcess.once('exit', (code) => {
@@ -115,15 +211,13 @@ class Builder {
115
211
  }
116
212
  });
117
213
  buildProcess.once('error', (e) => {
118
- reject(new Error(`Build failed with error: ${e.message}`, {
119
- cause: e,
120
- }));
214
+ reject(new Error(`Build failed with error: ${e.message}`, { cause: e }));
121
215
  });
122
216
  // watch mode only, they are piped through stderr
123
217
  buildProcess.stderr?.on('data', (data) => {
124
218
  const output = data.toString();
125
219
  console.error(output);
126
- if (/Finished\s(dev|release)/.test(output)) {
220
+ if (/Finished\s(`dev`|`release`)/.test(output)) {
127
221
  this.postBuild().catch(() => { });
128
222
  }
129
223
  });
@@ -219,13 +313,12 @@ class Builder {
219
313
  return this;
220
314
  }
221
315
  setEnvs() {
222
- // type definition intermediate file
223
- this.envs.TYPE_DEF_TMP_PATH = this.getIntermediateTypeFile();
224
- // WASI register intermediate file
225
- this.envs.WASI_REGISTER_TMP_PATH = this.getIntermediateWasiRegisterFile();
226
- // TODO:
227
- // remove after napi-derive@v3 release
228
- this.envs.CARGO_CFG_NAPI_RS_CLI_VERSION = CLI_VERSION;
316
+ // TYPE DEF
317
+ if (this.enableTypeDef) {
318
+ this.envs.NAPI_TYPE_DEF_TMP_FOLDER =
319
+ this.generateIntermediateTypeDefFolder();
320
+ this.setForceBuildEnvs(this.envs.NAPI_TYPE_DEF_TMP_FOLDER);
321
+ }
229
322
  // RUSTFLAGS
230
323
  let rustflags = process.env.RUSTFLAGS ?? process.env.CARGO_BUILD_RUSTFLAGS ?? '';
231
324
  if (this.target.abi?.includes('musl') &&
@@ -248,7 +341,7 @@ class Builder {
248
341
  // will detect by cargo config when it becomes stable
249
342
  // see: https://github.com/rust-lang/cargo/issues/9301
250
343
  const linkerEnv = `CARGO_TARGET_${targetToEnvVar(this.target.triple)}_LINKER`;
251
- if (linker && !process.env[linkerEnv]) {
344
+ if (linker && !process.env[linkerEnv] && !this.envs[linkerEnv]) {
252
345
  this.envs[linkerEnv] = linker;
253
346
  }
254
347
  if (this.target.platform === 'android') {
@@ -266,10 +359,10 @@ class Builder {
266
359
  Object.assign(this.envs, {
267
360
  CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/${hostPlatform}-x86_64/bin/${targetArch}-linux-android24-clang`,
268
361
  CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/${hostPlatform}-x86_64/bin/${targetArch}-linux-androideabi24-clang`,
269
- CC: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/${hostPlatform}-x86_64/bin/${targetArch}-linux-${targetPlatform}-clang`,
270
- CXX: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/${hostPlatform}-x86_64/bin/${targetArch}-linux-${targetPlatform}-clang++`,
271
- AR: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/${hostPlatform}-x86_64/bin/llvm-ar`,
272
- RANLIB: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/${hostPlatform}-x86_64/bin/llvm-ranlib`,
362
+ TARGET_CC: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/${hostPlatform}-x86_64/bin/${targetArch}-linux-${targetPlatform}-clang`,
363
+ TARGET_CXX: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/${hostPlatform}-x86_64/bin/${targetArch}-linux-${targetPlatform}-clang++`,
364
+ TARGET_AR: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/${hostPlatform}-x86_64/bin/llvm-ar`,
365
+ TARGET_RANLIB: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/${hostPlatform}-x86_64/bin/llvm-ranlib`,
273
366
  ANDROID_NDK: ANDROID_NDK_LATEST_HOME,
274
367
  PATH: `${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/${hostPlatform}-x86_64/bin:${process.env.PATH}`,
275
368
  });
@@ -278,6 +371,21 @@ class Builder {
278
371
  if (this.target.platform === 'wasi') {
279
372
  const emnapi = join(require.resolve('emnapi'), '..', 'lib', 'wasm32-wasi-threads');
280
373
  this.envs.EMNAPI_LINK_DIR = emnapi;
374
+ this.envs.SETJMP_LINK_DIR = setjmpLib;
375
+ const { WASI_SDK_PATH } = process.env;
376
+ if (WASI_SDK_PATH && existsSync(WASI_SDK_PATH)) {
377
+ this.envs.CARGO_TARGET_WASM32_WASI_PREVIEW1_THREADS_LINKER = join(WASI_SDK_PATH, 'bin', 'wasm-ld');
378
+ this.envs.CARGO_TARGET_WASM32_WASIP1_LINKER = join(WASI_SDK_PATH, 'bin', 'wasm-ld');
379
+ this.envs.CARGO_TARGET_WASM32_WASIP1_THREADS_LINKER = join(WASI_SDK_PATH, 'bin', 'wasm-ld');
380
+ this.envs.CARGO_TARGET_WASM32_WASIP2_LINKER = join(WASI_SDK_PATH, 'bin', 'wasm-ld');
381
+ this.setEnvIfNotExists('CC', join(WASI_SDK_PATH, 'bin', 'clang'));
382
+ this.setEnvIfNotExists('CXX', join(WASI_SDK_PATH, 'bin', 'clang++'));
383
+ this.setEnvIfNotExists('AR', join(WASI_SDK_PATH, 'bin', 'ar'));
384
+ this.setEnvIfNotExists('RANLIB', join(WASI_SDK_PATH, 'bin', 'ranlib'));
385
+ this.setEnvIfNotExists('CFLAGS', `--target=wasm32-wasi-threads --sysroot=${WASI_SDK_PATH}/share/wasi-sysroot -pthread -mllvm -wasm-enable-sjlj -I${setjmpInclude}`);
386
+ this.setEnvIfNotExists('CXXFLAGS', `--target=wasm32-wasi-threads --sysroot=${WASI_SDK_PATH}/share/wasi-sysroot -pthread -mllvm -wasm-enable-sjlj -I${setjmpInclude}`);
387
+ this.setEnvIfNotExists(`LDFLAGS`, `-fuse-ld=${WASI_SDK_PATH}/bin/wasm-ld --target=wasm32-wasi-threads`);
388
+ }
281
389
  }
282
390
  debug('Set envs: ');
283
391
  Object.entries(this.envs).forEach(([k, v]) => {
@@ -285,15 +393,27 @@ class Builder {
285
393
  });
286
394
  return this;
287
395
  }
396
+ setForceBuildEnvs(typeDefTmpFolder) {
397
+ // dynamically check all napi-rs deps and set `NAPI_FORCE_BUILD_{uppercase(snake_case(name))} = timestamp`
398
+ this.metadata.packages.forEach((crate) => {
399
+ if (crate.dependencies.some((d) => d.name === 'napi-derive') &&
400
+ !existsSync(join(typeDefTmpFolder, crate.name))) {
401
+ this.envs[`NAPI_FORCE_BUILD_${crate.name.replace(/-/g, '_').toUpperCase()}`] = Date.now().toString();
402
+ }
403
+ });
404
+ }
288
405
  setFeatures() {
289
406
  const args = [];
407
+ if (this.options.allFeatures && this.options.noDefaultFeatures) {
408
+ throw new Error('Cannot specify --all-features and --no-default-features together');
409
+ }
290
410
  if (this.options.allFeatures) {
291
411
  args.push('--all-features');
292
412
  }
293
413
  else if (this.options.noDefaultFeatures) {
294
414
  args.push('--no-default-features');
295
415
  }
296
- else if (this.options.features) {
416
+ if (this.options.features) {
297
417
  args.push('--features', ...this.options.features);
298
418
  }
299
419
  debug('Set features flags: ');
@@ -314,24 +434,26 @@ class Builder {
314
434
  if (this.options.profile) {
315
435
  this.args.push('--profile', this.options.profile);
316
436
  }
437
+ if (this.options.manifestPath) {
438
+ this.args.push('--manifest-path', this.options.manifestPath);
439
+ }
317
440
  if (this.options.cargoOptions?.length) {
318
441
  this.args.push(...this.options.cargoOptions);
319
442
  }
320
443
  return this;
321
444
  }
322
- getIntermediateTypeFile() {
323
- return join(tmpdir(), `${this.crate.name}-${createHash('sha256')
324
- .update(this.crate.manifest_path)
325
- .update(CLI_VERSION)
326
- .digest('hex')
327
- .substring(0, 8)}.napi_type_def.tmp`);
328
- }
329
- getIntermediateWasiRegisterFile() {
330
- return join(tmpdir(), `${this.crate.name}-${createHash('sha256')
445
+ generateIntermediateTypeDefFolder() {
446
+ let folder = join(this.targetDir, 'napi-rs', `${this.crate.name}-${createHash('sha256')
331
447
  .update(this.crate.manifest_path)
332
448
  .update(CLI_VERSION)
333
449
  .digest('hex')
334
- .substring(0, 8)}.napi_wasi_register.tmp`);
450
+ .substring(0, 8)}`);
451
+ if (!this.options.dtsCache) {
452
+ rmSync(folder, { recursive: true, force: true });
453
+ folder += `_${Date.now()}`;
454
+ }
455
+ mkdirAsync(folder, { recursive: true });
456
+ return folder;
335
457
  }
336
458
  async postBuild() {
337
459
  try {
@@ -345,30 +467,23 @@ class Builder {
345
467
  cause: e,
346
468
  });
347
469
  }
348
- const dest = await this.copyArtifact();
470
+ const wasmBinaryName = await this.copyArtifact();
349
471
  // only for cdylib
350
472
  if (this.cdyLibName) {
351
473
  const idents = await this.generateTypeDef();
352
- const intermediateWasiRegisterFile = this.envs.WASI_REGISTER_TMP_PATH;
353
- const wasiRegisterFunctions = this.target.arch === 'wasm32'
354
- ? JSON.parse(await readFileAsync(intermediateWasiRegisterFile, 'utf8').catch((err) => {
355
- console.warn(`Read ${colors.yellowBright(intermediateWasiRegisterFile)} failed, reason: ${err.message}`);
356
- return `[]`;
357
- }))
358
- : [];
359
474
  const jsOutput = await this.writeJsBinding(idents);
360
- const wasmOutput = await this.writeWasiBinding(wasiRegisterFunctions, dest ?? 'index.wasm', idents);
475
+ const wasmBindingsOutput = await this.writeWasiBinding(wasmBinaryName, idents);
361
476
  if (jsOutput) {
362
477
  this.outputs.push(jsOutput);
363
478
  }
364
- if (wasmOutput) {
365
- this.outputs.push(wasmOutput);
479
+ if (wasmBindingsOutput) {
480
+ this.outputs.push(...wasmBindingsOutput);
366
481
  }
367
482
  }
368
483
  return this.outputs;
369
484
  }
370
485
  async copyArtifact() {
371
- const [srcName, destName] = this.getArtifactNames();
486
+ const [srcName, destName, wasmBinaryName] = this.getArtifactNames();
372
487
  if (!srcName || !destName) {
373
488
  return;
374
489
  }
@@ -376,6 +491,7 @@ class Builder {
376
491
  const src = join(this.targetDir, this.target.triple, profile, srcName);
377
492
  debug(`Copy artifact from: [${src}]`);
378
493
  const dest = join(this.outputDir, destName);
494
+ const isWasm = dest.endsWith('.wasm');
379
495
  try {
380
496
  if (await fileExists(dest)) {
381
497
  debug('Old artifact found, remove it first');
@@ -383,22 +499,53 @@ class Builder {
383
499
  }
384
500
  debug('Copy artifact to:');
385
501
  debug(' %i', dest);
386
- await copyFileAsync(src, dest);
502
+ if (isWasm) {
503
+ const { ModuleConfig } = await import('@napi-rs/wasm-tools');
504
+ debug('Generate debug wasm module');
505
+ try {
506
+ const debugWasmModule = new ModuleConfig()
507
+ .generateDwarf(true)
508
+ .generateNameSection(true)
509
+ .generateProducersSection(true)
510
+ .preserveCodeTransform(true)
511
+ .strictValidate(false)
512
+ .parse(await readFileAsync(src));
513
+ const debugWasmBinary = debugWasmModule.emitWasm(true);
514
+ await writeFileAsync(dest.replace(/\.wasm$/, '.debug.wasm'), debugWasmBinary);
515
+ debug('Generate release wasm module');
516
+ const releaseWasmModule = new ModuleConfig()
517
+ .generateDwarf(false)
518
+ .generateNameSection(false)
519
+ .generateProducersSection(false)
520
+ .preserveCodeTransform(false)
521
+ .strictValidate(false)
522
+ .onlyStableFeatures(false)
523
+ .parse(debugWasmBinary);
524
+ const releaseWasmBinary = releaseWasmModule.emitWasm(false);
525
+ await writeFileAsync(dest, releaseWasmBinary);
526
+ }
527
+ catch (e) {
528
+ debug.warn(`Failed to generate debug wasm module: ${e.message ?? e}`);
529
+ await copyFileAsync(src, dest);
530
+ }
531
+ }
532
+ else {
533
+ await copyFileAsync(src, dest);
534
+ }
387
535
  this.outputs.push({
388
- kind: dest.endsWith('.node') ? 'node' : 'exe',
536
+ kind: dest.endsWith('.node') ? 'node' : isWasm ? 'wasm' : 'exe',
389
537
  path: dest,
390
538
  });
391
- return dest;
539
+ return wasmBinaryName ? join(this.outputDir, wasmBinaryName) : null;
392
540
  }
393
541
  catch (e) {
394
- throw new Error('Failed to copy artifact', {
395
- cause: e,
396
- });
542
+ throw new Error('Failed to copy artifact', { cause: e });
397
543
  }
398
544
  }
399
545
  getArtifactNames() {
400
546
  if (this.cdyLibName) {
401
547
  const cdyLib = this.cdyLibName.replace(/-/g, '_');
548
+ const wasiTarget = this.config.targets.find((t) => t.platform === 'wasi');
402
549
  const srcName = this.target.platform === 'darwin'
403
550
  ? `lib${cdyLib}.dylib`
404
551
  : this.target.platform === 'win32'
@@ -414,12 +561,18 @@ class Builder {
414
561
  destName += `.${this.target.platformArchABI}`;
415
562
  }
416
563
  if (srcName.endsWith('.wasm')) {
417
- destName += '.wasi-wasm32.wasm';
564
+ destName += '.wasm';
418
565
  }
419
566
  else {
420
567
  destName += '.node';
421
568
  }
422
- return [srcName, destName];
569
+ return [
570
+ srcName,
571
+ destName,
572
+ wasiTarget
573
+ ? `${this.config.binaryName}.${wasiTarget.platformArchABI}.wasm`
574
+ : null,
575
+ ];
423
576
  }
424
577
  else if (this.binName) {
425
578
  const srcName = this.target.platform === 'win32' ? `${this.binName}.exe` : this.binName;
@@ -428,21 +581,61 @@ class Builder {
428
581
  return [];
429
582
  }
430
583
  async generateTypeDef() {
431
- if (!(await fileExists(this.envs.TYPE_DEF_TMP_PATH))) {
584
+ const typeDefDir = this.envs.NAPI_TYPE_DEF_TMP_FOLDER;
585
+ if (!this.enableTypeDef || !(await dirExistsAsync(typeDefDir))) {
432
586
  return [];
433
587
  }
434
588
  const dest = join(this.outputDir, this.options.dts ?? 'index.d.ts');
435
- const { dts, exports } = await processTypeDef(this.envs.TYPE_DEF_TMP_PATH, this.options.constEnum ?? true, !this.options.noDtsHeader
436
- ? this.options.dtsHeader ?? DEFAULT_TYPE_DEF_HEADER
437
- : '');
589
+ let header = '';
590
+ let dts = '';
591
+ let exports = [];
592
+ if (!this.options.noDtsHeader) {
593
+ const dtsHeader = this.options.dtsHeader ?? this.config.dtsHeader;
594
+ // `dtsHeaderFile` in config > `dtsHeader` in cli flag > `dtsHeader` in config
595
+ if (this.config.dtsHeaderFile) {
596
+ try {
597
+ header = await readFileAsync(join(this.options.cwd, this.config.dtsHeaderFile), 'utf-8');
598
+ }
599
+ catch (e) {
600
+ debug.warn(`Failed to read dts header file ${this.config.dtsHeaderFile}`, e);
601
+ }
602
+ }
603
+ else if (dtsHeader) {
604
+ header = dtsHeader;
605
+ }
606
+ else {
607
+ header = DEFAULT_TYPE_DEF_HEADER;
608
+ }
609
+ }
610
+ const files = await readdirAsync(typeDefDir, { withFileTypes: true });
611
+ if (!files.length) {
612
+ debug('No type def files found. Skip generating dts file.');
613
+ return [];
614
+ }
615
+ for (const file of files) {
616
+ if (!file.isFile()) {
617
+ continue;
618
+ }
619
+ const { dts: fileDts, exports: fileExports } = await processTypeDef(join(typeDefDir, file.name), this.options.constEnum ?? this.config.constEnum ?? true);
620
+ dts += fileDts;
621
+ exports.push(...fileExports);
622
+ }
623
+ if (dts.indexOf('ExternalObject<') > -1) {
624
+ header += `
625
+ export declare class ExternalObject<T> {
626
+ readonly '': {
627
+ readonly '': unique symbol
628
+ [K: symbol]: T
629
+ }
630
+ }
631
+ `;
632
+ }
633
+ dts = header + dts;
438
634
  try {
439
635
  debug('Writing type def to:');
440
636
  debug(' %i', dest);
441
637
  await writeFileAsync(dest, dts, 'utf-8');
442
- this.outputs.push({
443
- kind: 'dts',
444
- path: dest,
445
- });
638
+ this.outputs.push({ kind: 'dts', path: dest });
446
639
  }
447
640
  catch (e) {
448
641
  debug.error('Failed to write type def file');
@@ -457,42 +650,58 @@ class Builder {
457
650
  idents.length === 0) {
458
651
  return;
459
652
  }
460
- const name = parse(this.options.jsBinding ?? 'index.js').name;
461
- const cjs = createCjsBinding(this.config.binaryName, this.config.packageName, idents);
653
+ const name = this.options.jsBinding ?? 'index.js';
654
+ const createBinding = this.options.esm ? createEsmBinding : createCjsBinding;
655
+ const binding = createBinding(this.config.binaryName, this.config.packageName, idents);
462
656
  try {
463
- const dest = join(this.outputDir, `${name}.js`);
657
+ const dest = join(this.outputDir, name);
464
658
  debug('Writing js binding to:');
465
659
  debug(' %i', dest);
466
- await writeFileAsync(dest, cjs, 'utf-8');
467
- return {
468
- kind: 'js',
469
- path: dest,
470
- };
660
+ await writeFileAsync(dest, binding, 'utf-8');
661
+ return { kind: 'js', path: dest };
471
662
  }
472
663
  catch (e) {
473
664
  throw new Error('Failed to write js binding file', { cause: e });
474
665
  }
475
666
  }
476
- async writeWasiBinding(wasiRegisterFunctions, distFileName, idents) {
477
- if (distFileName && wasiRegisterFunctions.length) {
667
+ async writeWasiBinding(distFileName, idents) {
668
+ if (distFileName) {
478
669
  const { name, dir } = parse(distFileName);
479
- const newPath = join(dir, `${this.config.binaryName}.wasi.mjs`);
670
+ const bindingPath = join(dir, `${this.config.binaryName}.wasi.cjs`);
671
+ const browserBindingPath = join(dir, `${this.config.binaryName}.wasi-browser.js`);
480
672
  const workerPath = join(dir, 'wasi-worker.mjs');
481
- const declareCodes = `const { ${idents.join(', ')} } = binding\n`;
482
- const exportsCode = `export {\n${idents
483
- .map((ident) => ` ${ident}`)
484
- .join(',\n')}\n}`;
485
- await writeFileAsync(newPath, createWasiBinding(name, wasiRegisterFunctions) +
486
- declareCodes +
673
+ const browserWorkerPath = join(dir, 'wasi-worker-browser.mjs');
674
+ const browserEntryPath = join(dir, 'browser.js');
675
+ const exportsCode = `module.exports = __napiModule.exports\n` +
676
+ idents
677
+ .map((ident) => `module.exports.${ident} = __napiModule.exports.${ident}`)
678
+ .join('\n');
679
+ await writeFileAsync(bindingPath, createWasiBinding(name, this.config.packageName, this.config.wasm?.initialMemory, this.config.wasm?.maximumMemory) +
487
680
  exportsCode +
488
681
  '\n', 'utf8');
682
+ await writeFileAsync(browserBindingPath, createWasiBrowserBinding(name, this.config.wasm?.initialMemory, this.config.wasm?.maximumMemory, this.config.wasm?.browser?.fs, this.config.wasm?.browser?.asyncInit) +
683
+ `export default __napiModule.exports\n` +
684
+ idents
685
+ .map((ident) => `export const ${ident} = __napiModule.exports.${ident}`)
686
+ .join('\n') +
687
+ '\n', 'utf8');
489
688
  await writeFileAsync(workerPath, WASI_WORKER_TEMPLATE, 'utf8');
490
- return {
491
- kind: 'wasm',
492
- path: newPath,
493
- };
689
+ await writeFileAsync(browserWorkerPath, createWasiBrowserWorkerBinding(this.config.wasm?.browser?.fs ?? false), 'utf8');
690
+ await writeFileAsync(browserEntryPath, `export * from '${this.config.packageName}-wasm32-wasi'\n`);
691
+ return [
692
+ { kind: 'js', path: bindingPath },
693
+ { kind: 'js', path: browserBindingPath },
694
+ { kind: 'js', path: workerPath },
695
+ { kind: 'js', path: browserWorkerPath },
696
+ { kind: 'js', path: browserEntryPath },
697
+ ];
698
+ }
699
+ return [];
700
+ }
701
+ setEnvIfNotExists(env, value) {
702
+ if (!process.env[env]) {
703
+ this.envs[env] = value;
494
704
  }
495
- return null;
496
705
  }
497
706
  }
498
707
  //# sourceMappingURL=build.js.map