@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
@@ -0,0 +1,429 @@
1
+ import { existsSync } from 'node:fs'
2
+ import { readFile, rm } from 'node:fs/promises'
3
+ import { tmpdir } from 'node:os'
4
+ import { join } from 'node:path'
5
+
6
+ import ava, { TestFn } from 'ava'
7
+ import { load as yamlLoad } from 'js-yaml'
8
+
9
+ import { newProject } from '../new.js'
10
+
11
+ const test = ava as TestFn<{
12
+ tmpDir: string
13
+ }>
14
+
15
+ test.beforeEach(async (t) => {
16
+ // Create a unique temp directory for tests
17
+ const timestamp = Date.now()
18
+ const random = Math.random().toString(36).substring(7)
19
+ const tmpDir = join(
20
+ tmpdir(),
21
+ 'napi-rs-test',
22
+ `new-project-${timestamp}-${random}`,
23
+ )
24
+ t.context = { tmpDir }
25
+ })
26
+
27
+ test.afterEach.always(async (t) => {
28
+ // Clean up any created directories
29
+ if (existsSync(t.context.tmpDir)) {
30
+ await rm(t.context.tmpDir, { recursive: true, force: true })
31
+ }
32
+ })
33
+
34
+ test('create a new project with default options', async (t) => {
35
+ const projectPath = join(t.context.tmpDir, 'default-project')
36
+
37
+ await newProject({
38
+ path: projectPath,
39
+ enableDefaultTargets: true,
40
+ })
41
+
42
+ t.true(existsSync(projectPath))
43
+ t.true(existsSync(join(projectPath, 'package.json')))
44
+ t.true(existsSync(join(projectPath, 'Cargo.toml')))
45
+ t.true(existsSync(join(projectPath, 'src')))
46
+ t.true(existsSync(join(projectPath, '.github', 'workflows', 'CI.yml')))
47
+
48
+ // Check package.json
49
+ const pkgJson = JSON.parse(
50
+ await readFile(join(projectPath, 'package.json'), 'utf-8'),
51
+ )
52
+ t.is(pkgJson.name, 'default-project')
53
+ t.is(pkgJson.napi.binaryName, 'default-project')
54
+ t.is(pkgJson.license, 'MIT')
55
+ t.truthy(pkgJson.engines.node)
56
+ t.falsy(existsSync(join(projectPath, 'default-project.wasi-browser.js')))
57
+ const gitAttributes = await readFile(
58
+ join(projectPath, '.gitattributes'),
59
+ 'utf-8',
60
+ )
61
+ t.truthy(gitAttributes.includes('default-project.wasi-browser.js'))
62
+ t.truthy(gitAttributes.includes('default-project.wasi.cjs'))
63
+ t.truthy(gitAttributes.includes('wasi-worker-browser.mjs'))
64
+ t.truthy(gitAttributes.includes('wasi-worker.mjs'))
65
+ const ciYaml = await readFile(
66
+ join(projectPath, '.github', 'workflows', 'CI.yml'),
67
+ 'utf-8',
68
+ )
69
+ const yamlObject = yamlLoad(ciYaml) as any
70
+ t.is(yamlObject.env.APP_NAME, 'default-project')
71
+ t.falsy(yamlObject.jobs.publish.needs.includes('wasm32-wasip1-threads'))
72
+ t.falsy(
73
+ yamlObject.jobs['test-linux-binding'].strategy.matrix.target.includes(
74
+ 'aarch64-unknown-linux-musl',
75
+ ),
76
+ )
77
+ })
78
+
79
+ test('create a new project with custom name', async (t) => {
80
+ const projectPath = join(t.context.tmpDir, 'custom-name-dir')
81
+
82
+ await newProject({
83
+ path: projectPath,
84
+ name: '@my-scope/custom-package',
85
+ enableDefaultTargets: true,
86
+ })
87
+
88
+ t.true(existsSync(projectPath))
89
+
90
+ const pkgJson = JSON.parse(
91
+ await readFile(join(projectPath, 'package.json'), 'utf-8'),
92
+ )
93
+ t.is(pkgJson.name, '@my-scope/custom-package')
94
+ t.is(pkgJson.napi.binaryName, 'custom-package')
95
+
96
+ const cargoToml = await readFile(join(projectPath, 'Cargo.toml'), 'utf-8')
97
+ // Verify that the package name was properly renamed to follow Rust naming conventions
98
+ t.true(cargoToml.includes('name = "custom_package"'))
99
+ })
100
+
101
+ test('create a new project with custom path', async (t) => {
102
+ const customPath = join(t.context.tmpDir, 'nested', 'folders', 'my-project')
103
+
104
+ await newProject({
105
+ path: customPath,
106
+ enableDefaultTargets: true,
107
+ })
108
+
109
+ t.true(existsSync(customPath))
110
+ t.true(existsSync(join(customPath, 'package.json')))
111
+
112
+ const pkgJson = JSON.parse(
113
+ await readFile(join(customPath, 'package.json'), 'utf-8'),
114
+ )
115
+ t.is(pkgJson.name, 'my-project')
116
+ })
117
+
118
+ test('create a new project with custom path and name', async (t) => {
119
+ const projectPath = join(t.context.tmpDir, 'custom-dir')
120
+
121
+ await newProject({
122
+ path: projectPath,
123
+ name: 'custom-project-name',
124
+ enableDefaultTargets: true,
125
+ })
126
+
127
+ t.true(existsSync(projectPath))
128
+
129
+ const pkgJson = JSON.parse(
130
+ await readFile(join(projectPath, 'package.json'), 'utf-8'),
131
+ )
132
+ t.is(pkgJson.name, 'custom-project-name')
133
+ t.is(pkgJson.napi.binaryName, 'custom-project-name')
134
+ })
135
+
136
+ test('create a new project with custom path, name, and targets', async (t) => {
137
+ const projectPath = join(t.context.tmpDir, 'full-custom')
138
+ const customTargets = [
139
+ 'x86_64-unknown-linux-gnu',
140
+ 'x86_64-apple-darwin',
141
+ 'aarch64-apple-darwin',
142
+ 'wasm32-wasip1-threads',
143
+ 'x86_64-unknown-freebsd',
144
+ ]
145
+
146
+ await newProject({
147
+ path: projectPath,
148
+ name: '@custom/full-project',
149
+ targets: customTargets,
150
+ enableDefaultTargets: false,
151
+ license: 'Apache-2.0',
152
+ minNodeApiVersion: 6,
153
+ })
154
+
155
+ t.true(existsSync(projectPath))
156
+
157
+ const pkgJson = JSON.parse(
158
+ await readFile(join(projectPath, 'package.json'), 'utf-8'),
159
+ )
160
+ t.is(pkgJson.name, '@custom/full-project')
161
+ t.is(pkgJson.napi.binaryName, 'full-project')
162
+ t.is(pkgJson.license, 'Apache-2.0')
163
+ t.true(pkgJson.engines.node.includes('>= 14.0.0'))
164
+
165
+ // Check that CI workflow only includes the specified targets
166
+ const ciYaml = await readFile(
167
+ join(projectPath, '.github', 'workflows', 'CI.yml'),
168
+ 'utf-8',
169
+ )
170
+ const yamlObject = yamlLoad(ciYaml) as any
171
+ t.true(
172
+ yamlObject.jobs.build.strategy.matrix.settings.some(
173
+ (setting: any) => setting.target === 'x86_64-unknown-linux-gnu',
174
+ ),
175
+ )
176
+ t.true(
177
+ yamlObject.jobs.build.strategy.matrix.settings.some(
178
+ (setting: any) => setting.target === 'x86_64-apple-darwin',
179
+ ),
180
+ )
181
+ t.true(
182
+ yamlObject.jobs.build.strategy.matrix.settings.some(
183
+ (setting: any) => setting.target === 'aarch64-apple-darwin',
184
+ ),
185
+ )
186
+ t.false(
187
+ yamlObject.jobs.build.strategy.matrix.settings.some(
188
+ (setting: any) => setting.target === 'x86_64-pc-windows-msvc',
189
+ ),
190
+ )
191
+ t.true(
192
+ yamlObject.jobs.build.strategy.matrix.settings.some(
193
+ (setting: any) => setting.target === 'wasm32-wasip1-threads',
194
+ ),
195
+ )
196
+ t.truthy(yamlObject.jobs['build-freebsd'])
197
+ t.truthy(yamlObject.jobs['test-wasi'])
198
+ t.falsy(
199
+ yamlObject.jobs['test-macOS-windows-binding'].strategy.matrix.settings.some(
200
+ (setting: any) => setting.target === 'x86_64-pc-windows-msvc',
201
+ ),
202
+ )
203
+ t.truthy(
204
+ yamlObject.jobs['test-macOS-windows-binding'].strategy.matrix.settings.some(
205
+ (setting: any) => setting.target === 'aarch64-apple-darwin',
206
+ ),
207
+ )
208
+ })
209
+
210
+ test('non Windows and macOS targets should remove test-macOS-windows-binding job', async (t) => {
211
+ const projectPath = join(t.context.tmpDir, 'no-windows-macos')
212
+ const targets = [
213
+ 'x86_64-unknown-linux-gnu',
214
+ 'aarch64-unknown-linux-gnu',
215
+ 'wasm32-wasip1-threads',
216
+ 'x86_64-unknown-freebsd',
217
+ ]
218
+
219
+ await newProject({
220
+ path: projectPath,
221
+ targets,
222
+ enableDefaultTargets: false,
223
+ })
224
+
225
+ t.true(existsSync(projectPath))
226
+
227
+ const ciYaml = await readFile(
228
+ join(projectPath, '.github', 'workflows', 'CI.yml'),
229
+ 'utf-8',
230
+ )
231
+ const yamlObject = yamlLoad(ciYaml) as any
232
+ t.falsy(yamlObject.jobs['test-macOS-windows-binding'])
233
+ t.falsy(yamlObject.jobs.publish.needs.includes('test-macOS-windows-binding'))
234
+ })
235
+
236
+ test('should remove test-linux-binding job if no Linux targets are enabled', async (t) => {
237
+ const projectPath = join(t.context.tmpDir, 'no-linux')
238
+ const targets = ['x86_64-apple-darwin', 'aarch64-apple-darwin']
239
+
240
+ await newProject({
241
+ path: projectPath,
242
+ targets,
243
+ enableDefaultTargets: false,
244
+ })
245
+
246
+ t.true(existsSync(projectPath))
247
+
248
+ const ciYaml = await readFile(
249
+ join(projectPath, '.github', 'workflows', 'CI.yml'),
250
+ 'utf-8',
251
+ )
252
+ const yamlObject = yamlLoad(ciYaml) as any
253
+ t.falsy(yamlObject.jobs['test-linux-binding'])
254
+ t.falsy(yamlObject.jobs.publish.needs.includes('test-linux-binding'))
255
+ })
256
+
257
+ test('should fail when no path is provided', async (t) => {
258
+ await t.throwsAsync(
259
+ async () => {
260
+ await newProject({
261
+ enableDefaultTargets: true,
262
+ })
263
+ },
264
+ { message: /Please provide the path as the argument/ },
265
+ )
266
+ })
267
+
268
+ test('should fail when path already exists and is not empty', async (t) => {
269
+ const projectPath = join(t.context.tmpDir, 'existing-project')
270
+
271
+ // Create directory with a file
272
+ await rm(projectPath, { recursive: true, force: true }).catch(() => {})
273
+ const { mkdirSync, writeFileSync } = await import('node:fs')
274
+ mkdirSync(projectPath, { recursive: true })
275
+ writeFileSync(join(projectPath, 'existing-file.txt'), 'content')
276
+
277
+ await t.throwsAsync(
278
+ async () => {
279
+ await newProject({
280
+ path: projectPath,
281
+ enableDefaultTargets: true,
282
+ })
283
+ },
284
+ { message: /already exists and it's not empty/ },
285
+ )
286
+ })
287
+
288
+ test('should fail when path is a file', async (t) => {
289
+ const filePath = join(t.context.tmpDir, 'file.txt')
290
+
291
+ // Create a file
292
+ const { writeFileSync } = await import('node:fs')
293
+ const { mkdirSync } = await import('node:fs')
294
+ mkdirSync(t.context.tmpDir, { recursive: true })
295
+ writeFileSync(filePath, 'content')
296
+
297
+ await t.throwsAsync(
298
+ async () => {
299
+ await newProject({
300
+ path: filePath,
301
+ enableDefaultTargets: true,
302
+ })
303
+ },
304
+ { message: /already exists and it's not a directory/ },
305
+ )
306
+ })
307
+
308
+ test('dry run should not create any files', async (t) => {
309
+ const projectPath = join(t.context.tmpDir, 'dry-run-project')
310
+
311
+ await newProject({
312
+ path: projectPath,
313
+ name: 'dry-run-test',
314
+ enableDefaultTargets: true,
315
+ dryRun: true,
316
+ })
317
+
318
+ t.false(existsSync(projectPath))
319
+ })
320
+
321
+ test('create project without GitHub Actions', async (t) => {
322
+ const projectPath = join(t.context.tmpDir, 'no-github-actions')
323
+
324
+ await newProject({
325
+ path: projectPath,
326
+ enableDefaultTargets: true,
327
+ enableGithubActions: false,
328
+ })
329
+
330
+ t.true(existsSync(projectPath))
331
+ t.true(existsSync(join(projectPath, 'package.json')))
332
+ t.false(existsSync(join(projectPath, '.github')))
333
+ })
334
+
335
+ test('create a new project with pnpm package manager', async (t) => {
336
+ const projectPath = join(t.context.tmpDir, 'pnpm-project')
337
+
338
+ await newProject({
339
+ path: projectPath,
340
+ name: 'pnpm-test-project',
341
+ packageManager: 'pnpm',
342
+ enableDefaultTargets: true,
343
+ })
344
+
345
+ t.true(existsSync(projectPath))
346
+ t.true(existsSync(join(projectPath, 'package.json')))
347
+ t.true(existsSync(join(projectPath, 'Cargo.toml')))
348
+
349
+ // Check package.json
350
+ const pkgJson = JSON.parse(
351
+ await readFile(join(projectPath, 'package.json'), 'utf-8'),
352
+ )
353
+ t.is(pkgJson.name, 'pnpm-test-project')
354
+ t.is(pkgJson.napi.binaryName, 'pnpm-test-project')
355
+
356
+ // Verify that the Cargo.toml has the correct sanitized name
357
+ const cargoToml = await readFile(join(projectPath, 'Cargo.toml'), 'utf-8')
358
+ t.true(cargoToml.includes('name = "pnpm_test_project"'))
359
+
360
+ // Check for pnpm-specific files or configurations if any
361
+ // The template might have different structure for pnpm
362
+ })
363
+
364
+ test('create a new project with pnpm and custom name', async (t) => {
365
+ const projectPath = join(t.context.tmpDir, 'pnpm-custom-name')
366
+
367
+ await newProject({
368
+ path: projectPath,
369
+ name: '@my-org/custom-pnpm-package',
370
+ packageManager: 'pnpm',
371
+ enableDefaultTargets: true,
372
+ })
373
+
374
+ t.true(existsSync(projectPath))
375
+ t.true(existsSync(join(projectPath, 'package.json')))
376
+
377
+ const pkgJson = JSON.parse(
378
+ await readFile(join(projectPath, 'package.json'), 'utf-8'),
379
+ )
380
+ t.is(pkgJson.name, '@my-org/custom-pnpm-package')
381
+ t.is(pkgJson.napi.binaryName, 'custom-pnpm-package')
382
+
383
+ const cargoToml = await readFile(join(projectPath, 'Cargo.toml'), 'utf-8')
384
+ t.true(cargoToml.includes('name = "custom_pnpm_package"'))
385
+ })
386
+
387
+ test('create a new project with pnpm and custom path/name combination', async (t) => {
388
+ const projectPath = join(t.context.tmpDir, 'deep', 'nested', 'pnpm-dir')
389
+
390
+ await newProject({
391
+ path: projectPath,
392
+ name: '@scoped/pnpm-custom-name',
393
+ packageManager: 'pnpm',
394
+ enableDefaultTargets: true,
395
+ license: 'Apache-2.0',
396
+ })
397
+
398
+ t.true(existsSync(projectPath))
399
+ t.true(existsSync(join(projectPath, 'package.json')))
400
+ t.true(existsSync(join(projectPath, 'Cargo.toml')))
401
+
402
+ // Check package.json
403
+ const pkgJson = JSON.parse(
404
+ await readFile(join(projectPath, 'package.json'), 'utf-8'),
405
+ )
406
+ t.is(pkgJson.name, '@scoped/pnpm-custom-name')
407
+ t.is(pkgJson.napi.binaryName, 'pnpm-custom-name')
408
+ t.is(pkgJson.license, 'Apache-2.0')
409
+
410
+ // Check Cargo.toml has sanitized name
411
+ const cargoToml = await readFile(join(projectPath, 'Cargo.toml'), 'utf-8')
412
+ t.true(cargoToml.includes('name = "pnpm_custom_name"'))
413
+ })
414
+
415
+ test('should fail when no targets are enabled', async (t) => {
416
+ const projectPath = join(t.context.tmpDir, 'no-targets')
417
+
418
+ await t.throwsAsync(
419
+ async () => {
420
+ await newProject({
421
+ path: projectPath,
422
+ enableDefaultTargets: false,
423
+ enableAllTargets: false,
424
+ targets: [],
425
+ })
426
+ },
427
+ { message: /At least one target must be enabled/ },
428
+ )
429
+ })
@@ -1,4 +1,4 @@
1
- import { join, parse, resolve } from 'node:path'
1
+ import { join, parse } from 'node:path'
2
2
 
3
3
  import * as colors from 'colorette'
4
4
 
@@ -20,23 +20,24 @@ const debug = debugFactory('artifacts')
20
20
  export async function collectArtifacts(userOptions: ArtifactsOptions) {
21
21
  const options = applyDefaultArtifactsOptions(userOptions)
22
22
 
23
- const packageJsonPath = resolve(options.cwd, options.packageJsonPath)
24
- const { targets, binaryName } = await readNapiConfig(packageJsonPath)
23
+ const packageJsonPath = join(options.cwd, options.packageJsonPath)
24
+ const { targets, binaryName, packageName } =
25
+ await readNapiConfig(packageJsonPath)
25
26
 
26
27
  const distDirs = targets.map((platform) =>
27
- resolve(options.cwd, options.npmDir, platform.platformArchABI),
28
+ join(options.cwd, options.npmDir, platform.platformArchABI),
28
29
  )
29
30
 
30
31
  const universalSourceBins = new Set(
31
32
  targets
32
33
  .filter((platform) => platform.arch === 'universal')
33
- .flatMap(
34
- (p) => UniArchsByPlatform[p.platform]?.map((a) => `${p.platform}-${a}`),
34
+ .flatMap((p) =>
35
+ UniArchsByPlatform[p.platform]?.map((a) => `${p.platform}-${a}`),
35
36
  )
36
37
  .filter(Boolean) as string[],
37
38
  )
38
39
 
39
- await collectNodeBinaries(resolve(options.cwd, options.outputDir)).then(
40
+ await collectNodeBinaries(join(options.cwd, options.outputDir)).then(
40
41
  (output) =>
41
42
  Promise.all(
42
43
  output.map(async (filePath) => {
@@ -80,17 +81,88 @@ export async function collectArtifacts(userOptions: ArtifactsOptions) {
80
81
  }),
81
82
  ),
82
83
  )
84
+
85
+ const wasiTarget = targets.find((t) => t.platform === 'wasi')
86
+ if (wasiTarget) {
87
+ const wasiDir = join(
88
+ options.cwd,
89
+ options.npmDir,
90
+ wasiTarget.platformArchABI,
91
+ )
92
+ const cjsFile = join(
93
+ options.buildOutputDir ?? options.cwd,
94
+ `${binaryName}.wasi.cjs`,
95
+ )
96
+ const workerFile = join(
97
+ options.buildOutputDir ?? options.cwd,
98
+ `wasi-worker.mjs`,
99
+ )
100
+ const browserEntry = join(
101
+ options.buildOutputDir ?? options.cwd,
102
+ `${binaryName}.wasi-browser.js`,
103
+ )
104
+ const browserWorkerFile = join(
105
+ options.buildOutputDir ?? options.cwd,
106
+ `wasi-worker-browser.mjs`,
107
+ )
108
+ debug.info(
109
+ `Move wasi binding file [${colors.yellowBright(
110
+ cjsFile,
111
+ )}] to [${colors.yellowBright(wasiDir)}]`,
112
+ )
113
+ await writeFileAsync(
114
+ join(wasiDir, `${binaryName}.wasi.cjs`),
115
+ await readFileAsync(cjsFile),
116
+ )
117
+ debug.info(
118
+ `Move wasi worker file [${colors.yellowBright(
119
+ workerFile,
120
+ )}] to [${colors.yellowBright(wasiDir)}]`,
121
+ )
122
+ await writeFileAsync(
123
+ join(wasiDir, `wasi-worker.mjs`),
124
+ await readFileAsync(workerFile),
125
+ )
126
+ debug.info(
127
+ `Move wasi browser entry file [${colors.yellowBright(
128
+ browserEntry,
129
+ )}] to [${colors.yellowBright(wasiDir)}]`,
130
+ )
131
+ await writeFileAsync(
132
+ join(wasiDir, `${binaryName}.wasi-browser.js`),
133
+ // https://github.com/vitejs/vite/issues/8427
134
+ (await readFileAsync(browserEntry, 'utf8')).replace(
135
+ `new URL('./wasi-worker-browser.mjs', import.meta.url)`,
136
+ `new URL('${packageName}-wasm32-wasi/wasi-worker-browser.mjs', import.meta.url)`,
137
+ ),
138
+ )
139
+ debug.info(
140
+ `Move wasi browser worker file [${colors.yellowBright(
141
+ browserWorkerFile,
142
+ )}] to [${colors.yellowBright(wasiDir)}]`,
143
+ )
144
+ await writeFileAsync(
145
+ join(wasiDir, `wasi-worker-browser.mjs`),
146
+ await readFileAsync(browserWorkerFile),
147
+ )
148
+ }
83
149
  }
84
150
 
85
151
  async function collectNodeBinaries(root: string) {
86
152
  const files = await readdirAsync(root, { withFileTypes: true })
87
153
  const nodeBinaries = files
88
- .filter((file) => file.isFile() && file.name.endsWith('.node'))
154
+ .filter(
155
+ (file) =>
156
+ file.isFile() &&
157
+ (file.name.endsWith('.node') || file.name.endsWith('.wasm')),
158
+ )
89
159
  .map((file) => join(root, file.name))
90
160
 
91
161
  const dirs = files.filter((file) => file.isDirectory())
92
162
  for (const dir of dirs) {
93
- nodeBinaries.push(...(await collectNodeBinaries(join(root, dir.name))))
163
+ if (dir.name !== 'node_modules') {
164
+ nodeBinaries.push(...(await collectNodeBinaries(join(root, dir.name))))
165
+ }
94
166
  }
95
167
  return nodeBinaries
96
168
  }