@napi-rs/cli 3.7.3 → 3.7.4
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 +41 -14
- package/dist/index.cjs +41 -14
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +41 -14
- package/package.json +2 -2
- package/src/api/build.ts +8 -6
- package/src/utils/__tests__/__fixtures__/optional-napi-derive/Cargo.toml +3 -0
- package/src/utils/__tests__/__fixtures__/optional-napi-derive/main-crate/Cargo.toml +8 -0
- package/src/utils/__tests__/__fixtures__/optional-napi-derive/main-crate/src/lib.rs +1 -0
- package/src/utils/__tests__/__fixtures__/optional-napi-derive/napi-derive/Cargo.toml +5 -0
- package/src/utils/__tests__/__fixtures__/optional-napi-derive/napi-derive/src/lib.rs +1 -0
- package/src/utils/__tests__/__fixtures__/optional-napi-derive/with-optional-derive/Cargo.toml +11 -0
- package/src/utils/__tests__/__fixtures__/optional-napi-derive/with-optional-derive/src/lib.rs +1 -0
- package/src/utils/__tests__/metadata.spec.ts +43 -2
- package/src/utils/metadata.ts +94 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@napi-rs/cli",
|
|
3
|
-
"version": "3.7.
|
|
3
|
+
"version": "3.7.4",
|
|
4
4
|
"description": "Cli tools for napi-rs",
|
|
5
5
|
"author": "LongYinan <lynweklm@gmail.com>",
|
|
6
6
|
"homepage": "https://napi.rs/",
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"ava": "^8.0.1",
|
|
85
85
|
"empathic": "^2.0.1",
|
|
86
86
|
"env-paths": "^4.0.0",
|
|
87
|
-
"oxc-parser": "^0.
|
|
87
|
+
"oxc-parser": "^0.140.0",
|
|
88
88
|
"prettier": "^3.8.3",
|
|
89
89
|
"tsdown": "^0.22.2",
|
|
90
90
|
"tslib": "^2.8.1",
|
package/src/api/build.ts
CHANGED
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
DEFAULT_TYPE_DEF_HEADER,
|
|
17
17
|
fileExists,
|
|
18
18
|
getSystemDefaultTarget,
|
|
19
|
+
getNapiDeriveDependentCrates,
|
|
19
20
|
getTargetLinker,
|
|
20
21
|
mkdirAsync,
|
|
21
22
|
type NapiConfig,
|
|
@@ -79,7 +80,11 @@ export async function buildProject(rawOptions: BuildOptions) {
|
|
|
79
80
|
const resolvePath = (...paths: string[]) => resolve(options.cwd, ...paths)
|
|
80
81
|
|
|
81
82
|
const manifestPath = resolvePath(options.manifestPath ?? 'Cargo.toml')
|
|
82
|
-
const metadata = await parseMetadata(manifestPath
|
|
83
|
+
const metadata = await parseMetadata(manifestPath, {
|
|
84
|
+
features: options.features,
|
|
85
|
+
allFeatures: options.allFeatures,
|
|
86
|
+
noDefaultFeatures: options.noDefaultFeatures,
|
|
87
|
+
})
|
|
83
88
|
|
|
84
89
|
const crate = metadata.packages.find((p) => {
|
|
85
90
|
// package with given name
|
|
@@ -734,11 +739,8 @@ class Builder {
|
|
|
734
739
|
|
|
735
740
|
private setForceBuildEnvs(typeDefTmpFolder: string) {
|
|
736
741
|
// dynamically check all napi-rs deps and set `NAPI_FORCE_BUILD_{uppercase(snake_case(name))} = timestamp`
|
|
737
|
-
this.metadata.
|
|
738
|
-
if (
|
|
739
|
-
crate.dependencies.some((d) => d.name === 'napi-derive') &&
|
|
740
|
-
!existsSync(join(typeDefTmpFolder, crate.name))
|
|
741
|
-
) {
|
|
742
|
+
getNapiDeriveDependentCrates(this.metadata).forEach((crate) => {
|
|
743
|
+
if (!existsSync(join(typeDefTmpFolder, crate.name))) {
|
|
742
744
|
this.envs[
|
|
743
745
|
`NAPI_FORCE_BUILD_${crate.name.replace(/-/g, '_').toUpperCase()}`
|
|
744
746
|
] = Date.now().toString()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import { unlink, writeFile } from 'node:fs/promises'
|
|
2
2
|
import { tmpdir } from 'node:os'
|
|
3
|
-
import { join } from 'node:path'
|
|
3
|
+
import { dirname, join } from 'node:path'
|
|
4
|
+
import { fileURLToPath } from 'node:url'
|
|
4
5
|
|
|
5
6
|
import test from 'ava'
|
|
6
7
|
|
|
7
|
-
import { parseMetadata } from '../metadata.js'
|
|
8
|
+
import { getNapiDeriveDependentCrates, parseMetadata } from '../metadata.js'
|
|
9
|
+
|
|
10
|
+
const FIXTURE_WORKSPACE = join(
|
|
11
|
+
dirname(fileURLToPath(import.meta.url)),
|
|
12
|
+
'__fixtures__',
|
|
13
|
+
'optional-napi-derive',
|
|
14
|
+
)
|
|
8
15
|
|
|
9
16
|
test('should surface child process startup errors', async (t) => {
|
|
10
17
|
const manifestPath = join(
|
|
@@ -26,3 +33,37 @@ test('should surface child process startup errors', async (t) => {
|
|
|
26
33
|
await unlink(manifestPath)
|
|
27
34
|
}
|
|
28
35
|
})
|
|
36
|
+
|
|
37
|
+
test('should exclude crates whose optional napi-derive dependency is disabled', async (t) => {
|
|
38
|
+
const metadata = await parseMetadata(
|
|
39
|
+
join(FIXTURE_WORKSPACE, 'main-crate', 'Cargo.toml'),
|
|
40
|
+
)
|
|
41
|
+
const dependentCrates = getNapiDeriveDependentCrates(metadata).map(
|
|
42
|
+
(crate) => crate.name,
|
|
43
|
+
)
|
|
44
|
+
t.deepEqual(dependentCrates, ['main-crate'])
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
test('should include crates whose optional napi-derive dependency is enabled via build features', async (t) => {
|
|
48
|
+
const metadata = await parseMetadata(
|
|
49
|
+
join(FIXTURE_WORKSPACE, 'with-optional-derive', 'Cargo.toml'),
|
|
50
|
+
{
|
|
51
|
+
features: ['node'],
|
|
52
|
+
},
|
|
53
|
+
)
|
|
54
|
+
const dependentCrates = getNapiDeriveDependentCrates(metadata).map(
|
|
55
|
+
(crate) => crate.name,
|
|
56
|
+
)
|
|
57
|
+
t.true(dependentCrates.includes('with-optional-derive'))
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
test('should fall back to declared dependencies when the resolve graph is unavailable', async (t) => {
|
|
61
|
+
const metadata = await parseMetadata(
|
|
62
|
+
join(FIXTURE_WORKSPACE, 'main-crate', 'Cargo.toml'),
|
|
63
|
+
)
|
|
64
|
+
metadata.resolve = null
|
|
65
|
+
const dependentCrates = getNapiDeriveDependentCrates(metadata)
|
|
66
|
+
.map((crate) => crate.name)
|
|
67
|
+
.sort()
|
|
68
|
+
t.deepEqual(dependentCrates, ['main-crate', 'with-optional-derive'])
|
|
69
|
+
})
|
package/src/utils/metadata.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process'
|
|
2
2
|
import fs from 'node:fs'
|
|
3
3
|
|
|
4
|
+
import { debugFactory } from './log.js'
|
|
5
|
+
|
|
6
|
+
const debug = debugFactory('metadata')
|
|
7
|
+
|
|
4
8
|
export type CrateTargetKind =
|
|
5
9
|
| 'bin'
|
|
6
10
|
| 'example'
|
|
@@ -40,23 +44,110 @@ export interface Crate {
|
|
|
40
44
|
}>
|
|
41
45
|
}
|
|
42
46
|
|
|
47
|
+
export interface CrateResolveNode {
|
|
48
|
+
id: string
|
|
49
|
+
deps: Array<{
|
|
50
|
+
name: string
|
|
51
|
+
pkg: string
|
|
52
|
+
}>
|
|
53
|
+
features: string[]
|
|
54
|
+
}
|
|
55
|
+
|
|
43
56
|
export interface CargoWorkspaceMetadata {
|
|
44
57
|
version: number
|
|
45
58
|
packages: Crate[]
|
|
46
59
|
workspace_members: string[]
|
|
47
60
|
target_directory: string
|
|
48
61
|
workspace_root: string
|
|
62
|
+
resolve?: {
|
|
63
|
+
nodes: CrateResolveNode[]
|
|
64
|
+
root: string | null
|
|
65
|
+
} | null
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface CargoFeatureOptions {
|
|
69
|
+
features?: string[]
|
|
70
|
+
allFeatures?: boolean
|
|
71
|
+
noDefaultFeatures?: boolean
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Crates that will actually compile `napi-derive` in this build, and are therefore
|
|
75
|
+
// expected to emit a type-def intermediate file. Prefer the resolved dependency graph:
|
|
76
|
+
// a crate whose `napi-derive` dependency is optional and stays disabled never emits a
|
|
77
|
+
// type-def file, so force-building it would invalidate cargo's fingerprint on every
|
|
78
|
+
// build without ever satisfying the type-def check. Fall back to declared dependencies
|
|
79
|
+
// when the resolve graph is unavailable.
|
|
80
|
+
export function getNapiDeriveDependentCrates(
|
|
81
|
+
metadata: CargoWorkspaceMetadata,
|
|
82
|
+
): Crate[] {
|
|
83
|
+
const resolveNodes = metadata.resolve?.nodes
|
|
84
|
+
if (!resolveNodes) {
|
|
85
|
+
return metadata.packages.filter((crate) =>
|
|
86
|
+
crate.dependencies.some((d) => d.name === 'napi-derive'),
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
const napiDerivePackageIds = new Set(
|
|
90
|
+
metadata.packages.filter((p) => p.name === 'napi-derive').map((p) => p.id),
|
|
91
|
+
)
|
|
92
|
+
const dependentPackageIds = new Set(
|
|
93
|
+
resolveNodes
|
|
94
|
+
.filter((node) =>
|
|
95
|
+
node.deps.some((dep) => napiDerivePackageIds.has(dep.pkg)),
|
|
96
|
+
)
|
|
97
|
+
.map((node) => node.id),
|
|
98
|
+
)
|
|
99
|
+
return metadata.packages.filter((crate) => dependentPackageIds.has(crate.id))
|
|
49
100
|
}
|
|
50
101
|
|
|
51
|
-
export async function parseMetadata(
|
|
102
|
+
export async function parseMetadata(
|
|
103
|
+
manifestPath: string,
|
|
104
|
+
featureOptions?: CargoFeatureOptions,
|
|
105
|
+
) {
|
|
52
106
|
if (!fs.existsSync(manifestPath)) {
|
|
53
107
|
throw new Error(`No crate found in manifest: ${manifestPath}`)
|
|
54
108
|
}
|
|
55
109
|
|
|
110
|
+
const featureArgs: string[] = []
|
|
111
|
+
if (featureOptions?.allFeatures) {
|
|
112
|
+
featureArgs.push('--all-features')
|
|
113
|
+
}
|
|
114
|
+
if (featureOptions?.noDefaultFeatures) {
|
|
115
|
+
featureArgs.push('--no-default-features')
|
|
116
|
+
}
|
|
117
|
+
if (featureOptions?.features?.length) {
|
|
118
|
+
featureArgs.push('--features', featureOptions.features.join(','))
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Resolving with the build's feature flags keeps `metadata.resolve` in sync with what
|
|
122
|
+
// `cargo build` will actually compile. Feature flags are rejected in some setups
|
|
123
|
+
// (e.g. a virtual workspace root manifest), so fall back to a plain invocation.
|
|
124
|
+
if (featureArgs.length) {
|
|
125
|
+
try {
|
|
126
|
+
return await execCargoMetadata(manifestPath, featureArgs)
|
|
127
|
+
} catch (e) {
|
|
128
|
+
debug.warn(
|
|
129
|
+
`cargo metadata failed with feature flags, retrying without them: ${e}`,
|
|
130
|
+
)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return execCargoMetadata(manifestPath, [])
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async function execCargoMetadata(manifestPath: string, extraArgs: string[]) {
|
|
56
138
|
const childProcess = spawn(
|
|
57
139
|
'cargo',
|
|
58
|
-
[
|
|
59
|
-
|
|
140
|
+
[
|
|
141
|
+
'metadata',
|
|
142
|
+
'--manifest-path',
|
|
143
|
+
manifestPath,
|
|
144
|
+
'--format-version',
|
|
145
|
+
'1',
|
|
146
|
+
...extraArgs,
|
|
147
|
+
],
|
|
148
|
+
{
|
|
149
|
+
stdio: 'pipe',
|
|
150
|
+
},
|
|
60
151
|
)
|
|
61
152
|
|
|
62
153
|
let stdout = ''
|