@napi-rs/cli 3.1.5 → 3.3.0

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 (38) hide show
  1. package/cli.mjs +3 -3
  2. package/dist/cli.js +976 -1070
  3. package/dist/index.cjs +1114 -873
  4. package/dist/index.d.cts +32 -3
  5. package/dist/index.d.ts +31 -4
  6. package/dist/index.js +1065 -866
  7. package/package.json +21 -22
  8. package/src/api/artifacts.ts +8 -5
  9. package/src/api/build.ts +176 -111
  10. package/src/api/create-npm-dirs.ts +2 -2
  11. package/src/api/new.ts +5 -3
  12. package/src/api/pre-publish.ts +1 -1
  13. package/src/api/rename.ts +3 -4
  14. package/src/api/templates/js-binding.ts +31 -6
  15. package/src/api/universalize.ts +1 -1
  16. package/src/api/version.ts +4 -1
  17. package/src/cli.ts +1 -27
  18. package/src/commands/new.ts +2 -1
  19. package/src/index.ts +33 -0
  20. package/src/utils/config.ts +1 -1
  21. package/src/utils/misc.ts +15 -15
  22. package/src/utils/read-config.ts +1 -3
  23. package/src/utils/target.ts +7 -0
  24. package/src/api/__tests__/create-npm-dirs.spec.ts +0 -210
  25. package/src/api/__tests__/new.spec.ts +0 -429
  26. package/src/utils/__tests__/__fixtures__/napi_type_def +0 -201
  27. package/src/utils/__tests__/__snapshots__/config.spec.ts.md +0 -97
  28. package/src/utils/__tests__/__snapshots__/config.spec.ts.snap +0 -0
  29. package/src/utils/__tests__/__snapshots__/target.spec.ts.md +0 -166
  30. package/src/utils/__tests__/__snapshots__/target.spec.ts.snap +0 -0
  31. package/src/utils/__tests__/__snapshots__/typegen.spec.ts.md +0 -1157
  32. package/src/utils/__tests__/__snapshots__/typegen.spec.ts.snap +0 -0
  33. package/src/utils/__tests__/__snapshots__/version.spec.ts.md +0 -21
  34. package/src/utils/__tests__/__snapshots__/version.spec.ts.snap +0 -0
  35. package/src/utils/__tests__/config.spec.ts +0 -81
  36. package/src/utils/__tests__/target.spec.ts +0 -19
  37. package/src/utils/__tests__/typegen.spec.ts +0 -64
  38. package/src/utils/__tests__/version.spec.ts +0 -13
@@ -9,7 +9,7 @@ import {
9
9
  AVAILABLE_TARGETS,
10
10
  debugFactory,
11
11
  DEFAULT_TARGETS,
12
- TargetTriple,
12
+ type TargetTriple,
13
13
  } from '../utils/index.js'
14
14
  import { napiEngineRequirement } from '../utils/version.js'
15
15
 
@@ -76,6 +76,7 @@ export class NewCommand extends BaseNewCommand {
76
76
  return select({
77
77
  message: 'Minimum node-api version (with node version requirement)',
78
78
  loop: false,
79
+ pageSize: 10,
79
80
  choices: Array.from({ length: 8 }, (_, i) => ({
80
81
  name: `napi${i + 1} (${napiEngineRequirement(i + 1)})`,
81
82
  value: i + 1,
package/src/index.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { Cli } from 'clipanion'
2
+
1
3
  import { collectArtifacts } from './api/artifacts.js'
2
4
  import { buildProject } from './api/build.js'
3
5
  import { createNpmDirs } from './api/create-npm-dirs.js'
@@ -6,6 +8,31 @@ import { prePublish } from './api/pre-publish.js'
6
8
  import { renameProject } from './api/rename.js'
7
9
  import { universalizeBinaries } from './api/universalize.js'
8
10
  import { version } from './api/version.js'
11
+ import { ArtifactsCommand } from './commands/artifacts.js'
12
+ import { BuildCommand } from './commands/build.js'
13
+ import { CreateNpmDirsCommand } from './commands/create-npm-dirs.js'
14
+ import { HelpCommand } from './commands/help.js'
15
+ import { NewCommand } from './commands/new.js'
16
+ import { PrePublishCommand } from './commands/pre-publish.js'
17
+ import { RenameCommand } from './commands/rename.js'
18
+ import { UniversalizeCommand } from './commands/universalize.js'
19
+ import { VersionCommand } from './commands/version.js'
20
+ import { CLI_VERSION } from './utils/misc.js'
21
+
22
+ export const cli = new Cli({
23
+ binaryName: 'napi',
24
+ binaryVersion: CLI_VERSION,
25
+ })
26
+
27
+ cli.register(NewCommand)
28
+ cli.register(BuildCommand)
29
+ cli.register(CreateNpmDirsCommand)
30
+ cli.register(ArtifactsCommand)
31
+ cli.register(UniversalizeCommand)
32
+ cli.register(RenameCommand)
33
+ cli.register(PrePublishCommand)
34
+ cli.register(VersionCommand)
35
+ cli.register(HelpCommand)
9
36
 
10
37
  /**
11
38
  *
@@ -31,3 +58,9 @@ export class NapiCli {
31
58
  }
32
59
 
33
60
  export { parseTriple } from './utils/target.js'
61
+ export {
62
+ type GenerateTypeDefOptions,
63
+ type WriteJsBindingOptions,
64
+ writeJsBinding,
65
+ generateTypeDef,
66
+ } from './api/build.js'
@@ -2,7 +2,7 @@ import { underline, yellow } from 'colorette'
2
2
  import { merge, omit } from 'es-toolkit'
3
3
 
4
4
  import { fileExists, readFileAsync } from './misc.js'
5
- import { DEFAULT_TARGETS, parseTriple, Target } from './target.js'
5
+ import { DEFAULT_TARGETS, parseTriple, type Target } from './target.js'
6
6
 
7
7
  export type ValueOfConstArray<T> = T[Exclude<keyof T, keyof Array<any>>]
8
8
 
package/src/utils/misc.ts CHANGED
@@ -1,30 +1,30 @@
1
1
  import {
2
2
  readFile,
3
3
  writeFile,
4
+ unlink,
4
5
  copyFile,
5
6
  mkdir,
6
- unlink,
7
7
  stat,
8
8
  readdir,
9
- } from 'node:fs'
10
- import { promisify } from 'node:util'
9
+ access,
10
+ } from 'node:fs/promises'
11
11
 
12
12
  import pkgJson from '../../package.json' with { type: 'json' }
13
13
  import { debug } from './log.js'
14
14
 
15
- export const readFileAsync = promisify(readFile)
16
- export const writeFileAsync = promisify(writeFile)
17
- export const unlinkAsync = promisify(unlink)
18
- export const copyFileAsync = promisify(copyFile)
19
- export const mkdirAsync = promisify(mkdir)
20
- export const statAsync = promisify(stat)
21
- export const readdirAsync = promisify(readdir)
15
+ export const readFileAsync = readFile
16
+ export const writeFileAsync = writeFile
17
+ export const unlinkAsync = unlink
18
+ export const copyFileAsync = copyFile
19
+ export const mkdirAsync = mkdir
20
+ export const statAsync = stat
21
+ export const readdirAsync = readdir
22
22
 
23
- export async function fileExists(path: string) {
24
- const exists = await statAsync(path)
25
- .then(() => true)
26
- .catch(() => false)
27
- return exists
23
+ export function fileExists(path: string): Promise<boolean> {
24
+ return access(path).then(
25
+ () => true,
26
+ () => false,
27
+ )
28
28
  }
29
29
 
30
30
  export async function dirExistsAsync(path: string) {
@@ -11,9 +11,7 @@ interface MinimalNapiOptions {
11
11
  export async function readConfig(options: MinimalNapiOptions) {
12
12
  const resolvePath = (...paths: string[]) => resolve(options.cwd, ...paths)
13
13
  const config = await readNapiConfig(
14
- resolvePath(
15
- options.configPath ?? options.packageJsonPath ?? 'package.json',
16
- ),
14
+ resolvePath(options.packageJsonPath ?? 'package.json'),
17
15
  options.configPath ? resolvePath(options.configPath) : undefined,
18
16
  )
19
17
  return config
@@ -17,6 +17,7 @@ export const AVAILABLE_TARGETS = [
17
17
  'aarch64-pc-windows-msvc',
18
18
  'x86_64-apple-darwin',
19
19
  'x86_64-pc-windows-msvc',
20
+ 'x86_64-pc-windows-gnu',
20
21
  'x86_64-unknown-linux-gnu',
21
22
  'x86_64-unknown-linux-musl',
22
23
  'x86_64-unknown-linux-ohos',
@@ -26,6 +27,7 @@ export const AVAILABLE_TARGETS = [
26
27
  'armv7-unknown-linux-musleabihf',
27
28
  'armv7-linux-androideabi',
28
29
  'universal-apple-darwin',
30
+ 'loongarch64-unknown-linux-gnu',
29
31
  'riscv64gc-unknown-linux-gnu',
30
32
  'powerpc64le-unknown-linux-gnu',
31
33
  's390x-unknown-linux-gnu',
@@ -44,6 +46,8 @@ export const DEFAULT_TARGETS = [
44
46
 
45
47
  export const TARGET_LINKER: Record<string, string> = {
46
48
  'aarch64-unknown-linux-musl': 'aarch64-linux-musl-gcc',
49
+ // TODO: Switch to loongarch64-linux-gnu-gcc when available
50
+ 'loongarch64-unknown-linux-gnu': 'loongarch64-linux-gnu-gcc-13',
47
51
  'riscv64gc-unknown-linux-gnu': 'riscv64-linux-gnu-gcc',
48
52
  'powerpc64le-unknown-linux-gnu': 'powerpc64le-linux-gnu-gcc',
49
53
  's390x-unknown-linux-gnu': 's390x-linux-gnu-gcc',
@@ -54,6 +58,7 @@ type NodeJSArch =
54
58
  | 'arm'
55
59
  | 'arm64'
56
60
  | 'ia32'
61
+ | 'loong64'
57
62
  | 'mips'
58
63
  | 'mipsel'
59
64
  | 'ppc'
@@ -71,6 +76,7 @@ const CpuToNodeArch: Record<string, NodeJSArch> = {
71
76
  aarch64: 'arm64',
72
77
  i686: 'ia32',
73
78
  armv7: 'arm',
79
+ loongarch64: 'loong64',
74
80
  riscv64gc: 'riscv64',
75
81
  powerpc64le: 'ppc64',
76
82
  }
@@ -80,6 +86,7 @@ export const NodeArchToCpu: Record<string, string> = {
80
86
  arm64: 'aarch64',
81
87
  ia32: 'i686',
82
88
  arm: 'armv7',
89
+ loong64: 'loongarch64',
83
90
  riscv64: 'riscv64gc',
84
91
  ppc64: 'powerpc64le',
85
92
  }
@@ -1,210 +0,0 @@
1
- import { existsSync } from 'node:fs'
2
- import { mkdir, readFile, rm, writeFile } from 'node:fs/promises'
3
- import { tmpdir } from 'node:os'
4
- import { join } from 'node:path'
5
-
6
- import ava, { TestFn } from 'ava'
7
-
8
- import { createNpmDirs } from '../create-npm-dirs.js'
9
-
10
- const test = ava as TestFn<{
11
- tmpDir: string
12
- packageJsonPath: 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
- `create-npm-dirs-${timestamp}-${random}`,
23
- )
24
- const packageJsonPath = join(tmpDir, 'package.json')
25
-
26
- // Create the directory
27
- await mkdir(tmpDir, { recursive: true })
28
-
29
- t.context = { tmpDir, packageJsonPath }
30
- })
31
-
32
- test.afterEach.always(async (t) => {
33
- // Clean up any created directories
34
- if (existsSync(t.context.tmpDir)) {
35
- await rm(t.context.tmpDir, { recursive: true, force: true })
36
- }
37
- })
38
-
39
- test('should omit exports fields from publishConfig in scoped packages', async (t) => {
40
- const { tmpDir, packageJsonPath } = t.context
41
-
42
- // Create a package.json with publishConfig that includes exports field
43
- const packageJson = {
44
- name: 'test-package',
45
- version: '1.0.0',
46
- publishConfig: {
47
- registry: 'https://custom-registry.com',
48
- access: 'public',
49
- exports: {
50
- '.': './dist/index.js',
51
- './package.json': './package.json',
52
- },
53
- tag: 'beta',
54
- },
55
- napi: {
56
- binaryName: 'test-package',
57
- targets: ['x86_64-unknown-linux-gnu'],
58
- },
59
- }
60
-
61
- await writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2))
62
-
63
- await createNpmDirs({
64
- cwd: tmpDir,
65
- packageJsonPath: 'package.json',
66
- })
67
-
68
- // Check that the scoped package directory was created
69
- const scopedDir = join(tmpDir, 'npm', 'linux-x64-gnu')
70
- t.true(existsSync(scopedDir))
71
-
72
- // Read the generated package.json for the scoped package
73
- const scopedPackageJsonPath = join(scopedDir, 'package.json')
74
- t.true(existsSync(scopedPackageJsonPath))
75
-
76
- const scopedPackageJson = JSON.parse(
77
- await readFile(scopedPackageJsonPath, 'utf-8'),
78
- )
79
-
80
- // Verify that publishConfig only contains registry and access, not exports
81
- t.truthy(scopedPackageJson.publishConfig)
82
- t.is(scopedPackageJson.publishConfig.registry, 'https://custom-registry.com')
83
- t.is(scopedPackageJson.publishConfig.access, 'public')
84
- t.is(scopedPackageJson.publishConfig.exports, undefined)
85
- t.is(scopedPackageJson.publishConfig.tag, undefined)
86
- })
87
-
88
- test('should handle package without publishConfig', async (t) => {
89
- const { tmpDir, packageJsonPath } = t.context
90
-
91
- // Create a package.json without publishConfig
92
- const packageJson = {
93
- name: 'test-package-no-config',
94
- version: '1.0.0',
95
- napi: {
96
- binaryName: 'test-package-no-config',
97
- targets: ['x86_64-unknown-linux-gnu'],
98
- },
99
- }
100
-
101
- await writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2))
102
-
103
- await createNpmDirs({
104
- cwd: tmpDir,
105
- packageJsonPath: 'package.json',
106
- })
107
-
108
- // Check that the scoped package directory was created
109
- const scopedDir = join(tmpDir, 'npm', 'linux-x64-gnu')
110
- t.true(existsSync(scopedDir))
111
-
112
- // Read the generated package.json for the scoped package
113
- const scopedPackageJsonPath = join(scopedDir, 'package.json')
114
- const scopedPackageJson = JSON.parse(
115
- await readFile(scopedPackageJsonPath, 'utf-8'),
116
- )
117
-
118
- // Verify that publishConfig is not present when not in source
119
- t.is(scopedPackageJson.publishConfig, undefined)
120
- })
121
-
122
- test('should preserve only registry and access in publishConfig', async (t) => {
123
- const { tmpDir, packageJsonPath } = t.context
124
-
125
- // Create a package.json with minimal publishConfig
126
- const packageJson = {
127
- name: 'test-package-minimal',
128
- version: '1.0.0',
129
- publishConfig: {
130
- registry: 'https://npm.company.com',
131
- access: 'restricted',
132
- },
133
- napi: {
134
- binaryName: 'test-package-minimal',
135
- targets: ['aarch64-apple-darwin'],
136
- },
137
- }
138
-
139
- await writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2))
140
-
141
- await createNpmDirs({
142
- cwd: tmpDir,
143
- packageJsonPath: 'package.json',
144
- })
145
-
146
- // Check that the scoped package directory was created
147
- const scopedDir = join(tmpDir, 'npm', 'darwin-arm64')
148
- t.true(existsSync(scopedDir))
149
-
150
- // Read the generated package.json for the scoped package
151
- const scopedPackageJsonPath = join(scopedDir, 'package.json')
152
- const scopedPackageJson = JSON.parse(
153
- await readFile(scopedPackageJsonPath, 'utf-8'),
154
- )
155
-
156
- // Verify that publishConfig contains exactly registry and access
157
- t.truthy(scopedPackageJson.publishConfig)
158
- t.is(scopedPackageJson.publishConfig.registry, 'https://npm.company.com')
159
- t.is(scopedPackageJson.publishConfig.access, 'restricted')
160
- t.is(Object.keys(scopedPackageJson.publishConfig).length, 2)
161
- })
162
-
163
- test('should handle WASM targets correctly with publishConfig', async (t) => {
164
- const { tmpDir, packageJsonPath } = t.context
165
-
166
- // Create a package.json for WASM target
167
- const packageJson = {
168
- name: 'test-wasm-package',
169
- version: '1.0.0',
170
- publishConfig: {
171
- registry: 'https://wasm-registry.com',
172
- access: 'public',
173
- exports: './wasm.js',
174
- browser: './browser.js',
175
- },
176
- napi: {
177
- binaryName: 'test-wasm-package',
178
- targets: ['wasm32-wasi-preview1-threads'],
179
- },
180
- }
181
-
182
- await writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2))
183
-
184
- await createNpmDirs({
185
- cwd: tmpDir,
186
- packageJsonPath: 'package.json',
187
- })
188
-
189
- // Check that the scoped package directory was created
190
- const scopedDir = join(tmpDir, 'npm', 'wasm32-wasi')
191
- t.true(existsSync(scopedDir))
192
-
193
- // Read the generated package.json for the scoped package
194
- const scopedPackageJsonPath = join(scopedDir, 'package.json')
195
- const scopedPackageJson = JSON.parse(
196
- await readFile(scopedPackageJsonPath, 'utf-8'),
197
- )
198
-
199
- // Verify that publishConfig is correctly filtered for WASM too
200
- t.truthy(scopedPackageJson.publishConfig)
201
- t.is(scopedPackageJson.publishConfig.registry, 'https://wasm-registry.com')
202
- t.is(scopedPackageJson.publishConfig.access, 'public')
203
- t.is(scopedPackageJson.publishConfig.exports, undefined)
204
- t.is(scopedPackageJson.publishConfig.browser, undefined)
205
-
206
- // Verify WASM-specific fields are set correctly
207
- t.truthy(scopedPackageJson.main)
208
- t.truthy(scopedPackageJson.browser)
209
- t.truthy(scopedPackageJson.dependencies)
210
- })