tegami 1.1.0 → 1.1.2
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/_accessExpressionAsString-QhbUZzwv.js +44 -0
- package/dist/_assertGuard-BBn2NbSz.js +91 -0
- package/dist/_createStandardSchema-BGQyz-uA.js +89 -0
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.js +2 -2
- package/dist/{draft-BLbt4bSG.js → draft-CzUiQasJ.js} +168 -37
- package/dist/{generate-Rvz4Lu98.js → generate-Bg86OJP4.js} +1 -1
- package/dist/generators/simple.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3 -3
- package/dist/npm-DoPhFKji.js +1252 -0
- package/dist/plugins/cargo.d.ts +1 -1
- package/dist/plugins/cargo.js +491 -66
- package/dist/plugins/git.d.ts +1 -1
- package/dist/plugins/github.d.ts +1 -1
- package/dist/plugins/github.js +39 -12
- package/dist/plugins/gitlab.d.ts +1 -1
- package/dist/plugins/gitlab.js +2 -2
- package/dist/plugins/go.d.ts +1 -1
- package/dist/plugins/go.js +243 -28
- package/dist/providers/npm.d.ts +2 -2
- package/dist/providers/npm.js +1 -1
- package/dist/shared-C_iSTp_s.js +115 -0
- package/dist/{types-BoQR7Hlg.d.ts → types-B50RK1rR.d.ts} +295 -406
- package/package.json +7 -3
- package/dist/npm-Cj685Ddn.js +0 -681
- package/dist/shared-C92toqVI.js +0 -32
package/dist/npm-Cj685Ddn.js
DELETED
|
@@ -1,681 +0,0 @@
|
|
|
1
|
-
import { c as somePromise, i as isNodeError, n as execFailure, r as handlePluginError, s as joinPath } from "./error-BhMYq9iW.js";
|
|
2
|
-
import { n as WorkspacePackage } from "./graph-BmXTJZxx.js";
|
|
3
|
-
import { a as parseChangelogFile, i as parsePublishLock, r as packageStoreSchema, t as changelogStoreSchema } from "./draft-BLbt4bSG.js";
|
|
4
|
-
import fs, { readFile, writeFile } from "node:fs/promises";
|
|
5
|
-
import path, { join } from "node:path";
|
|
6
|
-
import { x } from "tinyexec";
|
|
7
|
-
import * as semver$1 from "semver";
|
|
8
|
-
import z from "zod";
|
|
9
|
-
import { load } from "js-yaml";
|
|
10
|
-
import { glob } from "tinyglobby";
|
|
11
|
-
import { detect } from "package-manager-detector";
|
|
12
|
-
import { tmpdir } from "node:os";
|
|
13
|
-
import { intro, note, outro } from "@clack/prompts";
|
|
14
|
-
//#region src/providers/npm/schema.ts
|
|
15
|
-
const stringRecordSchema = z.record(z.string(), z.string());
|
|
16
|
-
const pnpmWorkspaceSchema = z.looseObject({ packages: z.array(z.string()).optional() });
|
|
17
|
-
const packageManifestSchema = z.looseObject({
|
|
18
|
-
name: z.string(),
|
|
19
|
-
version: z.string().optional(),
|
|
20
|
-
private: z.boolean().optional(),
|
|
21
|
-
publishConfig: z.looseObject({
|
|
22
|
-
access: z.enum(["public", "restricted"]).optional(),
|
|
23
|
-
registry: z.string().optional(),
|
|
24
|
-
tag: z.string().optional()
|
|
25
|
-
}).optional(),
|
|
26
|
-
scripts: z.record(z.string(), z.string()).optional(),
|
|
27
|
-
workspaces: z.array(z.string()).optional(),
|
|
28
|
-
dependencies: stringRecordSchema.optional(),
|
|
29
|
-
devDependencies: stringRecordSchema.optional(),
|
|
30
|
-
peerDependencies: stringRecordSchema.optional(),
|
|
31
|
-
optionalDependencies: stringRecordSchema.optional()
|
|
32
|
-
});
|
|
33
|
-
//#endregion
|
|
34
|
-
//#region src/plans/publish.ts
|
|
35
|
-
async function initPublishPlan(context, options) {
|
|
36
|
-
let lock;
|
|
37
|
-
try {
|
|
38
|
-
lock = parsePublishLock(await fs.readFile(context.lockPath, "utf8"));
|
|
39
|
-
} catch {
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
let data;
|
|
43
|
-
const packages = /* @__PURE__ */ new Map();
|
|
44
|
-
const changelogs = /* @__PURE__ */ new Map();
|
|
45
|
-
while (data = lock.read("core:changelogs")) {
|
|
46
|
-
const entry = changelogStoreSchema.safeParse(data).data;
|
|
47
|
-
if (!entry) continue;
|
|
48
|
-
const parsed = parseChangelogFile(entry.filename, entry.content);
|
|
49
|
-
if (!parsed) continue;
|
|
50
|
-
changelogs.set(parsed.id, parsed);
|
|
51
|
-
}
|
|
52
|
-
while (data = lock.read("core:packages")) {
|
|
53
|
-
const parsed = packageStoreSchema.safeParse(data).data;
|
|
54
|
-
if (!parsed) continue;
|
|
55
|
-
if (!context.graph.get(parsed.id)) continue;
|
|
56
|
-
const pkgChangelogs = [];
|
|
57
|
-
for (const id of parsed.changelogIds ?? []) {
|
|
58
|
-
const entry = changelogs.get(id);
|
|
59
|
-
if (entry) pkgChangelogs.push(entry);
|
|
60
|
-
}
|
|
61
|
-
packages.set(parsed.id, {
|
|
62
|
-
changelogs: pkgChangelogs,
|
|
63
|
-
updated: parsed.updated
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
const plan = {
|
|
67
|
-
options,
|
|
68
|
-
changelogs,
|
|
69
|
-
packages
|
|
70
|
-
};
|
|
71
|
-
for (const plugin of context.plugins) await handlePluginError(plugin, "initPublishPlan", () => plugin.initPublishPlan?.call(context, {
|
|
72
|
-
lock,
|
|
73
|
-
plan
|
|
74
|
-
}));
|
|
75
|
-
return plan;
|
|
76
|
-
}
|
|
77
|
-
function resolvePublishTargets(plan) {
|
|
78
|
-
const ordered = [];
|
|
79
|
-
const scanned = /* @__PURE__ */ new Set();
|
|
80
|
-
function scan(id, stack) {
|
|
81
|
-
const preflight = plan.packages.get(id)?.preflight;
|
|
82
|
-
if (!preflight || !preflight.shouldPublish) return;
|
|
83
|
-
if (stack.has(id)) throw new Error(`circular reference of deps: ${[...stack, id].join(" -> ")}`);
|
|
84
|
-
if (scanned.has(id)) return;
|
|
85
|
-
if (preflight.wait) {
|
|
86
|
-
stack.add(id);
|
|
87
|
-
for (const dep of preflight.wait) scan(dep, stack);
|
|
88
|
-
stack.delete(id);
|
|
89
|
-
}
|
|
90
|
-
ordered.push(id);
|
|
91
|
-
scanned.add(id);
|
|
92
|
-
}
|
|
93
|
-
const stack = /* @__PURE__ */ new Set();
|
|
94
|
-
for (const id of plan.packages.keys()) scan(id, stack);
|
|
95
|
-
return ordered;
|
|
96
|
-
}
|
|
97
|
-
async function runPublishPlan(context, plan) {
|
|
98
|
-
const { dryRun = false } = plan.options;
|
|
99
|
-
const onPublishResult = async (pkg, publishResult) => {
|
|
100
|
-
const packagePlan = plan.packages.get(pkg.id);
|
|
101
|
-
if (!packagePlan) return;
|
|
102
|
-
packagePlan.publishResult = publishResult;
|
|
103
|
-
if (publishResult.type === "skipped") return;
|
|
104
|
-
for (const plugin of context.plugins) await handlePluginError(plugin, "afterPublish", () => plugin.afterPublish?.call(context, {
|
|
105
|
-
pkg,
|
|
106
|
-
plan
|
|
107
|
-
}));
|
|
108
|
-
};
|
|
109
|
-
publishLoop: for (const id of resolvePublishTargets(plan)) {
|
|
110
|
-
const pkg = context.graph.get(id);
|
|
111
|
-
if (dryRun) {
|
|
112
|
-
await onPublishResult(pkg, { type: "published" });
|
|
113
|
-
continue;
|
|
114
|
-
}
|
|
115
|
-
for (const plugin of context.plugins) if (await handlePluginError(plugin, "willPublish", () => plugin.willPublish?.call(context, { pkg })) === false) continue publishLoop;
|
|
116
|
-
for (const plugin of context.plugins) {
|
|
117
|
-
const publishResult = await handlePluginError(plugin, "publish", () => plugin.publish?.call(context, {
|
|
118
|
-
pkg,
|
|
119
|
-
plan
|
|
120
|
-
}));
|
|
121
|
-
if (publishResult) {
|
|
122
|
-
await onPublishResult(pkg, publishResult);
|
|
123
|
-
continue publishLoop;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
await onPublishResult(pkg, {
|
|
127
|
-
type: "failed",
|
|
128
|
-
error: `There is no plugin to publish package "${pkg.id}", please make sure the package has a supported provider plugin.`
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
for (const packagePlan of plan.packages.values()) packagePlan.publishResult ??= { type: "skipped" };
|
|
132
|
-
for (const plugin of context.plugins) await handlePluginError(plugin, "afterPublishAll", () => plugin.afterPublishAll?.call(context, { plan }));
|
|
133
|
-
}
|
|
134
|
-
async function runPreflights(context, plan) {
|
|
135
|
-
const promises = [];
|
|
136
|
-
for (const [id, packagePlan] of plan.packages) {
|
|
137
|
-
const pkg = context.graph.get(id);
|
|
138
|
-
packagePlan.preflight = { shouldPublish: false };
|
|
139
|
-
if (!packagePlan.updated) continue;
|
|
140
|
-
promises.push((async () => {
|
|
141
|
-
for (const plugin of context.plugins) {
|
|
142
|
-
const res = await handlePluginError(plugin, "publishPreflight", () => plugin.publishPreflight?.call(context, {
|
|
143
|
-
pkg,
|
|
144
|
-
plan
|
|
145
|
-
}));
|
|
146
|
-
if (res) {
|
|
147
|
-
packagePlan.preflight = res;
|
|
148
|
-
break;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
})());
|
|
152
|
-
}
|
|
153
|
-
await Promise.all(promises);
|
|
154
|
-
for (const plugin of context.plugins) await handlePluginError(plugin, "afterPreflight", () => plugin.afterPreflight?.call(context, { plan }));
|
|
155
|
-
}
|
|
156
|
-
async function publishPlanStatus(plan, context) {
|
|
157
|
-
for (const plugin of context.plugins) {
|
|
158
|
-
const status = await handlePluginError(plugin, "resolvePlanStatus", () => plugin.resolvePlanStatus?.call(context, { plan }));
|
|
159
|
-
if (Array.isArray(status) && await somePromise(status, (v) => v === "pending")) return "pending";
|
|
160
|
-
if (status === "pending") return "pending";
|
|
161
|
-
}
|
|
162
|
-
return "success";
|
|
163
|
-
}
|
|
164
|
-
//#endregion
|
|
165
|
-
//#region src/providers/npm/cli.ts
|
|
166
|
-
const PLACEHOLDER_VERSION = "0.0.0-tegami-trusted-publish-setup";
|
|
167
|
-
const PLACEHOLDER_DIST_TAG = "temp";
|
|
168
|
-
const PROJECT_FLAG = {
|
|
169
|
-
gitlab: "--project",
|
|
170
|
-
github: "--repo"
|
|
171
|
-
};
|
|
172
|
-
function registerNpmCli(cli, options) {
|
|
173
|
-
cli.command("npm pretrust", { description: "publish empty placeholder packages and configure npm trusted publishing for new packages" }).option("dry-run", {
|
|
174
|
-
type: "boolean",
|
|
175
|
-
description: "show packages that would be configured without publishing"
|
|
176
|
-
}).action(async ({ context, values }) => {
|
|
177
|
-
intro("npm pretrust");
|
|
178
|
-
if (!context.graph.getPackages().some((pkg) => pkg instanceof NpmPackage)) throw new Error("No npm packages found in the workspace.");
|
|
179
|
-
const dryRun = values["dry-run"] ?? false;
|
|
180
|
-
let repo;
|
|
181
|
-
if (options.provider === "github") {
|
|
182
|
-
if (!context.github?.repo) throw new Error("The GitHub plugin must be configured with `repo` specified.");
|
|
183
|
-
repo = context.github.repo;
|
|
184
|
-
} else if (options.provider === "gitlab") {
|
|
185
|
-
if (!context.gitlab?.repo) throw new Error("The GitLab plugin must be configured with `repo` specified.");
|
|
186
|
-
repo = context.gitlab.repo;
|
|
187
|
-
} else throw new Error(`Invalid npm trusted publishing provider: ${options.provider}`);
|
|
188
|
-
const targets = await resolvePretrustTargets(context);
|
|
189
|
-
if (targets.length === 0) {
|
|
190
|
-
outro("All publishable packages in the publish lock already exist on npm.");
|
|
191
|
-
return;
|
|
192
|
-
}
|
|
193
|
-
const prepareLines = ["Make sure to run login command first, it will publish empty packages."];
|
|
194
|
-
for (const pkg of targets) prepareLines.push(`${pkg.name}: will publish a placeholder under dist-tag "${PLACEHOLDER_DIST_TAG}", then configure trusted publishing.`);
|
|
195
|
-
note(prepareLines.join("\n"), dryRun ? "Dry run" : "Configure trusted publishing");
|
|
196
|
-
const lines = [];
|
|
197
|
-
let lock;
|
|
198
|
-
if (!dryRun) try {
|
|
199
|
-
lock = parsePublishLock(await fs.readFile(context.lockPath, "utf8"));
|
|
200
|
-
} catch {}
|
|
201
|
-
for (const pkg of targets) {
|
|
202
|
-
if (dryRun) {
|
|
203
|
-
lines.push(`would configure ${pkg.name} (placeholder ${PLACEHOLDER_VERSION}@${PLACEHOLDER_DIST_TAG})`);
|
|
204
|
-
continue;
|
|
205
|
-
}
|
|
206
|
-
await publishPlaceholder(pkg);
|
|
207
|
-
await npmTrust(context, pkg, options, repo);
|
|
208
|
-
lines.push(`configured ${pkg.name} (placeholder ${PLACEHOLDER_VERSION}@${PLACEHOLDER_DIST_TAG})`);
|
|
209
|
-
lock?.write("npm:mark-latest", { id: pkg.id });
|
|
210
|
-
}
|
|
211
|
-
if (lock) await fs.writeFile(context.lockPath, lock.serialize());
|
|
212
|
-
note(lines.join("\n"), "Result");
|
|
213
|
-
outro(dryRun ? "Dry run complete. Re-run without --dry-run to publish placeholders and configure trusted publishing." : "Trusted publishing configured. CI can now publish real package versions with OIDC.");
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
async function resolvePretrustTargets(context) {
|
|
217
|
-
const plan = await initPublishPlan(context, {});
|
|
218
|
-
if (!plan) throw new Error(`No publish lock found at ${context.lockPath}. Run "tegami version" before configuring trusted publishing.`);
|
|
219
|
-
await runPreflights(context, plan);
|
|
220
|
-
return (await Promise.all(Array.from(plan.packages, async ([id, { preflight }]) => {
|
|
221
|
-
if (!preflight?.shouldPublish) return;
|
|
222
|
-
const pkg = context.graph.get(id);
|
|
223
|
-
if (!pkg || !(pkg instanceof NpmPackage)) return;
|
|
224
|
-
if (await isPackageOnRegistry(pkg.name, pkg.getRegistry())) return;
|
|
225
|
-
return pkg;
|
|
226
|
-
}))).filter((pkg) => pkg !== void 0);
|
|
227
|
-
}
|
|
228
|
-
async function isPackageOnRegistry(name, registry = "https://registry.npmjs.org") {
|
|
229
|
-
const response = await fetch(joinPath(registry, name), { headers: { Accept: "application/json" } });
|
|
230
|
-
if (response.status === 404) return false;
|
|
231
|
-
if (!response.ok) throw new Error(`Unable to check whether ${name} exists on the npm registry${registry ? ` "${registry}"` : ""}.`);
|
|
232
|
-
return true;
|
|
233
|
-
}
|
|
234
|
-
async function publishPlaceholder(pkg) {
|
|
235
|
-
const registry = pkg.getRegistry();
|
|
236
|
-
const access = pkg.manifest.publishConfig?.access;
|
|
237
|
-
const dir = await fs.mkdtemp(join(tmpdir(), "tegami-npm-placeholder-"));
|
|
238
|
-
try {
|
|
239
|
-
await fs.writeFile(join(dir, "package.json"), `${JSON.stringify({
|
|
240
|
-
name: pkg.name,
|
|
241
|
-
version: PLACEHOLDER_VERSION,
|
|
242
|
-
description: "Placeholder published by Tegami for npm trusted publishing setup."
|
|
243
|
-
}, null, 2)}\n`);
|
|
244
|
-
await fs.writeFile(join(dir, "README.md"), `# Placeholder package
|
|
245
|
-
|
|
246
|
-
This empty package was published by [Tegami](https://tegami.fuma-nama.dev) to configure npm trusted publishing.
|
|
247
|
-
|
|
248
|
-
The real package contents will be published via CI with OIDC.
|
|
249
|
-
`);
|
|
250
|
-
const args = [
|
|
251
|
-
"publish",
|
|
252
|
-
"--tag",
|
|
253
|
-
PLACEHOLDER_DIST_TAG,
|
|
254
|
-
"--ignore-scripts",
|
|
255
|
-
"--registry",
|
|
256
|
-
registry
|
|
257
|
-
];
|
|
258
|
-
if (access) args.push("--access", access);
|
|
259
|
-
const result = await x("npm", args, { nodeOptions: {
|
|
260
|
-
cwd: dir,
|
|
261
|
-
stdio: "inherit"
|
|
262
|
-
} });
|
|
263
|
-
if (result.exitCode !== 0) {
|
|
264
|
-
const hint = result.stderr.includes("EOTP") ? " Complete npm 2FA in the terminal, or publish with an OTP-capable session." : "";
|
|
265
|
-
throw execFailure(`Failed to publish placeholder ${pkg.name}@${PLACEHOLDER_VERSION} with dist-tag "${PLACEHOLDER_DIST_TAG}".${hint}`, result);
|
|
266
|
-
}
|
|
267
|
-
} finally {
|
|
268
|
-
await fs.rm(dir, {
|
|
269
|
-
recursive: true,
|
|
270
|
-
force: true
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
async function npmTrust(context, pkg, options, repo) {
|
|
275
|
-
const result = await x("npm", [
|
|
276
|
-
"trust",
|
|
277
|
-
options.provider,
|
|
278
|
-
pkg.name,
|
|
279
|
-
PROJECT_FLAG[options.provider],
|
|
280
|
-
repo,
|
|
281
|
-
"--file",
|
|
282
|
-
options.workflow,
|
|
283
|
-
"--allow-publish",
|
|
284
|
-
"-y",
|
|
285
|
-
"--registry",
|
|
286
|
-
pkg.getRegistry()
|
|
287
|
-
], { nodeOptions: {
|
|
288
|
-
cwd: context.cwd,
|
|
289
|
-
stdio: "inherit"
|
|
290
|
-
} });
|
|
291
|
-
if (result.exitCode !== 0) {
|
|
292
|
-
const hint = result.stderr.includes("EOTP") ? " Complete npm 2FA in the terminal when prompted." : "";
|
|
293
|
-
throw execFailure(`Failed to configure trusted publishing for ${pkg.name}.${hint}`, result);
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
//#endregion
|
|
297
|
-
//#region src/providers/npm.ts
|
|
298
|
-
const DEP_FIELDS = [
|
|
299
|
-
"dependencies",
|
|
300
|
-
"devDependencies",
|
|
301
|
-
"peerDependencies",
|
|
302
|
-
"optionalDependencies"
|
|
303
|
-
];
|
|
304
|
-
var NpmPackage = class extends WorkspacePackage {
|
|
305
|
-
path;
|
|
306
|
-
manifest;
|
|
307
|
-
manager = "npm";
|
|
308
|
-
constructor(path, manifest) {
|
|
309
|
-
super();
|
|
310
|
-
this.path = path;
|
|
311
|
-
this.manifest = manifest;
|
|
312
|
-
}
|
|
313
|
-
get name() {
|
|
314
|
-
return this.manifest.name;
|
|
315
|
-
}
|
|
316
|
-
get version() {
|
|
317
|
-
return this.manifest.version;
|
|
318
|
-
}
|
|
319
|
-
async write() {
|
|
320
|
-
await writeFile(path.join(this.path, "package.json"), `${JSON.stringify(this.manifest, null, 2)}\n`);
|
|
321
|
-
}
|
|
322
|
-
initDraft() {
|
|
323
|
-
const defaults = super.initDraft();
|
|
324
|
-
defaults.npm = { distTag: this.manifest.publishConfig?.tag };
|
|
325
|
-
return defaults;
|
|
326
|
-
}
|
|
327
|
-
getRegistry() {
|
|
328
|
-
return this.manifest.publishConfig?.registry ?? "https://registry.npmjs.org";
|
|
329
|
-
}
|
|
330
|
-
configureDraft({ draft }) {
|
|
331
|
-
super.configureDraft({ draft });
|
|
332
|
-
const { distTag = this.group?.options?.npm?.distTag } = this.options.npm ?? {};
|
|
333
|
-
if (distTag) {
|
|
334
|
-
draft.npm ??= {};
|
|
335
|
-
draft.npm.distTag = distTag;
|
|
336
|
-
} else if (draft.prerelease) {
|
|
337
|
-
draft.npm ??= {};
|
|
338
|
-
draft.npm.distTag ??= draft.prerelease;
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
};
|
|
342
|
-
function parseDependencySpec(context, dependent, name, range) {
|
|
343
|
-
const { graph } = context;
|
|
344
|
-
if (range.startsWith("workspace:")) return {
|
|
345
|
-
range: range.slice(10),
|
|
346
|
-
linked: graph.get(`npm:${name}`),
|
|
347
|
-
protocol: "workspace"
|
|
348
|
-
};
|
|
349
|
-
if (range.startsWith("file:")) {
|
|
350
|
-
let target = path.resolve(dependent.path, range.slice(5));
|
|
351
|
-
if (path.basename(target) === "package.json") target = path.dirname(target);
|
|
352
|
-
return {
|
|
353
|
-
protocol: "file",
|
|
354
|
-
raw: range,
|
|
355
|
-
linked: graph.getPackages().find((pkg) => pkg instanceof NpmPackage && pkg.path === target)
|
|
356
|
-
};
|
|
357
|
-
}
|
|
358
|
-
if (range.startsWith("npm:")) {
|
|
359
|
-
const spec = range.slice(4);
|
|
360
|
-
const separator = spec.lastIndexOf("@");
|
|
361
|
-
if (separator <= 0) return;
|
|
362
|
-
const alias = spec.slice(0, separator);
|
|
363
|
-
return {
|
|
364
|
-
alias,
|
|
365
|
-
linked: graph.get(`npm:${alias}`),
|
|
366
|
-
range: spec.slice(separator + 1),
|
|
367
|
-
protocol: "npm"
|
|
368
|
-
};
|
|
369
|
-
}
|
|
370
|
-
return {
|
|
371
|
-
linked: graph.get(`npm:${name}`),
|
|
372
|
-
range
|
|
373
|
-
};
|
|
374
|
-
}
|
|
375
|
-
function formatDependencySpec(spec) {
|
|
376
|
-
if (spec.protocol === "workspace") return `workspace:${spec.range}`;
|
|
377
|
-
if (spec.protocol === "file") return spec.raw;
|
|
378
|
-
if (spec.protocol === "npm") return `npm:${spec.alias}@${spec.range}`;
|
|
379
|
-
return spec.range;
|
|
380
|
-
}
|
|
381
|
-
const packageLockSchema = z.object({
|
|
382
|
-
id: z.string(),
|
|
383
|
-
distTag: z.string().optional()
|
|
384
|
-
});
|
|
385
|
-
const markLatestLockSchema = z.object({ id: z.string() });
|
|
386
|
-
function npm({ client: defaultClient, onBreakPeerDep = "set", updateLockFile = true, trustedPublish, bumpDep: getBumpDepType = ({ kind }) => {
|
|
387
|
-
switch (kind) {
|
|
388
|
-
case "dependencies":
|
|
389
|
-
case "optionalDependencies": return "patch";
|
|
390
|
-
case "devDependencies": return false;
|
|
391
|
-
case "peerDependencies":
|
|
392
|
-
if (onBreakPeerDep === "ignore") return false;
|
|
393
|
-
return "major";
|
|
394
|
-
}
|
|
395
|
-
} } = {}) {
|
|
396
|
-
let active = false;
|
|
397
|
-
let client;
|
|
398
|
-
return {
|
|
399
|
-
name: "npm",
|
|
400
|
-
enforce: "pre",
|
|
401
|
-
async init() {
|
|
402
|
-
if (defaultClient) client = defaultClient;
|
|
403
|
-
else client = (await detect({ cwd: this.cwd }))?.name ?? "npm";
|
|
404
|
-
this.npm = { client };
|
|
405
|
-
},
|
|
406
|
-
async resolve() {
|
|
407
|
-
await discoverNpmPackages(this.cwd, (pkg) => this.graph.add(pkg));
|
|
408
|
-
active = this.graph.getPackages().some((pkg) => pkg instanceof NpmPackage);
|
|
409
|
-
},
|
|
410
|
-
async publishPreflight({ pkg }) {
|
|
411
|
-
if (!(pkg instanceof NpmPackage)) return;
|
|
412
|
-
return { shouldPublish: pkg.version !== void 0 && pkg.manifest.private !== true };
|
|
413
|
-
},
|
|
414
|
-
resolvePlanStatus({ plan }) {
|
|
415
|
-
if (!active) return;
|
|
416
|
-
return Array.from(plan.packages, async ([id, { preflight }]) => {
|
|
417
|
-
if (!preflight.shouldPublish) return;
|
|
418
|
-
const pkg = this.graph.get(id);
|
|
419
|
-
if (!(pkg instanceof NpmPackage) || !pkg.version) return;
|
|
420
|
-
if (!await isPackagePublished(pkg.name, pkg.version, pkg.getRegistry())) return "pending";
|
|
421
|
-
});
|
|
422
|
-
},
|
|
423
|
-
initPublishLock({ lock, draft }) {
|
|
424
|
-
for (const [id, pkg] of draft.getPackageDrafts()) {
|
|
425
|
-
if (!pkg.npm) continue;
|
|
426
|
-
lock.write("npm:packages", {
|
|
427
|
-
id,
|
|
428
|
-
distTag: pkg.npm.distTag
|
|
429
|
-
});
|
|
430
|
-
}
|
|
431
|
-
},
|
|
432
|
-
initPublishPlan({ lock, plan }) {
|
|
433
|
-
let data;
|
|
434
|
-
while (data = lock.read("npm:packages")) {
|
|
435
|
-
const parsed = packageLockSchema.safeParse(data).data;
|
|
436
|
-
if (!parsed) continue;
|
|
437
|
-
const packagePlan = plan.packages.get(parsed.id);
|
|
438
|
-
if (!packagePlan) continue;
|
|
439
|
-
packagePlan.npm = { distTag: parsed.distTag };
|
|
440
|
-
}
|
|
441
|
-
while (data = lock.read("npm:mark-latest")) {
|
|
442
|
-
const parsed = markLatestLockSchema.safeParse(data).data;
|
|
443
|
-
if (!parsed) continue;
|
|
444
|
-
const packagePlan = plan.packages.get(parsed.id);
|
|
445
|
-
if (!packagePlan) continue;
|
|
446
|
-
packagePlan.npm ??= {};
|
|
447
|
-
packagePlan.npm.markLatest = true;
|
|
448
|
-
}
|
|
449
|
-
},
|
|
450
|
-
async publish({ pkg, plan }) {
|
|
451
|
-
if (!(pkg instanceof NpmPackage)) return;
|
|
452
|
-
const { distTag, markLatest } = plan.packages.get(pkg.id)?.npm ?? {};
|
|
453
|
-
const result = await publish(client, pkg, distTag);
|
|
454
|
-
if (result.type === "published" && markLatest) {
|
|
455
|
-
const tagResult = await x("npm", [
|
|
456
|
-
"dist-tag",
|
|
457
|
-
"add",
|
|
458
|
-
`${pkg.name}@${pkg.version}`,
|
|
459
|
-
"latest",
|
|
460
|
-
"--registry",
|
|
461
|
-
pkg.getRegistry()
|
|
462
|
-
], { nodeOptions: { cwd: pkg.path } });
|
|
463
|
-
if (tagResult.exitCode !== 0) return {
|
|
464
|
-
type: "failed",
|
|
465
|
-
error: execFailure("Failed to mark package as latest", tagResult).message
|
|
466
|
-
};
|
|
467
|
-
}
|
|
468
|
-
return result;
|
|
469
|
-
},
|
|
470
|
-
initDraft(plan) {
|
|
471
|
-
if (!active) return;
|
|
472
|
-
plan.addPolicy(depsPolicy(this, getBumpDepType));
|
|
473
|
-
},
|
|
474
|
-
async applyDraft(draft) {
|
|
475
|
-
if (!active) return;
|
|
476
|
-
const { graph } = this;
|
|
477
|
-
const writes = [];
|
|
478
|
-
for (const pkg of graph.getPackages()) {
|
|
479
|
-
if (!(pkg instanceof NpmPackage)) continue;
|
|
480
|
-
const bumped = draft.getPackageDraft(pkg.id)?.bumpVersion(pkg);
|
|
481
|
-
if (bumped) pkg.manifest.version = bumped;
|
|
482
|
-
}
|
|
483
|
-
for (const pkg of graph.getPackages()) {
|
|
484
|
-
if (!(pkg instanceof NpmPackage)) continue;
|
|
485
|
-
for (const field of DEP_FIELDS) {
|
|
486
|
-
const dependencies = pkg.manifest[field];
|
|
487
|
-
if (!dependencies) continue;
|
|
488
|
-
for (const [k, v] of Object.entries(dependencies)) {
|
|
489
|
-
const spec = parseDependencySpec(this, pkg, k, v);
|
|
490
|
-
if (!spec?.linked || spec.protocol === "workspace" || spec.protocol === "file") continue;
|
|
491
|
-
if (!semver$1.validRange(spec.range)) continue;
|
|
492
|
-
if (!spec.linked.version || semver$1.satisfies(spec.linked.version, spec.range)) continue;
|
|
493
|
-
let updatedRange;
|
|
494
|
-
const isPeer = field === "peerDependencies";
|
|
495
|
-
if (isPeer && onBreakPeerDep === "ignore") continue;
|
|
496
|
-
if (isPeer && onBreakPeerDep === "set") updatedRange = spec.linked.version;
|
|
497
|
-
else if (isPeer && onBreakPeerDep === "error") throw new Error(`[Tegami] the version of "${spec.linked.name}" is beyond its peer dependency constraint "${v}" in package "${pkg.name}", please update the constraint to satisfy.`);
|
|
498
|
-
else if (spec.range.startsWith("^")) updatedRange = `^${spec.linked.version}`;
|
|
499
|
-
else if (spec.range.startsWith("~")) updatedRange = `~${spec.linked.version}`;
|
|
500
|
-
else updatedRange = spec.linked.version;
|
|
501
|
-
dependencies[k] = formatDependencySpec({
|
|
502
|
-
...spec,
|
|
503
|
-
range: updatedRange
|
|
504
|
-
});
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
writes.push(pkg.write());
|
|
508
|
-
}
|
|
509
|
-
await Promise.all(writes);
|
|
510
|
-
},
|
|
511
|
-
async applyCliDraft() {
|
|
512
|
-
if (!active || !updateLockFile) return;
|
|
513
|
-
let args;
|
|
514
|
-
if (client === "npm") args = ["ci"];
|
|
515
|
-
else if (client === "yarn") args = ["install", "--immutable"];
|
|
516
|
-
else if (client === "bun") args = ["install", "--frozen-lockfile"];
|
|
517
|
-
else args = ["install", "--frozen-lockfile"];
|
|
518
|
-
const result = await x(client, args, { nodeOptions: { cwd: this.cwd } });
|
|
519
|
-
if (result.exitCode !== 0) throw execFailure("Failed to update lockfile.", result);
|
|
520
|
-
},
|
|
521
|
-
initCli(cli) {
|
|
522
|
-
if (!trustedPublish) return;
|
|
523
|
-
registerNpmCli(cli, trustedPublish);
|
|
524
|
-
}
|
|
525
|
-
};
|
|
526
|
-
}
|
|
527
|
-
function depsPolicy(context, getBumpDepType) {
|
|
528
|
-
const { graph } = context;
|
|
529
|
-
const dependentMap = /* @__PURE__ */ new Map();
|
|
530
|
-
for (const pkg of graph.getPackages()) {
|
|
531
|
-
if (!(pkg instanceof NpmPackage)) continue;
|
|
532
|
-
for (const kind of DEP_FIELDS) {
|
|
533
|
-
const dependencies = pkg.manifest[kind];
|
|
534
|
-
if (!dependencies) continue;
|
|
535
|
-
for (const [name, range] of Object.entries(dependencies)) {
|
|
536
|
-
const spec = parseDependencySpec(context, pkg, name, range);
|
|
537
|
-
if (!spec?.linked) continue;
|
|
538
|
-
const refs = dependentMap.get(spec.linked.id);
|
|
539
|
-
if (refs) refs.push({
|
|
540
|
-
dependent: pkg,
|
|
541
|
-
kind,
|
|
542
|
-
name,
|
|
543
|
-
spec
|
|
544
|
-
});
|
|
545
|
-
else dependentMap.set(spec.linked.id, [{
|
|
546
|
-
dependent: pkg,
|
|
547
|
-
kind,
|
|
548
|
-
name,
|
|
549
|
-
spec
|
|
550
|
-
}]);
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
function needsUpdate(spec, target) {
|
|
555
|
-
if (spec.linked && spec.protocol === "workspace") switch (spec.range) {
|
|
556
|
-
case "":
|
|
557
|
-
case "*": return true;
|
|
558
|
-
case "^":
|
|
559
|
-
case "~": return !semver$1.satisfies(target, `${spec.range}${spec.linked.version}`);
|
|
560
|
-
}
|
|
561
|
-
if (spec.linked && spec.protocol === "file") return true;
|
|
562
|
-
if (spec.protocol === "file" || !semver$1.validRange(spec.range)) return false;
|
|
563
|
-
return !semver$1.satisfies(target, spec.range);
|
|
564
|
-
}
|
|
565
|
-
return {
|
|
566
|
-
id: "npm:deps",
|
|
567
|
-
onUpdate({ pkg, packageDraft: plan }) {
|
|
568
|
-
if (!(pkg instanceof NpmPackage)) return;
|
|
569
|
-
const deps = dependentMap.get(pkg.id);
|
|
570
|
-
if (!deps) return;
|
|
571
|
-
const bumped = plan.bumpVersion(pkg);
|
|
572
|
-
if (!bumped) return;
|
|
573
|
-
for (const dep of deps) {
|
|
574
|
-
if (pkg.group?.options.syncBump && dep.dependent.group === pkg.group) continue;
|
|
575
|
-
if (!needsUpdate(dep.spec, bumped)) continue;
|
|
576
|
-
const bumpType = getBumpDepType(dep);
|
|
577
|
-
if (bumpType === false) continue;
|
|
578
|
-
this.bumpPackage(dep.dependent, {
|
|
579
|
-
type: bumpType,
|
|
580
|
-
reason: `update dependency "${dep.name}"`
|
|
581
|
-
});
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
};
|
|
585
|
-
}
|
|
586
|
-
async function publish(client, pkg, distTag) {
|
|
587
|
-
if (!pkg.version || await isPackagePublished(pkg.name, pkg.version, pkg.getRegistry())) return { type: "skipped" };
|
|
588
|
-
if (client === "bun") {
|
|
589
|
-
for (const script of [
|
|
590
|
-
"prepublishOnly",
|
|
591
|
-
"prepack",
|
|
592
|
-
"prepare"
|
|
593
|
-
]) {
|
|
594
|
-
if (!pkg.manifest.scripts?.[script]) continue;
|
|
595
|
-
const result = await x("bun", ["run", script], { nodeOptions: { cwd: pkg.path } });
|
|
596
|
-
if (result.exitCode === 0) continue;
|
|
597
|
-
return {
|
|
598
|
-
type: "failed",
|
|
599
|
-
error: execFailure(`Failed to run ${script} script for ${pkg.name}@${pkg.version}.`, result).message
|
|
600
|
-
};
|
|
601
|
-
}
|
|
602
|
-
const tarballPath = path.resolve(pkg.path, "pkg.tgz");
|
|
603
|
-
const packResult = await x("bun", [
|
|
604
|
-
"pm",
|
|
605
|
-
"pack",
|
|
606
|
-
"--filename",
|
|
607
|
-
tarballPath
|
|
608
|
-
], { nodeOptions: { cwd: pkg.path } });
|
|
609
|
-
if (packResult.exitCode !== 0) return {
|
|
610
|
-
type: "failed",
|
|
611
|
-
error: execFailure(`Failed to pack ${pkg.name}@${pkg.version}.`, packResult).message
|
|
612
|
-
};
|
|
613
|
-
const publishArgs = ["publish", tarballPath];
|
|
614
|
-
if (distTag) publishArgs.push("--tag", distTag);
|
|
615
|
-
const publishResult = await x("npm", publishArgs, { nodeOptions: { cwd: pkg.path } });
|
|
616
|
-
if (publishResult.exitCode !== 0) return {
|
|
617
|
-
type: "failed",
|
|
618
|
-
error: execFailure(`Failed to publish ${pkg.name}@${pkg.version}${distTag ? ` with dist-tag "${distTag}"` : ""}.`, publishResult).message
|
|
619
|
-
};
|
|
620
|
-
return { type: "published" };
|
|
621
|
-
}
|
|
622
|
-
let command;
|
|
623
|
-
const args = ["publish"];
|
|
624
|
-
if (distTag) args.push("--tag", distTag);
|
|
625
|
-
if (client === "pnpm") {
|
|
626
|
-
command = "pnpm";
|
|
627
|
-
args.push("--no-git-checks");
|
|
628
|
-
} else if (client === "yarn") command = "yarn";
|
|
629
|
-
else command = "npm";
|
|
630
|
-
const result = await x(command, args, { nodeOptions: { cwd: pkg.path } });
|
|
631
|
-
if (result.exitCode !== 0) return {
|
|
632
|
-
type: "failed",
|
|
633
|
-
error: execFailure(`Failed to publish ${pkg.name}@${pkg.version}${distTag ? ` with dist-tag "${distTag}"` : ""}.`, result).message
|
|
634
|
-
};
|
|
635
|
-
return { type: "published" };
|
|
636
|
-
}
|
|
637
|
-
async function isPackagePublished(name, version, registry) {
|
|
638
|
-
const response = await fetch(joinPath(registry, name, version), { headers: { Accept: "application/json" } });
|
|
639
|
-
if (response.status === 404) return false;
|
|
640
|
-
if (!response.ok) throw new Error(`Unable to validate ${name}@${version} against the npm registry${registry ? ` "${registry}"` : ""}.`);
|
|
641
|
-
return true;
|
|
642
|
-
}
|
|
643
|
-
async function discoverNpmPackages(cwd, add) {
|
|
644
|
-
const rootManifest = await readManifest(cwd).catch(() => void 0);
|
|
645
|
-
const pnpmPatterns = await readFile(path.join(cwd, "pnpm-workspace.yaml"), "utf8").then((content) => pnpmWorkspaceSchema.parse(load(content) ?? {})).catch((error) => {
|
|
646
|
-
if (isNodeError(error) && error.code === "ENOENT") return void 0;
|
|
647
|
-
throw error;
|
|
648
|
-
});
|
|
649
|
-
if (rootManifest?.name) add(new NpmPackage(cwd, rootManifest));
|
|
650
|
-
const patterns = pnpmPatterns?.packages ?? rootManifest?.workspaces;
|
|
651
|
-
if (!patterns || patterns.length === 0) return;
|
|
652
|
-
const candidatePaths = await expandWorkspacePatterns(cwd, patterns);
|
|
653
|
-
const manifests = await Promise.all(candidatePaths.map((path) => readManifest(path).then((manifest) => ({
|
|
654
|
-
path,
|
|
655
|
-
manifest
|
|
656
|
-
})).catch(() => void 0)));
|
|
657
|
-
for (const entry of manifests) {
|
|
658
|
-
if (!entry) continue;
|
|
659
|
-
add(new NpmPackage(entry.path, entry.manifest));
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
async function expandWorkspacePatterns(cwd, patterns) {
|
|
663
|
-
if (patterns.length === 0) return [];
|
|
664
|
-
return (await glob(patterns, {
|
|
665
|
-
absolute: true,
|
|
666
|
-
cwd,
|
|
667
|
-
ignore: ["**/node_modules/**", "**/dist/**"],
|
|
668
|
-
onlyDirectories: true,
|
|
669
|
-
onlyFiles: false
|
|
670
|
-
})).map((item) => {
|
|
671
|
-
return item.endsWith(path.sep) ? item.slice(0, -1) : item;
|
|
672
|
-
});
|
|
673
|
-
}
|
|
674
|
-
async function readManifest(packagePath) {
|
|
675
|
-
const content = await readFile(path.join(packagePath, "package.json"), "utf8");
|
|
676
|
-
const parsed = JSON.parse(content);
|
|
677
|
-
packageManifestSchema.parse(parsed);
|
|
678
|
-
return parsed;
|
|
679
|
-
}
|
|
680
|
-
//#endregion
|
|
681
|
-
export { runPreflights as a, publishPlanStatus as i, npm as n, runPublishPlan as o, initPublishPlan as r, NpmPackage as t };
|