@napi-rs/cli 3.6.2 → 3.7.1
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.
- package/dist/cli.js +107 -61
- package/dist/index.cjs +111 -65
- package/dist/index.d.cts +29 -13
- package/dist/index.d.ts +29 -13
- package/dist/index.js +107 -61
- package/package.json +19 -19
- package/src/api/build.ts +19 -1
- package/src/api/new.ts +1 -1
- package/src/api/templates/js-binding.ts +14 -4
- package/src/api/templates/load-wasi-template.ts +1 -1
- package/src/api/templates/wasi-worker-template.ts +2 -3
- package/src/api/universalize.ts +27 -5
- package/src/def/build.ts +10 -0
- package/src/utils/config.ts +9 -1
- package/src/utils/typegen.ts +37 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@napi-rs/cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.7.1",
|
|
4
4
|
"description": "Cli tools for napi-rs",
|
|
5
5
|
"author": "LongYinan <lynweklm@gmail.com>",
|
|
6
6
|
"homepage": "https://napi.rs/",
|
|
@@ -60,35 +60,35 @@
|
|
|
60
60
|
"url": "https://github.com/napi-rs/napi-rs/issues"
|
|
61
61
|
},
|
|
62
62
|
"dependencies": {
|
|
63
|
-
"@inquirer/prompts": "^8.
|
|
63
|
+
"@inquirer/prompts": "^8.5.2",
|
|
64
64
|
"@napi-rs/cross-toolchain": "^1.0.3",
|
|
65
65
|
"@napi-rs/wasm-tools": "^1.0.1",
|
|
66
66
|
"@octokit/rest": "^22.0.1",
|
|
67
67
|
"clipanion": "^4.0.0-rc.4",
|
|
68
68
|
"colorette": "^2.0.20",
|
|
69
|
-
"emnapi": "^1.
|
|
70
|
-
"es-toolkit": "^1.
|
|
71
|
-
"js-yaml": "^4.
|
|
72
|
-
"obug": "^2.
|
|
73
|
-
"semver": "^7.
|
|
69
|
+
"emnapi": "^1.11.0",
|
|
70
|
+
"es-toolkit": "^1.47.0",
|
|
71
|
+
"js-yaml": "^4.2.0",
|
|
72
|
+
"obug": "^2.1.2",
|
|
73
|
+
"semver": "^7.8.2",
|
|
74
74
|
"typanion": "^3.14.0"
|
|
75
75
|
},
|
|
76
76
|
"devDependencies": {
|
|
77
|
-
"@emnapi/runtime": "^1.
|
|
77
|
+
"@emnapi/runtime": "^1.11.0",
|
|
78
78
|
"@oxc-node/core": "^0.1.0",
|
|
79
79
|
"@std/toml": "npm:@jsr/std__toml@^1.0.11",
|
|
80
80
|
"@types/inquirer": "^9.0.9",
|
|
81
81
|
"@types/js-yaml": "^4.0.9",
|
|
82
|
-
"@types/node": "^
|
|
82
|
+
"@types/node": "^25.9.2",
|
|
83
83
|
"@types/semver": "^7.7.1",
|
|
84
|
-
"ava": "^
|
|
85
|
-
"empathic": "^2.0.
|
|
84
|
+
"ava": "^8.0.1",
|
|
85
|
+
"empathic": "^2.0.1",
|
|
86
86
|
"env-paths": "^4.0.0",
|
|
87
|
-
"oxc-parser": "^0.
|
|
88
|
-
"prettier": "^3.
|
|
89
|
-
"tsdown": "^0.
|
|
87
|
+
"oxc-parser": "^0.135.0",
|
|
88
|
+
"prettier": "^3.8.3",
|
|
89
|
+
"tsdown": "^0.22.2",
|
|
90
90
|
"tslib": "^2.8.1",
|
|
91
|
-
"typescript": "^
|
|
91
|
+
"typescript": "^6.0.3"
|
|
92
92
|
},
|
|
93
93
|
"peerDependencies": {
|
|
94
94
|
"@emnapi/runtime": "^1.7.1"
|
|
@@ -105,12 +105,12 @@
|
|
|
105
105
|
"scripts": {
|
|
106
106
|
"codegen": "node --import @oxc-node/core/register ./codegen/index.ts",
|
|
107
107
|
"build": "tsdown",
|
|
108
|
-
"test": "node --import @oxc-node/core/register ../node_modules/ava/entrypoints/cli.
|
|
108
|
+
"test": "node --import @oxc-node/core/register ../node_modules/ava/entrypoints/cli.js"
|
|
109
109
|
},
|
|
110
110
|
"ava": {
|
|
111
|
-
"extensions":
|
|
112
|
-
"ts"
|
|
113
|
-
|
|
111
|
+
"extensions": [
|
|
112
|
+
"ts"
|
|
113
|
+
],
|
|
114
114
|
"timeout": "2m",
|
|
115
115
|
"workerThreads": false,
|
|
116
116
|
"files": [
|
package/src/api/build.ts
CHANGED
|
@@ -874,6 +874,8 @@ class Builder {
|
|
|
874
874
|
configDtsHeader: this.config.dtsHeader,
|
|
875
875
|
configDtsHeaderFile: this.config.dtsHeaderFile,
|
|
876
876
|
constEnum: this.options.constEnum ?? this.config.constEnum,
|
|
877
|
+
runtimeStringEnum:
|
|
878
|
+
this.options.runtimeStringEnum ?? this.config.runtimeStringEnum,
|
|
877
879
|
cwd: this.options.cwd,
|
|
878
880
|
})
|
|
879
881
|
|
|
@@ -1050,9 +1052,16 @@ export interface GenerateTypeDefOptions {
|
|
|
1050
1052
|
configDtsHeader?: string
|
|
1051
1053
|
configDtsHeaderFile?: string
|
|
1052
1054
|
constEnum?: boolean
|
|
1055
|
+
runtimeStringEnum?: boolean
|
|
1053
1056
|
cwd: string
|
|
1054
1057
|
}
|
|
1055
1058
|
|
|
1059
|
+
/**
|
|
1060
|
+
* Walk the napi-derive intermediate type-def directory, render every entry
|
|
1061
|
+
* into TypeScript via {@link processTypeDef}, and return the concatenated
|
|
1062
|
+
* `.d.ts` source plus the list of identifiers to re-export from
|
|
1063
|
+
* `index.js`.
|
|
1064
|
+
*/
|
|
1056
1065
|
export async function generateTypeDef(
|
|
1057
1066
|
options: GenerateTypeDefOptions,
|
|
1058
1067
|
): Promise<{ exports: string[]; dts: string }> {
|
|
@@ -1097,11 +1106,20 @@ export async function generateTypeDef(
|
|
|
1097
1106
|
.filter((file) => file.isFile())
|
|
1098
1107
|
.sort((a, b) => a.name.localeCompare(b.name))
|
|
1099
1108
|
|
|
1109
|
+
const constEnum = options.constEnum ?? true
|
|
1110
|
+
const runtimeStringEnum = options.runtimeStringEnum ?? false
|
|
1111
|
+
if (runtimeStringEnum && constEnum) {
|
|
1112
|
+
debug.warn(
|
|
1113
|
+
'`--runtime-string-enum` has no effect when `--const-enum` is enabled (the default). Pass `--no-const-enum` to activate runtime string enum emission.',
|
|
1114
|
+
)
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1100
1117
|
const processedTypeDefs = await Promise.all(
|
|
1101
1118
|
typeDefFiles.map((file) =>
|
|
1102
1119
|
processTypeDef(
|
|
1103
1120
|
join(options.typeDefDir, file.name),
|
|
1104
|
-
|
|
1121
|
+
constEnum,
|
|
1122
|
+
runtimeStringEnum,
|
|
1105
1123
|
),
|
|
1106
1124
|
),
|
|
1107
1125
|
)
|
package/src/api/new.ts
CHANGED
|
@@ -193,7 +193,7 @@ async function updateCargoTomlTypeDef(
|
|
|
193
193
|
? { version: napiDeriveDependency }
|
|
194
194
|
: { ...napiDeriveDependency }
|
|
195
195
|
|
|
196
|
-
const existingFeatures = Array.isArray(dependencyConfig.features)
|
|
196
|
+
const existingFeatures: string[] = Array.isArray(dependencyConfig.features)
|
|
197
197
|
? dependencyConfig.features.filter(
|
|
198
198
|
(feature: unknown): feature is string => typeof feature === 'string',
|
|
199
199
|
)
|
|
@@ -227,23 +227,33 @@ function requireNative() {
|
|
|
227
227
|
|
|
228
228
|
nativeBinding = requireNative()
|
|
229
229
|
|
|
230
|
-
|
|
230
|
+
// NAPI_RS_FORCE_WASI is a tri-state flag:
|
|
231
|
+
// unset / any other value → native binding preferred, WASI is only a fallback
|
|
232
|
+
// 'true' → force WASI fallback even if native loaded
|
|
233
|
+
// 'error' → force WASI and throw if no WASI binding is found
|
|
234
|
+
// Treating any non-empty string as truthy (the historical behavior) meant
|
|
235
|
+
// NAPI_RS_FORCE_WASI=false, NAPI_RS_FORCE_WASI=0, etc. inadvertently triggered
|
|
236
|
+
// the WASI path, causing ENOENT for packages shipped without a .wasi.cjs file.
|
|
237
|
+
const forceWasi =
|
|
238
|
+
process.env.NAPI_RS_FORCE_WASI === 'true' || process.env.NAPI_RS_FORCE_WASI === 'error'
|
|
239
|
+
|
|
240
|
+
if (!nativeBinding || forceWasi) {
|
|
231
241
|
let wasiBinding = null
|
|
232
242
|
let wasiBindingError = null
|
|
233
243
|
try {
|
|
234
244
|
wasiBinding = require('./${localName}.wasi.cjs')
|
|
235
245
|
nativeBinding = wasiBinding
|
|
236
246
|
} catch (err) {
|
|
237
|
-
if (
|
|
247
|
+
if (forceWasi) {
|
|
238
248
|
wasiBindingError = err
|
|
239
249
|
}
|
|
240
250
|
}
|
|
241
|
-
if (!nativeBinding ||
|
|
251
|
+
if (!nativeBinding || forceWasi) {
|
|
242
252
|
try {
|
|
243
253
|
wasiBinding = require('${pkgName}-wasm32-wasi')
|
|
244
254
|
nativeBinding = wasiBinding
|
|
245
255
|
} catch (err) {
|
|
246
|
-
if (
|
|
256
|
+
if (forceWasi) {
|
|
247
257
|
if (!wasiBindingError) {
|
|
248
258
|
wasiBindingError = err
|
|
249
259
|
} else {
|
|
@@ -34,7 +34,7 @@ const __wasi = new __WASI({
|
|
|
34
34
|
: ''
|
|
35
35
|
|
|
36
36
|
const workerErrorHandler = errorEvent
|
|
37
|
-
? ` worker.addEventListener('
|
|
37
|
+
? ` worker.addEventListener('message', (event) => {
|
|
38
38
|
if (event.data && typeof event.data === 'object' && event.data.type === 'error') {
|
|
39
39
|
window.dispatchEvent(new CustomEvent('napi-rs-worker-error', { detail: event.data }))
|
|
40
40
|
}
|
|
@@ -109,10 +109,9 @@ const fs = createFsProxy(__memfsExported)`
|
|
|
109
109
|
errorOutputs.length = 0
|
|
110
110
|
}`
|
|
111
111
|
: ''
|
|
112
|
+
const errorOutputsDecl = errorEvent ? `\nconst errorOutputs = []\n` : ''
|
|
112
113
|
return `${fsImport}
|
|
113
|
-
|
|
114
|
-
const errorOutputs = []
|
|
115
|
-
|
|
114
|
+
${errorOutputsDecl}
|
|
116
115
|
const handler = new MessageHandler({
|
|
117
116
|
onLoad({ wasmModule, wasmMemory }) {
|
|
118
117
|
${wasiCreation}
|
package/src/api/universalize.ts
CHANGED
|
@@ -12,14 +12,32 @@ import { UniArchsByPlatform } from '../utils/target.js'
|
|
|
12
12
|
|
|
13
13
|
const debug = debugFactory('universalize')
|
|
14
14
|
|
|
15
|
+
function createDarwinUniversalBinary(inputs: string[], output: string) {
|
|
16
|
+
const result = spawnSync('lipo', ['-create', '-output', output, ...inputs], {
|
|
17
|
+
stdio: 'inherit',
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
if (result.error) {
|
|
21
|
+
throw result.error
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (result.signal) {
|
|
25
|
+
throw new Error(
|
|
26
|
+
`Failed to create universal binary '${output}': lipo terminated with signal ${result.signal}.`,
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (result.status !== 0) {
|
|
31
|
+
throw new Error(
|
|
32
|
+
`Failed to create universal binary '${output}': lipo exited with status ${result.status}.`,
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
15
37
|
const universalizers: Partial<
|
|
16
38
|
Record<NodeJS.Platform, (inputs: string[], output: string) => void>
|
|
17
39
|
> = {
|
|
18
|
-
darwin:
|
|
19
|
-
spawnSync('lipo', ['-create', '-output', output, ...inputs], {
|
|
20
|
-
stdio: 'inherit',
|
|
21
|
-
})
|
|
22
|
-
},
|
|
40
|
+
darwin: createDarwinUniversalBinary,
|
|
23
41
|
}
|
|
24
42
|
|
|
25
43
|
export async function universalizeBinaries(userOptions: UniversalizeOptions) {
|
|
@@ -77,5 +95,9 @@ export async function universalizeBinaries(userOptions: UniversalizeOptions) {
|
|
|
77
95
|
|
|
78
96
|
universalizers[process.platform]?.(srcFiles, output)
|
|
79
97
|
|
|
98
|
+
if (!(await fileExists(output))) {
|
|
99
|
+
throw new Error(`Universal binary was not created: ${output}`)
|
|
100
|
+
}
|
|
101
|
+
|
|
80
102
|
debug(`Produced universal binary: ${output}`)
|
|
81
103
|
}
|
package/src/def/build.ts
CHANGED
|
@@ -55,6 +55,11 @@ export abstract class BaseBuildCommand extends Command {
|
|
|
55
55
|
description: 'Whether generate const enum for typescript bindings',
|
|
56
56
|
})
|
|
57
57
|
|
|
58
|
+
runtimeStringEnum?: boolean = Option.Boolean('--runtime-string-enum', {
|
|
59
|
+
description:
|
|
60
|
+
'Emit `#[napi(string_enum)]` enums as runtime enums (`export declare enum`) under `--no-const-enum`. Default: type-only union.',
|
|
61
|
+
})
|
|
62
|
+
|
|
58
63
|
jsBinding?: string = Option.String('--js', {
|
|
59
64
|
description:
|
|
60
65
|
'Path and filename of generated JS binding file. Only works with `--platform` flag. Relative to `--output-dir`.',
|
|
@@ -157,6 +162,7 @@ export abstract class BaseBuildCommand extends Command {
|
|
|
157
162
|
platform: this.platform,
|
|
158
163
|
jsPackageName: this.jsPackageName,
|
|
159
164
|
constEnum: this.constEnum,
|
|
165
|
+
runtimeStringEnum: this.runtimeStringEnum,
|
|
160
166
|
jsBinding: this.jsBinding,
|
|
161
167
|
noJsBinding: this.noJsBinding,
|
|
162
168
|
dts: this.dts,
|
|
@@ -225,6 +231,10 @@ export interface BuildOptions {
|
|
|
225
231
|
* Whether generate const enum for typescript bindings
|
|
226
232
|
*/
|
|
227
233
|
constEnum?: boolean
|
|
234
|
+
/**
|
|
235
|
+
* Emit `#[napi(string_enum)]` enums as runtime enums (`export declare enum`) under `--no-const-enum`. Default: type-only union.
|
|
236
|
+
*/
|
|
237
|
+
runtimeStringEnum?: boolean
|
|
228
238
|
/**
|
|
229
239
|
* Path and filename of generated JS binding file. Only works with `--platform` flag. Relative to `--output-dir`.
|
|
230
240
|
*/
|
package/src/utils/config.ts
CHANGED
|
@@ -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<
|
|
163
|
+
Pick<
|
|
164
|
+
UserNapiConfig,
|
|
165
|
+
'wasm' | 'dtsHeader' | 'dtsHeaderFile' | 'constEnum' | 'runtimeStringEnum'
|
|
166
|
+
> & {
|
|
159
167
|
targets: Target[]
|
|
160
168
|
packageJson: CommonPackageJsonFields
|
|
161
169
|
}
|
package/src/utils/typegen.ts
CHANGED
|
@@ -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 +=
|
|
169
|
+
declaration +=
|
|
170
|
+
prettyPrint(def, constEnum, runtimeStringEnum, 2, true) + '\n'
|
|
138
171
|
}
|
|
139
172
|
declaration += '}'
|
|
140
173
|
return declaration
|