@joshmossas/nx-cargo 0.6.2 → 0.6.3
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/package.json +1 -1
- package/src/graph/index.ts +79 -98
package/package.json
CHANGED
package/src/graph/index.ts
CHANGED
|
@@ -4,11 +4,13 @@ import {
|
|
|
4
4
|
RawProjectGraphDependency as GraphDependency,
|
|
5
5
|
DependencyType,
|
|
6
6
|
} from "@nx/devkit";
|
|
7
|
-
import
|
|
8
|
-
import * as
|
|
9
|
-
import * as
|
|
10
|
-
import * as
|
|
7
|
+
import * as cp from "child_process";
|
|
8
|
+
import * as os from "os";
|
|
9
|
+
import * as path from "path";
|
|
10
|
+
import * as fs from "fs";
|
|
11
11
|
|
|
12
|
+
/** * Type Definitions for Cargo Metadata
|
|
13
|
+
*/
|
|
12
14
|
type VersionNumber = `${number}.${number}.${number}`;
|
|
13
15
|
type PackageVersion = `${string}@${VersionNumber}` | VersionNumber;
|
|
14
16
|
type CargoId = `${"registry" | "path"}+${
|
|
@@ -20,109 +22,91 @@ interface CargoPackage {
|
|
|
20
22
|
name: string;
|
|
21
23
|
version: string;
|
|
22
24
|
id: CargoId;
|
|
23
|
-
|
|
24
|
-
license_file: string | null;
|
|
25
|
-
description: string;
|
|
26
|
-
source: string | null;
|
|
27
|
-
dependencies: CargoDependency[];
|
|
28
|
-
targets: unknown; // TODO
|
|
29
|
-
features: Record<string, string[]>;
|
|
25
|
+
dependencies: unknown[];
|
|
30
26
|
manifest_path: string;
|
|
31
|
-
metadata: unknown | null; // TODO
|
|
32
|
-
publish: unknown | null; // TODO
|
|
33
|
-
authors: string[];
|
|
34
|
-
categories: string[];
|
|
35
|
-
keywords: string[];
|
|
36
|
-
readme: string | null;
|
|
37
|
-
repository: string | null;
|
|
38
|
-
homepage: string | null;
|
|
39
|
-
documentation: string | null;
|
|
40
|
-
edition: string;
|
|
41
|
-
links: unknown | null; // TODO
|
|
42
|
-
default_run: unknown | null; // TODO
|
|
43
|
-
rust_version: string;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
interface CargoDependency {
|
|
47
|
-
name: string;
|
|
48
|
-
source: string | null;
|
|
49
|
-
req: string;
|
|
50
|
-
kind: "build" | "dev" | null;
|
|
51
|
-
rename: string | null;
|
|
52
|
-
optional: boolean;
|
|
53
|
-
uses_default_features: boolean;
|
|
54
|
-
features: string[];
|
|
55
|
-
target: string | null;
|
|
56
|
-
registry: string | null;
|
|
57
|
-
path?: string;
|
|
58
27
|
}
|
|
59
28
|
|
|
60
29
|
interface CargoMetadata {
|
|
61
30
|
packages: CargoPackage[];
|
|
62
31
|
workspace_members: CargoId[];
|
|
63
|
-
workspace_default_members: CargoId[];
|
|
64
32
|
resolve: {
|
|
65
33
|
nodes: ResolveNode[];
|
|
66
|
-
root: unknown;
|
|
67
34
|
};
|
|
68
|
-
target_directory: string;
|
|
69
|
-
version: number;
|
|
70
35
|
workspace_root: string;
|
|
71
|
-
metadata: unknown | null;
|
|
72
36
|
}
|
|
73
37
|
|
|
74
38
|
interface ResolveNode {
|
|
75
39
|
id: CargoId;
|
|
76
40
|
dependencies: CargoId[];
|
|
77
41
|
}
|
|
78
|
-
export function createDependencies(_: unknown, ctx: Context): GraphDependency[] {
|
|
79
|
-
const allDependencies: GraphDependency[] = [];
|
|
80
|
-
const processedWorkspaceRoots = new Set<string>();
|
|
81
42
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
.filter(p => fs.existsSync(p));
|
|
86
|
-
|
|
87
|
-
for (const configPath of cargoConfigPaths) {
|
|
88
|
-
const configDir = path.dirname(configPath);
|
|
89
|
-
|
|
90
|
-
// 2. Get metadata for this specific workspace
|
|
91
|
-
const metadata = getCargoMetadata(configDir);
|
|
43
|
+
type WithReq<T, K extends keyof T> = Omit<T, K> & {
|
|
44
|
+
[Key in K]-?: Exclude<T[Key], null | undefined>;
|
|
45
|
+
};
|
|
92
46
|
|
|
93
|
-
|
|
94
|
-
|
|
47
|
+
/**
|
|
48
|
+
* Main Nx Dependency Creator
|
|
49
|
+
*/
|
|
50
|
+
export function createDependencies(_: unknown, ctx: Context): GraphDependency[] {
|
|
51
|
+
const allDependencies: GraphDependency[] = [];
|
|
52
|
+
const seenManifestPaths = new Set<string>();
|
|
53
|
+
|
|
54
|
+
// 1. Identify and sort manifests by depth (shallowest first)
|
|
55
|
+
// This ensures we hit workspace roots before hitting their members.
|
|
56
|
+
const sortedManifests = Object.values(ctx.projects)
|
|
57
|
+
.map(project => {
|
|
58
|
+
const filepath = path.resolve(ctx.workspaceRoot, project.root, "Cargo.toml");
|
|
59
|
+
const depth = filepath.split(path.sep).length;
|
|
60
|
+
return { filepath, depth };
|
|
61
|
+
})
|
|
62
|
+
.filter(manifest => fs.existsSync(manifest.filepath))
|
|
63
|
+
.sort((a, b) => a.depth - b.depth);
|
|
64
|
+
|
|
65
|
+
for (const { filepath } of sortedManifests) {
|
|
66
|
+
// 2. Skip if this manifest was already included in a previously processed workspace
|
|
67
|
+
if (seenManifestPaths.has(filepath)) {
|
|
95
68
|
continue;
|
|
96
69
|
}
|
|
97
|
-
processedWorkspaceRoots.add(metadata.workspace_root);
|
|
98
70
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
71
|
+
try {
|
|
72
|
+
const metadata = getCargoMetadata(path.dirname(filepath));
|
|
73
|
+
|
|
74
|
+
// 3. Mark every package in this metadata as "seen" to avoid redundant calls
|
|
75
|
+
for (const pkg of metadata.packages) {
|
|
76
|
+
seenManifestPaths.add(path.resolve(pkg.manifest_path));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// 4. Extract dependencies from this specific workspace/crate
|
|
80
|
+
const workspaceDeps = processWorkspaceMetadata(ctx, metadata);
|
|
81
|
+
allDependencies.push(...workspaceDeps);
|
|
82
|
+
} catch (e) {
|
|
83
|
+
console.warn(
|
|
84
|
+
`[nx-rust] Skipping ${filepath} due to error:`,
|
|
85
|
+
e instanceof Error ? e.message : e
|
|
86
|
+
);
|
|
87
|
+
}
|
|
102
88
|
}
|
|
103
89
|
|
|
104
90
|
return allDependencies;
|
|
105
91
|
}
|
|
106
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Orchestrates the mapping between Cargo's internal resolve graph and Nx projects
|
|
95
|
+
*/
|
|
107
96
|
function processWorkspaceMetadata(
|
|
108
97
|
ctx: Context,
|
|
109
98
|
metadata: CargoMetadata
|
|
110
99
|
): GraphDependency[] {
|
|
111
|
-
const {
|
|
112
|
-
packages,
|
|
113
|
-
workspace_members: cargoWsMembers,
|
|
114
|
-
resolve: cargoResolve,
|
|
115
|
-
} = metadata;
|
|
100
|
+
const { packages, resolve } = metadata;
|
|
116
101
|
|
|
117
102
|
const workspacePackages = new Map<CargoId, CargoPackage>();
|
|
118
|
-
for (const
|
|
119
|
-
|
|
120
|
-
if (pkg) workspacePackages.set(id, pkg);
|
|
103
|
+
for (const pkg of packages) {
|
|
104
|
+
workspacePackages.set(pkg.id, pkg);
|
|
121
105
|
}
|
|
122
106
|
|
|
123
107
|
const nxData = mapCargoProjects(ctx, workspacePackages);
|
|
124
108
|
|
|
125
|
-
return
|
|
109
|
+
return (resolve?.nodes ?? [])
|
|
126
110
|
.filter(({ id }) => nxData.has(id))
|
|
127
111
|
.flatMap(({ id: sourceId, dependencies }) => {
|
|
128
112
|
const sourceProject = nxData.get(sourceId)!;
|
|
@@ -142,42 +126,24 @@ function processWorkspaceMetadata(
|
|
|
142
126
|
});
|
|
143
127
|
}
|
|
144
128
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
const metadata = cp.execSync("cargo metadata --format-version=1", {
|
|
149
|
-
encoding: "utf8",
|
|
150
|
-
maxBuffer: availableMemory,
|
|
151
|
-
cwd: cwd, // Crucial: run in the workspace directory
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
return JSON.parse(metadata);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
type WithReq<T, K extends keyof T> = Omit<T, K> & {
|
|
158
|
-
[Key in K]-?: Exclude<T[Key], null | undefined>;
|
|
159
|
-
};
|
|
160
|
-
|
|
129
|
+
/**
|
|
130
|
+
* Maps Cargo Packages to Nx Project Configurations based on their root directories
|
|
131
|
+
*/
|
|
161
132
|
function mapCargoProjects(ctx: Context, packages: Map<CargoId, CargoPackage>) {
|
|
162
|
-
|
|
133
|
+
const result = new Map<CargoId, WithReq<ProjectConfiguration, "name">>();
|
|
163
134
|
|
|
164
|
-
for (
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
let manifestDir = path.dirname(cargoPackage.manifest_path);
|
|
170
|
-
let projectDir = path
|
|
135
|
+
for (const [cargoId, cargoPackage] of packages) {
|
|
136
|
+
const manifestDir = path.dirname(cargoPackage.manifest_path);
|
|
137
|
+
const projectDir = path
|
|
171
138
|
.relative(ctx.workspaceRoot, manifestDir)
|
|
172
139
|
.replace(/\\/g, "/");
|
|
173
140
|
|
|
174
|
-
|
|
141
|
+
const found = Object.entries(ctx.projects).find(
|
|
175
142
|
([, config]) => config.root === projectDir
|
|
176
143
|
);
|
|
177
144
|
|
|
178
145
|
if (found) {
|
|
179
|
-
|
|
180
|
-
|
|
146
|
+
const [projectName, projectConfig] = found;
|
|
181
147
|
result.set(cargoId, {
|
|
182
148
|
...projectConfig,
|
|
183
149
|
name: projectName,
|
|
@@ -187,3 +153,18 @@ function mapCargoProjects(ctx: Context, packages: Map<CargoId, CargoPackage>) {
|
|
|
187
153
|
|
|
188
154
|
return result;
|
|
189
155
|
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Executes 'cargo metadata'.
|
|
159
|
+
* Uses --no-deps because we only care about internal workspace dependencies.
|
|
160
|
+
*/
|
|
161
|
+
function getCargoMetadata(cwd: string): CargoMetadata {
|
|
162
|
+
const availableMemory = os.freemem();
|
|
163
|
+
const metadata = cp.execSync("cargo metadata --format-version=1 --no-deps", {
|
|
164
|
+
encoding: "utf8",
|
|
165
|
+
maxBuffer: availableMemory,
|
|
166
|
+
cwd: cwd,
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
return JSON.parse(metadata);
|
|
170
|
+
}
|