tegami 0.0.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.
- package/dist/generators/simple.d.mts +6 -0
- package/dist/generators/simple.mjs +17 -0
- package/dist/index.d.mts +31 -0
- package/dist/index.mjs +569 -0
- package/dist/npm-BSE_dtB3.mjs +280 -0
- package/dist/plugins/git.d.mts +19 -0
- package/dist/plugins/git.mjs +56 -0
- package/dist/plugins/github.d.mts +23 -0
- package/dist/plugins/github.mjs +41 -0
- package/dist/providers/cargo.d.mts +32 -0
- package/dist/providers/cargo.mjs +186 -0
- package/dist/providers/npm.d.mts +2 -0
- package/dist/providers/npm.mjs +2 -0
- package/dist/types-DdIMewK9.d.mts +283 -0
- package/dist/workspace-B5_i21S0.mjs +63 -0
- package/package.json +49 -0
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import { n as WorkspacePackage, r as isNodeError } from "./workspace-B5_i21S0.mjs";
|
|
2
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import { join, normalize } from "node:path";
|
|
4
|
+
import { x } from "tinyexec";
|
|
5
|
+
import * as semver from "semver";
|
|
6
|
+
import { glob } from "tinyglobby";
|
|
7
|
+
import { load } from "js-yaml";
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
import { detect } from "package-manager-detector";
|
|
10
|
+
//#region src/schemas.ts
|
|
11
|
+
const changelogFrontmatterSchema = z.object({
|
|
12
|
+
subject: z.string().optional(),
|
|
13
|
+
packages: z.array(z.string()).default([])
|
|
14
|
+
});
|
|
15
|
+
const stringRecordSchema = z.record(z.string(), z.string());
|
|
16
|
+
const jsonCodec = (schema) => z.codec(z.string(), schema, {
|
|
17
|
+
decode: (jsonString, ctx) => {
|
|
18
|
+
try {
|
|
19
|
+
return JSON.parse(jsonString);
|
|
20
|
+
} catch (err) {
|
|
21
|
+
ctx.issues.push({
|
|
22
|
+
code: "invalid_format",
|
|
23
|
+
format: "json",
|
|
24
|
+
input: jsonString,
|
|
25
|
+
message: err.message
|
|
26
|
+
});
|
|
27
|
+
return z.NEVER;
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
encode: (value) => JSON.stringify(value)
|
|
31
|
+
});
|
|
32
|
+
const workspacePatternsSchema = z.union([z.array(z.string()), z.looseObject({ packages: z.array(z.string()).optional() }).transform((workspaces) => workspaces.packages ?? ["."])]).pipe(z.array(z.string()));
|
|
33
|
+
const packageManifestSchema = z.looseObject({
|
|
34
|
+
name: z.string().optional(),
|
|
35
|
+
version: z.string().optional(),
|
|
36
|
+
private: z.boolean().optional(),
|
|
37
|
+
publishConfig: z.looseObject({
|
|
38
|
+
access: z.enum(["public", "restricted"]).optional(),
|
|
39
|
+
registry: z.string().optional(),
|
|
40
|
+
tag: z.string().optional()
|
|
41
|
+
}).optional(),
|
|
42
|
+
workspaces: workspacePatternsSchema.optional(),
|
|
43
|
+
dependencies: stringRecordSchema.optional(),
|
|
44
|
+
devDependencies: stringRecordSchema.optional(),
|
|
45
|
+
peerDependencies: stringRecordSchema.optional(),
|
|
46
|
+
optionalDependencies: stringRecordSchema.optional()
|
|
47
|
+
});
|
|
48
|
+
/** the persisted plan data for actual publishing */
|
|
49
|
+
const planStoreSchema = jsonCodec(z.object({
|
|
50
|
+
id: z.string(),
|
|
51
|
+
createdAt: z.iso.datetime(),
|
|
52
|
+
/** release note entries */
|
|
53
|
+
changelogs: z.record(z.string(), z.object({
|
|
54
|
+
filename: z.string(),
|
|
55
|
+
subject: z.string().optional(),
|
|
56
|
+
packages: z.array(z.string()),
|
|
57
|
+
type: z.enum([
|
|
58
|
+
"major",
|
|
59
|
+
"minor",
|
|
60
|
+
"patch"
|
|
61
|
+
]),
|
|
62
|
+
title: z.string(),
|
|
63
|
+
content: z.string()
|
|
64
|
+
})),
|
|
65
|
+
/** package id -> package info */
|
|
66
|
+
packages: z.record(z.string(), z.object({
|
|
67
|
+
type: z.enum([
|
|
68
|
+
"major",
|
|
69
|
+
"minor",
|
|
70
|
+
"patch"
|
|
71
|
+
]),
|
|
72
|
+
changelogIds: z.codec(z.array(z.string()), z.set(z.string()), {
|
|
73
|
+
encode: (v) => Array.from(v),
|
|
74
|
+
decode: (v) => new Set(v)
|
|
75
|
+
}),
|
|
76
|
+
distTag: z.string().optional(),
|
|
77
|
+
publish: z.boolean()
|
|
78
|
+
}))
|
|
79
|
+
}));
|
|
80
|
+
//#endregion
|
|
81
|
+
//#region src/providers/npm.ts
|
|
82
|
+
const DEP_FIELDS = [
|
|
83
|
+
"dependencies",
|
|
84
|
+
"devDependencies",
|
|
85
|
+
"peerDependencies",
|
|
86
|
+
"optionalDependencies"
|
|
87
|
+
];
|
|
88
|
+
var NpmPackage = class NpmPackage extends WorkspacePackage {
|
|
89
|
+
path;
|
|
90
|
+
manifest;
|
|
91
|
+
manager = "npm";
|
|
92
|
+
constructor(path, manifest) {
|
|
93
|
+
super();
|
|
94
|
+
this.path = path;
|
|
95
|
+
this.manifest = manifest;
|
|
96
|
+
}
|
|
97
|
+
get name() {
|
|
98
|
+
return this.manifest.name;
|
|
99
|
+
}
|
|
100
|
+
get version() {
|
|
101
|
+
return this.manifest.version ?? "0.0.0";
|
|
102
|
+
}
|
|
103
|
+
get publish() {
|
|
104
|
+
return this.manifest.private !== true;
|
|
105
|
+
}
|
|
106
|
+
get distTag() {
|
|
107
|
+
return this.manifest.publishConfig?.tag;
|
|
108
|
+
}
|
|
109
|
+
setVersion(version) {
|
|
110
|
+
this.manifest.version = version;
|
|
111
|
+
}
|
|
112
|
+
async updateDependency(target, version, context) {
|
|
113
|
+
if (!(target instanceof NpmPackage)) return;
|
|
114
|
+
const next = semver.parse(version);
|
|
115
|
+
if (!next) return;
|
|
116
|
+
for (const field of DEP_FIELDS) {
|
|
117
|
+
const dependencies = this.manifest[field];
|
|
118
|
+
if (!dependencies) continue;
|
|
119
|
+
for (const [rawName, rawRange] of Object.entries(dependencies)) {
|
|
120
|
+
const spec = parseNpmDependency(rawName, rawRange);
|
|
121
|
+
if (!spec || spec.name !== target.name) continue;
|
|
122
|
+
dependencies[rawName] = formatNpmDependency(await this.updateRange(context, spec, next));
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
async write() {
|
|
127
|
+
await writeFile(join(this.path, "package.json"), `${JSON.stringify(this.manifest, null, 2)}\n`);
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
var NpmRegistryClient = class {
|
|
131
|
+
cwd;
|
|
132
|
+
npmClient;
|
|
133
|
+
graph;
|
|
134
|
+
id = "npm";
|
|
135
|
+
#versionMap = /* @__PURE__ */ new Map();
|
|
136
|
+
#resolvedClient;
|
|
137
|
+
constructor(cwd, npmClient = void 0, graph) {
|
|
138
|
+
this.cwd = cwd;
|
|
139
|
+
this.npmClient = npmClient;
|
|
140
|
+
this.graph = graph;
|
|
141
|
+
}
|
|
142
|
+
supports(pkg) {
|
|
143
|
+
return pkg instanceof NpmPackage;
|
|
144
|
+
}
|
|
145
|
+
async packageVersionExists(pkg, version) {
|
|
146
|
+
const cacheKey = `${pkg.id}@${version}`;
|
|
147
|
+
let info = this.#versionMap.get(cacheKey);
|
|
148
|
+
if (!info) {
|
|
149
|
+
const run = async () => {
|
|
150
|
+
if (!(pkg instanceof NpmPackage)) return false;
|
|
151
|
+
const registry = pkg.manifest.publishConfig?.registry;
|
|
152
|
+
const args = [
|
|
153
|
+
"view",
|
|
154
|
+
`${pkg.name}@${version}`,
|
|
155
|
+
"version",
|
|
156
|
+
"--json"
|
|
157
|
+
];
|
|
158
|
+
if (registry) args.push("--registry", registry);
|
|
159
|
+
const result = await x(await this.resolveClient(), args, { nodeOptions: { cwd: this.cwd } });
|
|
160
|
+
if (result.exitCode === 0) return true;
|
|
161
|
+
const output = commandOutput(result);
|
|
162
|
+
if (isMissingRegistryEntry(output)) return false;
|
|
163
|
+
throw new Error(`Unable to validate ${pkg.name}@${version} against the npm registry${registry ? ` "${registry}"` : ""}: ${output.trim() || `command exited with code ${result.exitCode}`}`);
|
|
164
|
+
};
|
|
165
|
+
info = run();
|
|
166
|
+
this.#versionMap.set(cacheKey, info);
|
|
167
|
+
}
|
|
168
|
+
return info;
|
|
169
|
+
}
|
|
170
|
+
async publish(pkg, options = {}) {
|
|
171
|
+
const args = ["publish"];
|
|
172
|
+
const distTag = options.distTag ?? pkg.distTag;
|
|
173
|
+
if (distTag) args.push("--tag", distTag);
|
|
174
|
+
await x(await this.resolveClient(), args, {
|
|
175
|
+
nodeOptions: { cwd: pkg.path },
|
|
176
|
+
throwOnError: true
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
async publishPlanStatus(plan) {
|
|
180
|
+
for (const [name, pkgPlan] of Object.entries(plan.packages)) {
|
|
181
|
+
const pkg = this.graph.get(name);
|
|
182
|
+
if (!(pkg instanceof NpmPackage) || !pkgPlan.publish) continue;
|
|
183
|
+
if (!await this.packageVersionExists(pkg, pkg.version)) return { state: "pending" };
|
|
184
|
+
}
|
|
185
|
+
return { state: "success" };
|
|
186
|
+
}
|
|
187
|
+
resolveClient() {
|
|
188
|
+
if (!this.#resolvedClient) this.#resolvedClient = this.npmClient ?? detect({ cwd: this.cwd }).then((result) => {
|
|
189
|
+
if (result?.name === "pnpm") return "pnpm";
|
|
190
|
+
return "npm";
|
|
191
|
+
});
|
|
192
|
+
return this.#resolvedClient;
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
function commandOutput(result) {
|
|
196
|
+
return [result.stdout, result.stderr].filter(Boolean).join("\n");
|
|
197
|
+
}
|
|
198
|
+
function isMissingRegistryEntry(output) {
|
|
199
|
+
const normalized = output.toLowerCase();
|
|
200
|
+
return normalized.includes("e404") || normalized.includes("404") || normalized.includes("no match") || normalized.includes("no matching version") || normalized.includes("not found");
|
|
201
|
+
}
|
|
202
|
+
function parseNpmDependency(rawName, rawRange) {
|
|
203
|
+
if (rawRange.startsWith("workspace:")) return {
|
|
204
|
+
name: rawName,
|
|
205
|
+
range: rawRange.slice(10),
|
|
206
|
+
protocol: "workspace"
|
|
207
|
+
};
|
|
208
|
+
if (rawRange.startsWith("npm:")) {
|
|
209
|
+
const spec = rawRange.slice(4);
|
|
210
|
+
const separator = spec.lastIndexOf("@");
|
|
211
|
+
if (separator <= 0) return void 0;
|
|
212
|
+
return {
|
|
213
|
+
name: spec.slice(0, separator),
|
|
214
|
+
range: spec.slice(separator + 1),
|
|
215
|
+
protocol: "npm"
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
return {
|
|
219
|
+
name: rawName,
|
|
220
|
+
range: rawRange
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
function formatNpmDependency(spec) {
|
|
224
|
+
const npmSpec = spec;
|
|
225
|
+
if (npmSpec.protocol === "workspace") return `workspace:${spec.range}`;
|
|
226
|
+
if (npmSpec.protocol === "npm") return `npm:${spec.name}@${spec.range}`;
|
|
227
|
+
return spec.range;
|
|
228
|
+
}
|
|
229
|
+
function npm(client) {
|
|
230
|
+
return {
|
|
231
|
+
name: "npm",
|
|
232
|
+
enforce: "pre",
|
|
233
|
+
async resolve() {
|
|
234
|
+
await discoverNpmPackages(this.cwd, (pkg) => this.graph.add(pkg));
|
|
235
|
+
},
|
|
236
|
+
createRegistryClient() {
|
|
237
|
+
return new NpmRegistryClient(this.cwd, client, this.graph);
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
async function discoverNpmPackages(cwd, add) {
|
|
242
|
+
const candidatePaths = await expandWorkspacePatterns(cwd, await readWorkspacePatterns(cwd));
|
|
243
|
+
const rootManifestPromise = readManifest(cwd).catch(() => void 0);
|
|
244
|
+
const manifests = await Promise.all(candidatePaths.map((path) => readManifest(path).then((manifest) => ({
|
|
245
|
+
path,
|
|
246
|
+
manifest
|
|
247
|
+
})).catch(() => void 0)));
|
|
248
|
+
const rootManifest = await rootManifestPromise;
|
|
249
|
+
if (rootManifest?.name && rootManifest.version && rootManifest.private !== true) add(new NpmPackage(cwd, rootManifest));
|
|
250
|
+
for (const entry of manifests) {
|
|
251
|
+
if (!entry?.manifest.name || !entry.manifest.version) continue;
|
|
252
|
+
add(new NpmPackage(entry.path, entry.manifest));
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
async function readWorkspacePatterns(cwd) {
|
|
256
|
+
const pnpmPatterns = await readFile(join(cwd, "pnpm-workspace.yaml"), "utf8").then((content) => workspacePatternsSchema.parse(load(content) ?? {})).catch((error) => {
|
|
257
|
+
if (isNodeError(error) && error.code === "ENOENT") return void 0;
|
|
258
|
+
throw error;
|
|
259
|
+
});
|
|
260
|
+
if (pnpmPatterns) return pnpmPatterns;
|
|
261
|
+
return (await readManifest(cwd).catch(() => void 0))?.workspaces ?? ["."];
|
|
262
|
+
}
|
|
263
|
+
async function expandWorkspacePatterns(cwd, patterns) {
|
|
264
|
+
const paths = patterns.includes(".") ? [cwd] : [];
|
|
265
|
+
const globPatterns = patterns.filter((pattern) => pattern !== ".");
|
|
266
|
+
if (globPatterns.length > 0) paths.push(...await glob(globPatterns, {
|
|
267
|
+
absolute: true,
|
|
268
|
+
cwd,
|
|
269
|
+
ignore: ["**/node_modules/**"],
|
|
270
|
+
onlyDirectories: true,
|
|
271
|
+
onlyFiles: false
|
|
272
|
+
}));
|
|
273
|
+
return paths.map(normalize);
|
|
274
|
+
}
|
|
275
|
+
async function readManifest(packagePath) {
|
|
276
|
+
const content = await readFile(join(packagePath, "package.json"), "utf8");
|
|
277
|
+
return packageManifestSchema.parse(JSON.parse(content));
|
|
278
|
+
}
|
|
279
|
+
//#endregion
|
|
280
|
+
export { planStoreSchema as a, changelogFrontmatterSchema as i, NpmRegistryClient as n, npm as r, NpmPackage as t };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { o as TegamiPlugin } from "../types-DdIMewK9.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/plugins/git.d.ts
|
|
4
|
+
interface GitPluginOptions {
|
|
5
|
+
/** Set to false to skip creating git tags after all packages publish successfully. */
|
|
6
|
+
createTags?: boolean;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Basic Git integrations:
|
|
10
|
+
* - auto tags.
|
|
11
|
+
*
|
|
12
|
+
* Note: you do not need this with `github` plugin enabled.
|
|
13
|
+
*/
|
|
14
|
+
declare function git(options?: GitPluginOptions): TegamiPlugin;
|
|
15
|
+
/** create a Git tag, ignored if already exists */
|
|
16
|
+
declare function createGitTag(cwd: string, tag: string): Promise<void>;
|
|
17
|
+
declare function gitTagExists(cwd: string, tag: string): Promise<boolean>;
|
|
18
|
+
//#endregion
|
|
19
|
+
export { GitPluginOptions, createGitTag, git, gitTagExists };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { x } from "tinyexec";
|
|
2
|
+
//#region src/plugins/git.ts
|
|
3
|
+
/**
|
|
4
|
+
* Basic Git integrations:
|
|
5
|
+
* - auto tags.
|
|
6
|
+
*
|
|
7
|
+
* Note: you do not need this with `github` plugin enabled.
|
|
8
|
+
*/
|
|
9
|
+
function git(options = {}) {
|
|
10
|
+
const { createTags = true } = options;
|
|
11
|
+
return {
|
|
12
|
+
name: "git",
|
|
13
|
+
enforce: "pre",
|
|
14
|
+
async afterPublish(result) {
|
|
15
|
+
const { graph, publishOptions: { dryRun = false } } = this;
|
|
16
|
+
if (dryRun || !createTags || result.state === "failed") return result;
|
|
17
|
+
for (const pkg of result.packages) try {
|
|
18
|
+
const gitTag = `${pkg.name}@${pkg.version}`;
|
|
19
|
+
await createGitTag(graph.get(pkg.id).path, gitTag);
|
|
20
|
+
pkg.gitTag = gitTag;
|
|
21
|
+
} catch (error) {
|
|
22
|
+
return {
|
|
23
|
+
...result,
|
|
24
|
+
state: "failed",
|
|
25
|
+
packages: result.packages.map((pkgResult) => {
|
|
26
|
+
if (pkgResult.id === pkg.id) return {
|
|
27
|
+
...pkgResult,
|
|
28
|
+
state: "failed",
|
|
29
|
+
error: error instanceof Error ? error.message : String(error)
|
|
30
|
+
};
|
|
31
|
+
return pkgResult;
|
|
32
|
+
})
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/** create a Git tag, ignored if already exists */
|
|
40
|
+
async function createGitTag(cwd, tag) {
|
|
41
|
+
if (await gitTagExists(cwd, tag)) return;
|
|
42
|
+
await x("git", ["tag", tag], {
|
|
43
|
+
nodeOptions: { cwd },
|
|
44
|
+
throwOnError: true
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
async function gitTagExists(cwd, tag) {
|
|
48
|
+
return (await x("git", [
|
|
49
|
+
"rev-parse",
|
|
50
|
+
"-q",
|
|
51
|
+
"--verify",
|
|
52
|
+
`refs/tags/${tag}`
|
|
53
|
+
], { nodeOptions: { cwd } })).exitCode === 0;
|
|
54
|
+
}
|
|
55
|
+
//#endregion
|
|
56
|
+
export { createGitTag, git, gitTagExists };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { f as PackagePublishResult, o as TegamiPlugin, t as Awaitable } from "../types-DdIMewK9.mjs";
|
|
2
|
+
import { GitPluginOptions } from "./git.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/plugins/github.d.ts
|
|
5
|
+
interface GithubRelease {
|
|
6
|
+
/** Release title */
|
|
7
|
+
title?: string;
|
|
8
|
+
/** Release notes */
|
|
9
|
+
notes?: string;
|
|
10
|
+
/** Whether to mark release as prerelease */
|
|
11
|
+
prerelease?: boolean;
|
|
12
|
+
}
|
|
13
|
+
/** Options for creating GitHub releases after a successful publish. */
|
|
14
|
+
interface GitHubPluginOptions extends GitPluginOptions {
|
|
15
|
+
/** GitHub repository. */
|
|
16
|
+
repo?: string;
|
|
17
|
+
/** override release details, return `false` to skip */
|
|
18
|
+
onCreateRelease?: (result: PackagePublishResult) => Awaitable<GithubRelease | false>;
|
|
19
|
+
}
|
|
20
|
+
/** Create GitHub releases for successfully published packages after the whole plan succeeds. */
|
|
21
|
+
declare function github(options?: GitHubPluginOptions): TegamiPlugin[];
|
|
22
|
+
//#endregion
|
|
23
|
+
export { GitHubPluginOptions, github };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { git } from "./git.mjs";
|
|
2
|
+
import { x } from "tinyexec";
|
|
3
|
+
//#region src/plugins/github.ts
|
|
4
|
+
/** Create GitHub releases for successfully published packages after the whole plan succeeds. */
|
|
5
|
+
function github(options = {}) {
|
|
6
|
+
async function createGithubRelease(pkg) {
|
|
7
|
+
if (!pkg.gitTag) return;
|
|
8
|
+
const release = await options.onCreateRelease?.(pkg) ?? {};
|
|
9
|
+
if (release === false) return;
|
|
10
|
+
const args = [
|
|
11
|
+
"release",
|
|
12
|
+
"create",
|
|
13
|
+
pkg.gitTag,
|
|
14
|
+
"--title",
|
|
15
|
+
release.title ?? `${pkg.name}@${pkg.version}`,
|
|
16
|
+
"--notes",
|
|
17
|
+
release.notes ?? defaultNotes(pkg)
|
|
18
|
+
];
|
|
19
|
+
if (options.repo) args.push("--repo", options.repo);
|
|
20
|
+
if (release.prerelease) args.push("--prerelease");
|
|
21
|
+
await x("gh", args, { throwOnError: true });
|
|
22
|
+
}
|
|
23
|
+
return [git(options), {
|
|
24
|
+
name: "github",
|
|
25
|
+
async afterPublish(result) {
|
|
26
|
+
if (result.state !== "success") return;
|
|
27
|
+
await Promise.all(result.packages.map(createGithubRelease));
|
|
28
|
+
}
|
|
29
|
+
}];
|
|
30
|
+
}
|
|
31
|
+
function defaultNotes(pkg) {
|
|
32
|
+
const entries = pkg.changelogs;
|
|
33
|
+
if (entries.length > 0) return entries.map((entry) => [`### ${entry.title}`, entry.content].filter(Boolean).join("\n\n")).join("\n\n");
|
|
34
|
+
return [
|
|
35
|
+
`Published ${pkg.name}@${pkg.version}.`,
|
|
36
|
+
"",
|
|
37
|
+
`npm dist-tag: ${pkg.distTag}`
|
|
38
|
+
].join("\n");
|
|
39
|
+
}
|
|
40
|
+
//#endregion
|
|
41
|
+
export { github };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { b as PackageGraph, h as PlanStore, i as RegistryClient, o as TegamiPlugin, r as PublishPlanStatus, x as WorkspacePackage, y as TegamiContext } from "../types-DdIMewK9.mjs";
|
|
2
|
+
import { TomlTable } from "smol-toml";
|
|
3
|
+
|
|
4
|
+
//#region src/providers/cargo.d.ts
|
|
5
|
+
declare class CargoPackage extends WorkspacePackage {
|
|
6
|
+
readonly path: string;
|
|
7
|
+
readonly manifest: TomlTable;
|
|
8
|
+
private readonly workspaceManifest?;
|
|
9
|
+
readonly manager = "cargo";
|
|
10
|
+
constructor(path: string, manifest: TomlTable, workspaceManifest?: TomlTable | undefined);
|
|
11
|
+
get name(): string;
|
|
12
|
+
get version(): string;
|
|
13
|
+
get publish(): boolean;
|
|
14
|
+
setVersion(version: string): void;
|
|
15
|
+
updateDependency(target: WorkspacePackage, version: string, context: TegamiContext): Promise<void>;
|
|
16
|
+
write(): Promise<void>;
|
|
17
|
+
private get packageInfo();
|
|
18
|
+
private get workspaceVersion();
|
|
19
|
+
}
|
|
20
|
+
declare class CargoRegistryClient implements RegistryClient {
|
|
21
|
+
#private;
|
|
22
|
+
private readonly graph;
|
|
23
|
+
readonly id = "cargo";
|
|
24
|
+
constructor(graph: PackageGraph);
|
|
25
|
+
supports(pkg: WorkspacePackage): boolean;
|
|
26
|
+
packageVersionExists(pkg: WorkspacePackage, version: string): Promise<boolean>;
|
|
27
|
+
publish(pkg: WorkspacePackage): Promise<void>;
|
|
28
|
+
publishPlanStatus(plan: PlanStore): Promise<PublishPlanStatus>;
|
|
29
|
+
}
|
|
30
|
+
declare function cargo(): TegamiPlugin;
|
|
31
|
+
//#endregion
|
|
32
|
+
export { CargoPackage, CargoRegistryClient, cargo };
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { n as WorkspacePackage, r as isNodeError } from "../workspace-B5_i21S0.mjs";
|
|
2
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import { join, normalize } from "node:path";
|
|
4
|
+
import { x } from "tinyexec";
|
|
5
|
+
import * as semver from "semver";
|
|
6
|
+
import { parse, stringify } from "smol-toml";
|
|
7
|
+
import { glob } from "tinyglobby";
|
|
8
|
+
//#region src/providers/cargo.ts
|
|
9
|
+
const DEP_FIELDS = [
|
|
10
|
+
"dependencies",
|
|
11
|
+
"dev-dependencies",
|
|
12
|
+
"build-dependencies"
|
|
13
|
+
];
|
|
14
|
+
var CargoPackage = class CargoPackage extends WorkspacePackage {
|
|
15
|
+
path;
|
|
16
|
+
manifest;
|
|
17
|
+
workspaceManifest;
|
|
18
|
+
manager = "cargo";
|
|
19
|
+
constructor(path, manifest, workspaceManifest) {
|
|
20
|
+
super();
|
|
21
|
+
this.path = path;
|
|
22
|
+
this.manifest = manifest;
|
|
23
|
+
this.workspaceManifest = workspaceManifest;
|
|
24
|
+
}
|
|
25
|
+
get name() {
|
|
26
|
+
return stringValue(this.packageInfo.name);
|
|
27
|
+
}
|
|
28
|
+
get version() {
|
|
29
|
+
return stringValue(this.packageInfo.version) ?? this.workspaceVersion ?? "0.0.0";
|
|
30
|
+
}
|
|
31
|
+
get publish() {
|
|
32
|
+
return this.packageInfo.publish !== false;
|
|
33
|
+
}
|
|
34
|
+
setVersion(version) {
|
|
35
|
+
this.packageInfo.version = version;
|
|
36
|
+
}
|
|
37
|
+
async updateDependency(target, version, context) {
|
|
38
|
+
if (!(target instanceof CargoPackage)) return;
|
|
39
|
+
const next = semver.parse(version);
|
|
40
|
+
if (!next) return;
|
|
41
|
+
for (const table of dependencyTables(this.manifest)) for (const [rawName, rawSpec] of Object.entries(table)) {
|
|
42
|
+
const spec = tableValue(rawSpec);
|
|
43
|
+
const packageName = stringValue(spec?.package) ?? rawName;
|
|
44
|
+
if (packageName !== target.name) continue;
|
|
45
|
+
if (typeof rawSpec === "string") {
|
|
46
|
+
table[rawName] = (await this.updateRange(context, {
|
|
47
|
+
name: packageName,
|
|
48
|
+
range: rawSpec
|
|
49
|
+
}, next)).range;
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (spec) {
|
|
53
|
+
const current = stringValue(spec.version);
|
|
54
|
+
spec.version = current ? (await this.updateRange(context, {
|
|
55
|
+
name: packageName,
|
|
56
|
+
range: current
|
|
57
|
+
}, next)).range : next.format();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
async write() {
|
|
62
|
+
await writeFile(join(this.path, "Cargo.toml"), stringify(this.manifest));
|
|
63
|
+
}
|
|
64
|
+
get packageInfo() {
|
|
65
|
+
return tableValue(this.manifest.package) ?? {};
|
|
66
|
+
}
|
|
67
|
+
get workspaceVersion() {
|
|
68
|
+
return stringValue(tableValue(tableValue(this.workspaceManifest?.workspace)?.package)?.version);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
var CargoRegistryClient = class {
|
|
72
|
+
graph;
|
|
73
|
+
id = "cargo";
|
|
74
|
+
#versionMap = /* @__PURE__ */ new Map();
|
|
75
|
+
constructor(graph) {
|
|
76
|
+
this.graph = graph;
|
|
77
|
+
}
|
|
78
|
+
supports(pkg) {
|
|
79
|
+
return pkg instanceof CargoPackage;
|
|
80
|
+
}
|
|
81
|
+
async packageVersionExists(pkg, version) {
|
|
82
|
+
const cacheKey = `${pkg.id}@${version}`;
|
|
83
|
+
let info = this.#versionMap.get(cacheKey);
|
|
84
|
+
if (!info) {
|
|
85
|
+
info = fetch(`https://crates.io/api/v1/crates/${encodeURIComponent(pkg.name)}/${version}`).then(async (response) => {
|
|
86
|
+
if (response.status === 200) return true;
|
|
87
|
+
if (response.status === 404) return false;
|
|
88
|
+
throw new Error(`Unable to validate ${pkg.name}@${version} against crates.io: ${await response.text()}`);
|
|
89
|
+
});
|
|
90
|
+
this.#versionMap.set(cacheKey, info);
|
|
91
|
+
}
|
|
92
|
+
return info;
|
|
93
|
+
}
|
|
94
|
+
async publish(pkg) {
|
|
95
|
+
await x("cargo", ["publish"], {
|
|
96
|
+
nodeOptions: { cwd: pkg.path },
|
|
97
|
+
throwOnError: true
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
async publishPlanStatus(plan) {
|
|
101
|
+
for (const [id, pkgPlan] of Object.entries(plan.packages)) {
|
|
102
|
+
const pkg = this.graph.get(id);
|
|
103
|
+
if (!(pkg instanceof CargoPackage) || !pkgPlan.publish) continue;
|
|
104
|
+
if (!await this.packageVersionExists(pkg, pkg.version)) return { state: "pending" };
|
|
105
|
+
}
|
|
106
|
+
return { state: "success" };
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
function cargo() {
|
|
110
|
+
return {
|
|
111
|
+
name: "cargo",
|
|
112
|
+
enforce: "pre",
|
|
113
|
+
async resolve() {
|
|
114
|
+
await discoverCargoPackages(this.cwd, (pkg) => this.graph.add(pkg));
|
|
115
|
+
},
|
|
116
|
+
createRegistryClient() {
|
|
117
|
+
return new CargoRegistryClient(this.graph);
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
async function discoverCargoPackages(cwd, add) {
|
|
122
|
+
const root = await readCargoManifest(cwd).catch((error) => {
|
|
123
|
+
if (isNodeError(error) && error.code === "ENOENT") return void 0;
|
|
124
|
+
throw error;
|
|
125
|
+
});
|
|
126
|
+
if (!root) return;
|
|
127
|
+
addCargoPackage(cwd, root, root, add);
|
|
128
|
+
const workspace = tableValue(root.workspace);
|
|
129
|
+
const members = workspace?.members;
|
|
130
|
+
if (!workspace || !Array.isArray(members)) return;
|
|
131
|
+
const exclude = Array.isArray(workspace.exclude) ? workspace.exclude.filter((member) => typeof member === "string") : [];
|
|
132
|
+
const paths = await expandWorkspaceMembers(cwd, members.filter((member) => typeof member === "string"), exclude);
|
|
133
|
+
const manifests = await Promise.all(paths.map((path) => readCargoManifest(path).then((manifest) => ({
|
|
134
|
+
path,
|
|
135
|
+
manifest
|
|
136
|
+
})).catch(() => void 0)));
|
|
137
|
+
for (const entry of manifests) if (entry) addCargoPackage(entry.path, entry.manifest, root, add);
|
|
138
|
+
}
|
|
139
|
+
function addCargoPackage(path, manifest, workspaceManifest, add) {
|
|
140
|
+
const packageInfo = tableValue(manifest.package);
|
|
141
|
+
const workspacePackage = tableValue(workspaceManifest.workspace)?.package;
|
|
142
|
+
if (!packageInfo?.name) return;
|
|
143
|
+
if (!packageInfo.version && !tableValue(workspacePackage)?.version) return;
|
|
144
|
+
add(new CargoPackage(path, manifest, workspaceManifest));
|
|
145
|
+
}
|
|
146
|
+
async function expandWorkspaceMembers(cwd, members, exclude) {
|
|
147
|
+
const paths = members.includes(".") ? [cwd] : [];
|
|
148
|
+
const patterns = members.filter((member) => member !== ".");
|
|
149
|
+
if (patterns.length > 0) paths.push(...await glob(patterns, {
|
|
150
|
+
absolute: true,
|
|
151
|
+
cwd,
|
|
152
|
+
ignore: ["**/target/**", ...exclude],
|
|
153
|
+
onlyDirectories: true,
|
|
154
|
+
onlyFiles: false
|
|
155
|
+
}));
|
|
156
|
+
return paths.map(normalize);
|
|
157
|
+
}
|
|
158
|
+
function dependencyTables(manifest) {
|
|
159
|
+
const tables = [];
|
|
160
|
+
for (const field of DEP_FIELDS) {
|
|
161
|
+
const table = tableValue(manifest[field]);
|
|
162
|
+
if (table) tables.push(table);
|
|
163
|
+
}
|
|
164
|
+
const target = tableValue(manifest.target);
|
|
165
|
+
if (target) for (const targetConfig of Object.values(target)) {
|
|
166
|
+
const targetTable = tableValue(targetConfig);
|
|
167
|
+
if (!targetTable) continue;
|
|
168
|
+
for (const field of DEP_FIELDS) {
|
|
169
|
+
const table = tableValue(targetTable[field]);
|
|
170
|
+
if (table) tables.push(table);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return tables;
|
|
174
|
+
}
|
|
175
|
+
async function readCargoManifest(path) {
|
|
176
|
+
return parse(await readFile(join(path, "Cargo.toml"), "utf8"));
|
|
177
|
+
}
|
|
178
|
+
function tableValue(value) {
|
|
179
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
180
|
+
return value;
|
|
181
|
+
}
|
|
182
|
+
function stringValue(value) {
|
|
183
|
+
return typeof value === "string" ? value : void 0;
|
|
184
|
+
}
|
|
185
|
+
//#endregion
|
|
186
|
+
export { CargoPackage, CargoRegistryClient, cargo };
|