forked from Lilly-Protocol/lily-sdk
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathadd.mjs
More file actions
289 lines (289 loc) · 13.1 KB
/
Copy pathadd.mjs
File metadata and controls
289 lines (289 loc) · 13.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
import { t as src_default } from "./src.mjs";
import { t as getCommitFunctions } from "./getCommitFunctions.mjs";
import { a as importantWarning, i as askQuestion, n as askList, r as askMultiselect, t as askConfirm } from "./cli-utilities.mjs";
import { t as readConfig } from "./read-config.mjs";
import { t as getVersionableChangedPackages } from "./versionablePackages.mjs";
import { t as ensureChangesetFolder } from "./shared.mjs";
import { ExitError, InternalError } from "@changesets/errors";
import { confirm, isCancel, log, note } from "@clack/prompts";
import path from "node:path";
import { fileURLToPath } from "node:url";
import * as git from "@changesets/git";
import { shouldSkipPackage } from "@changesets/should-skip-package";
import { writeChangeset } from "@changesets/write";
import { getPackages } from "@manypkg/get-packages";
import launchEditor from "launch-editor";
import fs from "node:fs/promises";
import semverLt from "semver/functions/lt.js";
import { tmpdir } from "node:os";
//#region src/utils/askWithEditor.ts
async function askWithEditor(initialContents = "") {
const tmpDir = await fs.mkdtemp(tmpdir());
const tmpFile = path.join(tmpDir, "changeset.md");
await fs.writeFile(tmpFile, initialContents);
launchEditor(tmpFile);
const done = await confirm({
message: "Opening external editor...",
active: "Continue",
inactive: "Cancel",
initialValue: true
});
if (!done || isCancel(done)) {
await fs.rm(tmpDir, { recursive: true });
return "";
}
const contents = await fs.readFile(tmpFile, "utf8");
await fs.rm(tmpDir, { recursive: true });
return contents.replace(/^#.*\n?/gm, "").replace(/\n+$/g, "").trim();
}
//#endregion
//#region src/commands/add/createChangeset.ts
async function confirmMajorRelease({ name, version }) {
if (semverLt(version, "1.0.0")) {
importantWarning(`
The ${src_default.red("major")} version of ${src_default.blue(name)} will be its ${src_default.red("first major release")} (1.0.0).
If you are unsure if this is correct, contact the package's maintainers ${src_default.red("before committing this changeset")}.
`);
return askConfirm(`Are you sure you want to release the ${src_default.red("first major version")} of ${src_default.blue(name)}?`);
}
return true;
}
async function getPackagesToRelease(changedPackages, allPackages) {
if (allPackages.length <= 1) throw new InternalError("getPackagesToRelease should not be called if there is only one package");
const allSortedPackages = allPackages.toSorted((a, b) => a.packageJson.name.localeCompare(b.packageJson.name));
const changedPackagesList = [];
const unchangedPackagesList = [];
for (const { packageJson } of allSortedPackages) {
const pkgName = packageJson.name;
(changedPackages.includes(pkgName) ? changedPackagesList : unchangedPackagesList).push({
label: pkgName + (packageJson.private ? " (private)" : ""),
value: pkgName
});
}
const multiselectValues = {};
if (changedPackagesList.length > 0) multiselectValues["changed packages"] = changedPackagesList;
if (unchangedPackagesList.length > 0) multiselectValues["unchanged packages"] = unchangedPackagesList;
return await askMultiselect("Which packages were affected by the changes you made?", multiselectValues, { required: true });
}
function getPkgJsonsByName(packages) {
return new Map(packages.map(({ packageJson }) => [packageJson.name, packageJson]));
}
function formatPkgNameAndVersion(pkgName, version) {
return `${src_default.bold(pkgName)}@${src_default.bold(version)}`;
}
function validateSelectedPackageNames(pkgNames, optionsFromCli, messages) {
for (const [flag, packageNamesFromCli] of [
["--major", optionsFromCli?.major],
["--minor", optionsFromCli?.minor],
["--patch", optionsFromCli?.patch]
]) for (const pkgName of packageNamesFromCli ?? []) {
if (pkgNames.has(pkgName)) continue;
messages.push(`The package ${src_default.blue(pkgName)} is passed to the \`${flag}\` option but it is not found in the project. You may have misspelled the package name.`);
}
}
function validateDuplicatePackageNames(pkgNames, optionsFromCli, messages) {
const major = new Set(optionsFromCli?.major).intersection(pkgNames);
const minor = new Set(optionsFromCli?.minor).intersection(pkgNames);
const patch = new Set(optionsFromCli?.patch).intersection(pkgNames);
const duplicates = major.intersection(minor).union(major.intersection(patch)).union(minor.intersection(patch));
for (const pkgName of duplicates) {
const flags = [
major.has(pkgName) && "--major",
minor.has(pkgName) && "--minor",
patch.has(pkgName) && "--patch"
].filter((flag) => flag !== false);
messages.push(`The package ${src_default.blue(pkgName)} is passed to multiple release type options: ${flags.map((flag) => `\`${flag}\``).join(", ")}. Please select only one release type for this package.`);
}
}
async function createChangeset(changedPackages, allPackages, optionsFromCli) {
const releases = [];
if (optionsFromCli?.major || optionsFromCli?.minor || optionsFromCli?.patch) {
const pkgNames = new Set(allPackages.map(({ packageJson }) => packageJson.name));
const messages = [];
validateSelectedPackageNames(pkgNames, optionsFromCli, messages);
validateDuplicatePackageNames(pkgNames, optionsFromCli, messages);
if (messages.length > 0) {
log.error(messages.join("\n"));
throw new ExitError(1);
}
for (const [type, packageNamesFromCli] of [
["major", optionsFromCli?.major],
["minor", optionsFromCli?.minor],
["patch", optionsFromCli?.patch]
]) for (const pkgName of packageNamesFromCli ?? []) releases.push({
name: pkgName,
type
});
} else if (allPackages.length > 1) {
const packagesToRelease = await getPackagesToRelease(changedPackages, allPackages);
packagesToRelease.sort((a, b) => a.localeCompare(b));
const pkgJsonsByName = getPkgJsonsByName(allPackages);
const pkgsLeftToGetBumpTypeFor = new Set(packagesToRelease);
const pkgsThatShouldBeMajorBumped = await askMultiselect(src_default.bold(`Which packages should have a ${src_default.red("major")} ${src_default.gray(`(${src_default.red("X")}.X.X)`)} bump?`), { "all packages": packagesToRelease.map((pkgName) => ({
label: formatPkgNameAndVersion(pkgName, pkgJsonsByName.get(pkgName).version),
value: pkgName
})) });
for (const pkgName of pkgsThatShouldBeMajorBumped) if (await confirmMajorRelease(pkgJsonsByName.get(pkgName))) {
pkgsLeftToGetBumpTypeFor.delete(pkgName);
releases.push({
name: pkgName,
type: "major"
});
}
if (pkgsLeftToGetBumpTypeFor.size !== 0) {
const pkgsThatShouldBeMinorBumped = await askMultiselect(src_default.bold(`Which packages should have a ${src_default.green("minor")} ${src_default.gray(`(X.${src_default.green("X")}.X)`)} bump?`), { "all packages": Array.from(pkgsLeftToGetBumpTypeFor, (pkgName) => ({
label: formatPkgNameAndVersion(pkgName, pkgJsonsByName.get(pkgName).version),
value: pkgName
})) });
for (const pkgName of pkgsThatShouldBeMinorBumped) {
pkgsLeftToGetBumpTypeFor.delete(pkgName);
releases.push({
name: pkgName,
type: "minor"
});
}
}
if (pkgsLeftToGetBumpTypeFor.size !== 0) {
const patchBumpedPackages = Array.from(pkgsLeftToGetBumpTypeFor, (pkgName) => formatPkgNameAndVersion(pkgName, pkgJsonsByName.get(pkgName).version));
log.info(`
The following packages will be ${src_default.blue("patch")} ${src_default.gray(`(X.X.${src_default.blue("X")})`)} bumped:
${src_default.gray(patchBumpedPackages.join(", "))}
`.trim());
for (const pkgName of pkgsLeftToGetBumpTypeFor) releases.push({
name: pkgName,
type: "patch"
});
}
} else {
const pkg = allPackages[0];
const type = await askList(`What kind of change is this for ${src_default.blue(pkg.packageJson.name)}? ${src_default.gray(`(current version is ${pkg.packageJson.version})`)}`, [
{
value: "patch",
label: `patch ${src_default.gray(`(X.X.${src_default.blue("X")})`)}`
},
{
value: "minor",
label: `minor ${src_default.gray(`(X.${src_default.green("X")}.X)`)}`
},
{
value: "major",
label: `major ${src_default.gray(`(${src_default.red("X")}.X.X)`)}`
}
]);
if (type === "major") {
if (!await confirmMajorRelease(pkg.packageJson)) throw new ExitError(1);
}
releases.push({
name: pkg.packageJson.name,
type
});
}
if (optionsFromCli?.message != null) return {
summary: optionsFromCli.message,
releases
};
let summary = await askQuestion("Please enter a summary for this change (this will be in the changelogs).", { placeholder: " (submit nothing to open an external editor)" });
if (summary.length === 0) {
try {
summary = await askWithEditor("\n\n# Please enter a summary for your changes.\n# An empty message aborts the editor.");
if (summary.length > 0) return {
summary,
releases
};
} catch {
summary = await askQuestion(`${src_default.red("An error happened using external editor. Please type your summary here:")}`, { notEmpty: true });
}
summary ||= await askQuestion("Did not find a summary in the edited file. Please enter one:", { notEmpty: true });
}
return {
summary,
releases
};
}
//#endregion
//#region src/commands/add/messages.ts
function printConfirmationMessage(changeset, repoHasMultiplePackages) {
function getReleasesOfType(type) {
return changeset.releases.filter((release) => release.type === type).map((release) => release.name);
}
const majorReleases = getReleasesOfType("major");
const minorReleases = getReleasesOfType("minor");
const patchReleases = getReleasesOfType("patch");
let msg = src_default.bold("Summary of changesets:");
if (majorReleases.length > 0) msg += `\n${src_default.bold(src_default.red("major"))}: ${majorReleases.join(", ")}`;
if (minorReleases.length > 0) msg += `\n${src_default.bold(src_default.green("minor"))}: ${minorReleases.join(", ")}`;
if (patchReleases.length > 0) msg += `\n${src_default.bold(src_default.blue("patch"))}: ${patchReleases.join(", ")}`;
log.success(msg);
if (repoHasMultiplePackages) note(`All packages that depend on these whose required versions will be incompatible will also be ${src_default.blue("patch")} bumped when this changeset is applied.`, "NOTE");
}
//#endregion
//#region src/commands/add/index.ts
async function add(options) {
const packages = await getPackages(options?.cwd ?? process.cwd());
await ensureChangesetFolder(packages.rootDir);
if (packages.packages.length === 0) {
log.error(`No packages found. You might have ${packages.tool.type} workspaces configured but no packages yet?`);
throw new ExitError(1);
}
const config = await readConfig(packages);
const versionablePackages = packages.packages.filter((pkg) => !shouldSkipPackage(pkg, {
ignore: config.ignore,
allowPrivatePackages: config.privatePackages.version
}));
if (versionablePackages.length === 0) {
log.error(`
No versionable packages found
${src_default.italic("Ensure the packages to version are not ignored by the config")}
${src_default.italic("Ensure that relevant package.json files have a `version` field")}
`.trim());
throw new ExitError(1);
}
const changesetBase = path.resolve(packages.rootDir, ".changeset");
let newChangeset;
if (options?.empty) newChangeset = {
releases: [],
summary: options?.message ?? ""
};
else {
let changedPackagesNames = [];
try {
changedPackagesNames = (await getVersionableChangedPackages(config, {
cwd: packages.rootDir,
ref: options?.since
})).map((pkg) => pkg.packageJson.name);
} catch (error) {
log.warn(`
Failed to identify which packages have changed since the ${options?.since ? "ref" : "base branch"} due to an error:
${error.toString()}
`.trim());
}
newChangeset = await createChangeset(changedPackagesNames, versionablePackages, {
message: options?.message,
major: options?.major,
minor: options?.minor,
patch: options?.patch
});
printConfirmationMessage(newChangeset, versionablePackages.length > 1);
}
const changesetID = await writeChangeset(newChangeset, packages.rootDir, config);
const [{ getAddMessage }, commitOpts] = await getCommitFunctions(config.commit, packages.rootDir, path.dirname(fileURLToPath(import.meta.url)));
const finalLogMessageLines = [];
if (getAddMessage) {
await git.add(path.resolve(changesetBase, `${changesetID}.md`), packages.rootDir);
await git.commit(await getAddMessage(newChangeset, commitOpts), packages.rootDir);
finalLogMessageLines.push(src_default.green(`${options?.empty ? "Empty " : ""}Changeset added and committed!`));
} else finalLogMessageLines.push(src_default.green(`${options?.empty ? "Empty " : ""}Changeset added - you can now commit it!`));
if ([...newChangeset.releases].find((c) => c.type === "major")) importantWarning(`
This Changeset includes a major change and we STRONGLY recommend adding more information to the changeset:
WHAT the breaking change is
WHY the change was made
HOW a consumer should update their code
`);
else finalLogMessageLines.push(src_default.green("If you want to modify or expand on the changeset summary, you can find it here:"));
const changesetPath = path.relative(process.cwd(), path.join(changesetBase, `${changesetID}.md`));
finalLogMessageLines.push(src_default.blue(changesetPath));
log.success(finalLogMessageLines.join("\n"));
if (options?.open) launchEditor(changesetPath);
}
//#endregion
export { add };