@napi-rs/cli 3.6.1 → 3.7.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.
@@ -42,6 +42,11 @@ export interface UserNapiConfig {
42
42
  */
43
43
  constEnum?: boolean
44
44
 
45
+ /**
46
+ * Emit `#[napi(string_enum)]` enums as runtime enums (`export declare enum`) under `--no-const-enum`. Default: type-only union.
47
+ */
48
+ runtimeStringEnum?: boolean
49
+
45
50
  /**
46
51
  * dts header prepend to the generated dts file
47
52
  */
@@ -155,7 +160,10 @@ export interface CommonPackageJsonFields {
155
160
  export type NapiConfig = Required<
156
161
  Pick<UserNapiConfig, 'binaryName' | 'packageName' | 'npmClient'>
157
162
  > &
158
- Pick<UserNapiConfig, 'wasm' | 'dtsHeader' | 'dtsHeaderFile' | 'constEnum'> & {
163
+ Pick<
164
+ UserNapiConfig,
165
+ 'wasm' | 'dtsHeader' | 'dtsHeaderFile' | 'constEnum' | 'runtimeStringEnum'
166
+ > & {
159
167
  targets: Target[]
160
168
  packageJson: CommonPackageJsonFields
161
169
  }
package/src/utils/misc.ts CHANGED
@@ -43,6 +43,27 @@ export function pick<O, K extends keyof O>(o: O, ...keys: K[]): Pick<O, K> {
43
43
  }, {} as O)
44
44
  }
45
45
 
46
+ function isPlainObject(value: unknown): value is Record<string, any> {
47
+ return value !== null && typeof value === 'object' && !Array.isArray(value)
48
+ }
49
+
50
+ function mergePackageJson(
51
+ old: Record<string, any>,
52
+ partial: Record<string, any>,
53
+ ): Record<string, any> {
54
+ const merged = { ...old }
55
+
56
+ for (const [key, value] of Object.entries(partial)) {
57
+ if (isPlainObject(merged[key]) && isPlainObject(value)) {
58
+ merged[key] = mergePackageJson(merged[key], value)
59
+ } else {
60
+ merged[key] = value
61
+ }
62
+ }
63
+
64
+ return merged
65
+ }
66
+
46
67
  export async function updatePackageJson(
47
68
  path: string,
48
69
  partial: Record<string, any>,
@@ -53,7 +74,10 @@ export async function updatePackageJson(
53
74
  return
54
75
  }
55
76
  const old = JSON.parse(await readFileAsync(path, 'utf8'))
56
- await writeFileAsync(path, JSON.stringify({ ...old, ...partial }, null, 2))
77
+ await writeFileAsync(
78
+ path,
79
+ JSON.stringify(mergePackageJson(old, partial), null, 2),
80
+ )
57
81
  }
58
82
 
59
83
  export const CLI_VERSION = pkgJson.version
@@ -29,9 +29,26 @@ interface TypeDefLine {
29
29
  js_mod?: string
30
30
  }
31
31
 
32
+ /**
33
+ * Render a single intermediate type-def line as the TypeScript source it
34
+ * should produce in `index.d.ts`.
35
+ *
36
+ * @param line - The intermediate type-def entry to render.
37
+ * @param constEnum - When true, emit numeric and string `#[napi]` enums as
38
+ * `const enum`. When false (`--no-const-enum`), numeric enums become
39
+ * regular runtime enums and string enums fall back to a type-only union
40
+ * unless `runtimeStringEnum` is also true.
41
+ * @param runtimeStringEnum - When true under `--no-const-enum`, emit
42
+ * `#[napi(string_enum)]` as a runtime enum (`export declare enum`)
43
+ * instead of a type-only union. No-op when `constEnum` is true.
44
+ * @param ident - Indentation level applied to the rendered output.
45
+ * @param ambient - When true, emit declarations in the ambient form used
46
+ * inside `declare namespace` blocks (e.g. drop the `declare` keyword).
47
+ */
32
48
  function prettyPrint(
33
49
  line: TypeDefLine,
34
50
  constEnum: boolean,
51
+ runtimeStringEnum: boolean,
35
52
  ident: number,
36
53
  ambient = false,
37
54
  ): string {
@@ -45,18 +62,22 @@ function prettyPrint(
45
62
  s += `export type ${line.name} = \n${line.def}`
46
63
  break
47
64
 
48
- case TypeDefKind.Enum:
65
+ case TypeDefKind.Enum: {
49
66
  const enumName = constEnum ? 'const enum' : 'enum'
50
67
  s += `${exportDeclare(ambient)} ${enumName} ${line.name} {\n${line.def}\n}`
51
68
  break
69
+ }
52
70
 
53
- case TypeDefKind.StringEnum:
71
+ case TypeDefKind.StringEnum: {
54
72
  if (constEnum) {
55
73
  s += `${exportDeclare(ambient)} const enum ${line.name} {\n${line.def}\n}`
74
+ } else if (runtimeStringEnum) {
75
+ s += `${exportDeclare(ambient)} enum ${line.name} {\n${line.def}\n}`
56
76
  } else {
57
77
  s += `export type ${line.name} = ${line.def.replaceAll(/.*=/g, '').replaceAll(',', '|')};`
58
78
  }
59
79
  break
80
+ }
60
81
 
61
82
  case TypeDefKind.Struct:
62
83
  const extendsDef = line.extends ? ` extends ${line.extends}` : ''
@@ -97,9 +118,20 @@ function exportDeclare(ambient: boolean): string {
97
118
  return 'export declare'
98
119
  }
99
120
 
121
+ /**
122
+ * Read the napi-derive-emitted intermediate type-def file and render its
123
+ * entries into the `index.d.ts` source string plus the list of names to
124
+ * re-export from `index.js`.
125
+ *
126
+ * @param intermediateTypeFile - Path to the JSONL type-def file produced
127
+ * by napi-derive (one entry per `#[napi]` item).
128
+ * @param constEnum - See {@link prettyPrint}.
129
+ * @param runtimeStringEnum - See {@link prettyPrint}. Defaults to `false`.
130
+ */
100
131
  export async function processTypeDef(
101
132
  intermediateTypeFile: string,
102
133
  constEnum: boolean,
134
+ runtimeStringEnum: boolean = false,
103
135
  ) {
104
136
  const exports: string[] = []
105
137
  const defs = await readIntermediateTypeFile(intermediateTypeFile)
@@ -126,7 +158,7 @@ export async function processTypeDef(
126
158
  default:
127
159
  break
128
160
  }
129
- return prettyPrint(def, constEnum, 0)
161
+ return prettyPrint(def, constEnum, runtimeStringEnum, 0)
130
162
  })
131
163
  .join('\n\n')
132
164
  } else {
@@ -134,7 +166,8 @@ export async function processTypeDef(
134
166
  let declaration = ''
135
167
  declaration += `export declare namespace ${namespace} {\n`
136
168
  for (const def of defs) {
137
- declaration += prettyPrint(def, constEnum, 2, true) + '\n'
169
+ declaration +=
170
+ prettyPrint(def, constEnum, runtimeStringEnum, 2, true) + '\n'
138
171
  }
139
172
  declaration += '}'
140
173
  return declaration
@@ -25,6 +25,10 @@ const NAPI_VERSION_MATRIX = new Map<NapiVersion, string>([
25
25
  [NapiVersion.Napi9, '18.17.0 | 20.3.0 | 21.1.0'],
26
26
  ])
27
27
 
28
+ export const SUPPORTED_NAPI_VERSIONS = Object.values(NapiVersion).filter(
29
+ (v): v is NapiVersion => typeof v === 'number',
30
+ )
31
+
28
32
  interface NodeVersion {
29
33
  major: number
30
34
  minor: number