UNPKG

38.4 kBJavaScriptView Raw
1"use strict";
2
3var meow = require("meow"), errors = require("@changesets/errors"), logger = require("@changesets/logger"), util = require("util"), fs = require("fs-extra"), path = require("path"), getPackages = require("@manypkg/get-packages"), getDependentsGraph = require("@changesets/get-dependents-graph"), config = require("@changesets/config"), chalk = require("chalk"), termSize = require("term-size"), enquirer = require("enquirer"), externalEditor = require("external-editor"), git = require("@changesets/git"), writeChangeset = require("@changesets/write"), semver = require("semver"), boxen = require("boxen"), outdent = require("outdent"), applyReleasePlan = require("@changesets/apply-release-plan"), readChangesets = require("@changesets/read"), assembleReleasePlan = require("@changesets/assemble-release-plan"), pre$1 = require("@changesets/pre"), pLimit = require("p-limit"), preferredPM = require("preferred-pm"), spawn = require("spawndamnit"), isCI$1 = require("is-ci"), table = require("tty-table"), getReleasePlan = require("@changesets/get-release-plan");
4
5function _interopDefault(e) {
6 return e && e.__esModule ? e : {
7 default: e
8 };
9}
10
11var meow__default = _interopDefault(meow), fs__default = _interopDefault(fs), path__default = _interopDefault(path), chalk__default = _interopDefault(chalk), termSize__default = _interopDefault(termSize), writeChangeset__default = _interopDefault(writeChangeset), semver__default = _interopDefault(semver), boxen__default = _interopDefault(boxen), outdent__default = _interopDefault(outdent), applyReleasePlan__default = _interopDefault(applyReleasePlan), readChangesets__default = _interopDefault(readChangesets), assembleReleasePlan__default = _interopDefault(assembleReleasePlan), pLimit__default = _interopDefault(pLimit), preferredPM__default = _interopDefault(preferredPM), spawn__default = _interopDefault(spawn), isCI__default = _interopDefault(isCI$1), table__default = _interopDefault(table), getReleasePlan__default = _interopDefault(getReleasePlan);
12
13const pkgPath = path__default.default.dirname(require.resolve("@changesets/cli/package.json"));
14
15async function init(cwd) {
16 const changesetBase = path__default.default.resolve(cwd, ".changeset");
17 fs__default.default.existsSync(changesetBase) ? fs__default.default.existsSync(path__default.default.join(changesetBase, "config.json")) ? logger.warn("It looks like you already have changesets initialized. You should be able to run changeset commands no problems.") : (fs__default.default.existsSync(path__default.default.join(changesetBase, "config.js")) ? (logger.error("It looks like you're using the version 1 `.changeset/config.js` file"),
18 logger.error("The format of the config object has significantly changed in v2 as well"),
19 logger.error(" - we thoroughly recommend looking at the changelog for this package for what has changed"),
20 logger.error("Changesets will write the defaults for the new config, remember to transfer your options into the new config at `.changeset/config.json`")) : (logger.error("It looks like you don't have a config file"),
21 logger.info("The default config file will be written at `.changeset/config.json`")),
22 await fs__default.default.writeFile(path__default.default.resolve(changesetBase, "config.json"), JSON.stringify(config.defaultWrittenConfig, null, 2))) : (await fs__default.default.copy(path__default.default.resolve(pkgPath, "./default-files"), changesetBase),
23 await fs__default.default.writeFile(path__default.default.resolve(changesetBase, "config.json"), JSON.stringify(config.defaultWrittenConfig, null, 2)),
24 logger.log(chalk__default.default`Thanks for choosing {green changesets} to help manage your versioning and publishing\n`),
25 logger.log("You should be set up to start using changesets now!\n"), logger.info("We have added a `.changeset` folder, and a couple of files to help you out:"),
26 logger.info(chalk__default.default`- {blue .changeset/README.md} contains information about using changesets`),
27 logger.info(chalk__default.default`- {blue .changeset/config.json} is our default config`));
28}
29
30const serialId = function() {
31 let id = 0;
32 return () => id++;
33}(), limit = Math.max(termSize__default.default().rows - 5, 10);
34
35let cancelFlow = () => {
36 logger.success("Cancelled... 👋 "), process.exit();
37};
38
39async function askCheckboxPlus(message, choices, format) {
40 const name = "CheckboxPlus-" + serialId();
41 return enquirer.prompt({
42 type: "autocomplete",
43 name: name,
44 message: message,
45 prefix: logger.prefix,
46 multiple: !0,
47 choices: choices,
48 format: format,
49 limit: limit,
50 onCancel: cancelFlow
51 }).then((responses => responses[name])).catch((err => {
52 logger.error(err);
53 }));
54}
55
56async function askQuestion(message) {
57 const name = "Question-" + serialId();
58 return enquirer.prompt([ {
59 type: "input",
60 message: message,
61 name: name,
62 prefix: logger.prefix,
63 onCancel: cancelFlow
64 } ]).then((responses => responses[name])).catch((err => {
65 logger.error(err);
66 }));
67}
68
69function askQuestionWithEditor(message) {
70 return externalEditor.edit(message, {
71 postfix: ".md"
72 }).replace(/^#.*\n?/gm, "").replace(/\n+$/g, "").trim();
73}
74
75async function askConfirm(message) {
76 const name = "Confirm-" + serialId();
77 return enquirer.prompt([ {
78 message: message,
79 name: name,
80 prefix: logger.prefix,
81 type: "confirm",
82 initial: !0,
83 onCancel: cancelFlow
84 } ]).then((responses => responses[name])).catch((err => {
85 logger.error(err);
86 }));
87}
88
89async function askList(message, choices) {
90 const name = "List-" + serialId();
91 return enquirer.prompt([ {
92 choices: choices,
93 message: message,
94 name: name,
95 prefix: logger.prefix,
96 type: "select",
97 onCancel: cancelFlow
98 } ]).then((responses => responses[name])).catch((err => {
99 logger.error(err);
100 }));
101}
102
103const {green: green, yellow: yellow, red: red, bold: bold, blue: blue, cyan: cyan} = chalk__default.default;
104
105async function confirmMajorRelease(pkgJSON) {
106 if (semver__default.default.lt(pkgJSON.version, "1.0.0")) {
107 return logger.log(yellow(`WARNING: Releasing a major version for ${green(pkgJSON.name)} will be its ${red("first major release")}.`)),
108 logger.log(yellow(`If you are unsure if this is correct, contact the package's maintainers ${red("before committing this changeset")}.`)),
109 await askConfirm(bold(`Are you sure you want still want to release the ${red("first major release")} of ${pkgJSON.name}?`));
110 }
111 return !0;
112}
113
114async function getPackagesToRelease(changedPackages, allPackages) {
115 function askInitialReleaseQuestion(defaultChoiceList) {
116 return askCheckboxPlus("Which packages would you like to include?", defaultChoiceList, (x => Array.isArray(x) ? x.filter((x => "changed packages" !== x && "unchanged packages" !== x)).map((x => cyan(x))).join(", ") : x));
117 }
118 if (allPackages.length > 1) {
119 const unchangedPackagesNames = allPackages.map((({packageJson: packageJson}) => packageJson.name)).filter((name => !changedPackages.includes(name))), defaultChoiceList = [ {
120 name: "changed packages",
121 choices: changedPackages
122 }, {
123 name: "unchanged packages",
124 choices: unchangedPackagesNames
125 } ].filter((({choices: choices}) => 0 !== choices.length));
126 let packagesToRelease = await askInitialReleaseQuestion(defaultChoiceList);
127 if (0 === packagesToRelease.length) do {
128 logger.error("You must select at least one package to release"), logger.error("(You most likely hit enter instead of space!)"),
129 packagesToRelease = await askInitialReleaseQuestion(defaultChoiceList);
130 } while (0 === packagesToRelease.length);
131 return packagesToRelease.filter((pkgName => "changed packages" !== pkgName && "unchanged packages" !== pkgName));
132 }
133 return [ allPackages[0].packageJson.name ];
134}
135
136function formatPkgNameAndVersion(pkgName, version) {
137 return `${bold(pkgName)}@${bold(version)}`;
138}
139
140async function createChangeset(changedPackages, allPackages) {
141 const releases = [];
142 if (allPackages.length > 1) {
143 const packagesToRelease = await getPackagesToRelease(changedPackages, allPackages);
144 let pkgJsonsByName = new Map(allPackages.map((({packageJson: packageJson}) => [ packageJson.name, packageJson ]))), pkgsLeftToGetBumpTypeFor = new Set(packagesToRelease), pkgsThatShouldBeMajorBumped = (await askCheckboxPlus(bold(`Which packages should have a ${red("major")} bump?`), [ {
145 name: "all packages",
146 choices: packagesToRelease.map((pkgName => ({
147 name: pkgName,
148 message: formatPkgNameAndVersion(pkgName, pkgJsonsByName.get(pkgName).version)
149 })))
150 } ], (x => Array.isArray(x) ? x.filter((x => "all packages" !== x)).map((x => cyan(x))).join(", ") : x))).filter((x => "all packages" !== x));
151 for (const pkgName of pkgsThatShouldBeMajorBumped) {
152 let pkgJson = pkgJsonsByName.get(pkgName);
153 await confirmMajorRelease(pkgJson) && (pkgsLeftToGetBumpTypeFor.delete(pkgName),
154 releases.push({
155 name: pkgName,
156 type: "major"
157 }));
158 }
159 if (0 !== pkgsLeftToGetBumpTypeFor.size) {
160 let pkgsThatShouldBeMinorBumped = (await askCheckboxPlus(bold(`Which packages should have a ${green("minor")} bump?`), [ {
161 name: "all packages",
162 choices: [ ...pkgsLeftToGetBumpTypeFor ].map((pkgName => ({
163 name: pkgName,
164 message: formatPkgNameAndVersion(pkgName, pkgJsonsByName.get(pkgName).version)
165 })))
166 } ], (x => Array.isArray(x) ? x.filter((x => "all packages" !== x)).map((x => cyan(x))).join(", ") : x))).filter((x => "all packages" !== x));
167 for (const pkgName of pkgsThatShouldBeMinorBumped) pkgsLeftToGetBumpTypeFor.delete(pkgName),
168 releases.push({
169 name: pkgName,
170 type: "minor"
171 });
172 }
173 if (0 !== pkgsLeftToGetBumpTypeFor.size) {
174 logger.log(`The following packages will be ${blue("patch")} bumped:`), pkgsLeftToGetBumpTypeFor.forEach((pkgName => {
175 logger.log(formatPkgNameAndVersion(pkgName, pkgJsonsByName.get(pkgName).version));
176 }));
177 for (const pkgName of pkgsLeftToGetBumpTypeFor) releases.push({
178 name: pkgName,
179 type: "patch"
180 });
181 }
182 } else {
183 let pkg = allPackages[0], type = await askList(`What kind of change is this for ${green(pkg.packageJson.name)}? (current version is ${pkg.packageJson.version})`, [ "patch", "minor", "major" ]);
184 if (console.log(type), "major" === type) {
185 if (!await confirmMajorRelease(pkg.packageJson)) throw new errors.ExitError(1);
186 }
187 releases.push({
188 name: pkg.packageJson.name,
189 type: type
190 });
191 }
192 logger.log("Please enter a summary for this change (this will be in the changelogs). Submit empty line to open external editor");
193 let summary = await askQuestion("Summary");
194 0 === summary.length && (summary = askQuestionWithEditor(""));
195 try {
196 for (;0 === summary.length; ) summary = await askQuestionWithEditor("\n\n# A summary is required for the changelog! 😪");
197 } catch (err) {
198 for (logger.log("An error happened using external editor. Please type your summary here:"),
199 summary = await askQuestion(""); 0 === summary.length; ) summary = await askQuestion("\n\n# A summary is required for the changelog! 😪");
200 }
201 return {
202 summary: summary,
203 releases: releases
204 };
205}
206
207function printConfirmationMessage(changeset, repoHasMultiplePackages) {
208 function getReleasesOfType(type) {
209 return changeset.releases.filter((release => release.type === type)).map((release => release.name));
210 }
211 logger.log("=== Releasing the following packages ===");
212 const majorReleases = getReleasesOfType("major"), minorReleases = getReleasesOfType("minor"), patchReleases = getReleasesOfType("patch");
213 if (majorReleases.length > 0 && logger.log(`${chalk__default.default.green("[Major]")}\n ${majorReleases.join(", ")}`),
214 minorReleases.length > 0 && logger.log(`${chalk__default.default.green("[Minor]")}\n ${minorReleases.join(", ")}`),
215 patchReleases.length > 0 && logger.log(`${chalk__default.default.green("[Patch]")}\n ${patchReleases.join(", ")}`),
216 repoHasMultiplePackages) {
217 const message = outdent__default.default`
218 ${chalk__default.default.red("========= NOTE ========")}
219 All dependents of these packages that will be incompatible with the new version will be ${chalk__default.default.red("patch bumped")} when this changeset is applied.`, prettyMessage = boxen__default.default(message, {
220 borderStyle: "double",
221 align: "center"
222 });
223 logger.log(prettyMessage);
224 }
225}
226
227async function add(cwd, {empty: empty}, config) {
228 const packages = await getPackages.getPackages(cwd), changesetBase = path__default.default.resolve(cwd, ".changeset");
229 let newChangeset, confirmChangeset;
230 if (empty) newChangeset = {
231 releases: [],
232 summary: ""
233 }, confirmChangeset = !0; else {
234 const changePackagesName = (await git.getChangedPackagesSinceRef({
235 cwd: cwd,
236 ref: config.baseBranch
237 })).filter((a => a)).map((pkg => pkg.packageJson.name));
238 newChangeset = await createChangeset(changePackagesName, packages.packages), printConfirmationMessage(newChangeset, packages.packages.length > 1),
239 confirmChangeset = await askConfirm("Is this your desired changeset?");
240 }
241 if (confirmChangeset) {
242 const changesetID = await writeChangeset__default.default(newChangeset, cwd);
243 config.commit ? (await git.add(path__default.default.resolve(changesetBase, changesetID + ".md"), cwd),
244 await git.commit("docs(changeset): " + newChangeset.summary, cwd), logger.log(chalk__default.default.green((empty ? "Empty " : "") + "Changeset added and committed"))) : logger.log(chalk__default.default.green((empty ? "Empty " : "") + "Changeset added! - you can now commit it\n")),
245 [ ...newChangeset.releases ].find((c => "major" === c.type)) ? (logger.warn("This Changeset includes a major change and we STRONGLY recommend adding more information to the changeset:"),
246 logger.warn("WHAT the breaking change is"), logger.warn("WHY the change was made"),
247 logger.warn("HOW a consumer should update their code")) : logger.log(chalk__default.default.green("If you want to modify or expand on the changeset summary, you can find it here")),
248 logger.info(chalk__default.default.blue(path__default.default.resolve(changesetBase, changesetID + ".md")));
249 }
250}
251
252function _defineProperty(obj, key, value) {
253 return key in obj ? Object.defineProperty(obj, key, {
254 value: value,
255 enumerable: !0,
256 configurable: !0,
257 writable: !0
258 }) : obj[key] = value, obj;
259}
260
261function ownKeys(object, enumerableOnly) {
262 var keys = Object.keys(object);
263 if (Object.getOwnPropertySymbols) {
264 var symbols = Object.getOwnPropertySymbols(object);
265 enumerableOnly && (symbols = symbols.filter((function(sym) {
266 return Object.getOwnPropertyDescriptor(object, sym).enumerable;
267 }))), keys.push.apply(keys, symbols);
268 }
269 return keys;
270}
271
272function _objectSpread2(target) {
273 for (var i = 1; i < arguments.length; i++) {
274 var source = null != arguments[i] ? arguments[i] : {};
275 i % 2 ? ownKeys(Object(source), !0).forEach((function(key) {
276 _defineProperty(target, key, source[key]);
277 })) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach((function(key) {
278 Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
279 }));
280 }
281 return target;
282}
283
284const removeEmptyFolders = async folderPath => {
285 const dirContents = fs__default.default.readdirSync(folderPath);
286 return Promise.all(dirContents.map((async contentPath => {
287 const singleChangesetPath = path__default.default.resolve(folderPath, contentPath);
288 try {
289 (await fs__default.default.readdir(singleChangesetPath)).length < 1 && await fs__default.default.rmdir(singleChangesetPath);
290 } catch (err) {
291 if ("ENOTDIR" !== err.code) throw err;
292 }
293 })));
294};
295
296let importantSeparator = chalk__default.default.red("===============================IMPORTANT!==============================="), importantEnd = chalk__default.default.red("----------------------------------------------------------------------");
297
298async function version(cwd, options, config) {
299 const releaseConfig = _objectSpread2(_objectSpread2({}, config), {}, {
300 commit: !options.snapshot && config.commit
301 }), [changesets, preState] = await Promise.all([ readChangesets__default.default(cwd), pre$1.readPreState(cwd), removeEmptyFolders(path__default.default.resolve(cwd, ".changeset")) ]);
302 if ("pre" === (null == preState ? void 0 : preState.mode)) {
303 if (logger.warn(importantSeparator), void 0 !== options.snapshot) throw logger.error("Snapshot release is not allowed in pre mode"),
304 logger.log("To resolve this exit the pre mode by running `changeset pre exit`"),
305 new errors.ExitError(1);
306 logger.warn("You are in prerelease mode"), logger.warn("If you meant to do a normal release you should revert these changes and run `changeset pre exit`"),
307 logger.warn("You can then run `changeset version` again to do a normal release"),
308 logger.warn(importantEnd);
309 }
310 if (0 === changesets.length && (void 0 === preState || "exit" !== preState.mode)) return void logger.warn("No unreleased changesets found, exiting.");
311 let packages = await getPackages.getPackages(cwd), releasePlan = assembleReleasePlan__default.default(changesets, packages, releaseConfig, preState, options.snapshot);
312 await applyReleasePlan__default.default(releasePlan, packages, releaseConfig, options.snapshot),
313 releaseConfig.commit ? logger.log("All files have been updated and committed. You're ready to publish!") : logger.log("All files have been updated. Review them and commit at your leisure");
314}
315
316var isCI = !(!isCI__default.default && !process.env.GITHUB_ACTIONS);
317
318const npmRequestLimit = pLimit__default.default(40);
319
320function jsonParse(input) {
321 try {
322 return JSON.parse(input);
323 } catch (err) {
324 throw err instanceof SyntaxError && console.error("error parsing json:", input),
325 err;
326 }
327}
328
329function getCorrectRegistry() {
330 return "https://registry.yarnpkg.com" === process.env.npm_config_registry ? void 0 : process.env.npm_config_registry;
331}
332
333async function getPublishTool(cwd) {
334 const pm = await preferredPM__default.default(cwd);
335 if (!pm || "pnpm" !== pm.name) return {
336 name: "npm"
337 };
338 try {
339 let version = (await spawn__default.default("pnpm", [ "--version" ], {
340 cwd: cwd
341 })).stdout.toString().trim(), parsed = semver__default.default.parse(version);
342 return {
343 name: "pnpm",
344 shouldAddNoGitChecks: void 0 !== (null == parsed ? void 0 : parsed.major) && parsed.major >= 5
345 };
346 } catch (e) {
347 return {
348 name: "pnpm",
349 shouldAddNoGitChecks: !1
350 };
351 }
352}
353
354async function getTokenIsRequired() {
355 const envOverride = {
356 npm_config_registry: getCorrectRegistry()
357 };
358 let json = jsonParse((await spawn__default.default("npm", [ "profile", "get", "--json" ], {
359 env: Object.assign({}, process.env, envOverride)
360 })).stdout.toString());
361 return !(json.error || !json.tfa || !json.tfa.mode) && "auth-and-writes" === json.tfa.mode;
362}
363
364function getPackageInfo(pkgName) {
365 return npmRequestLimit((async () => {
366 logger.info("npm info " + pkgName);
367 const envOverride = {
368 npm_config_registry: getCorrectRegistry()
369 };
370 let result = await spawn__default.default("npm", [ "info", pkgName, "--json" ], {
371 env: Object.assign({}, process.env, envOverride)
372 });
373 return "" === result.stdout.toString() ? {
374 error: {
375 code: "E404"
376 }
377 } : jsonParse(result.stdout.toString());
378 }));
379}
380
381async function infoAllow404(pkgName) {
382 let pkgInfo = await getPackageInfo(pkgName);
383 if (pkgInfo.error && "E404" === pkgInfo.error.code) return logger.warn("Received 404 for npm info " + chalk__default.default.cyan(`"${pkgName}"`)),
384 {
385 published: !1,
386 pkgInfo: {}
387 };
388 if (pkgInfo.error) throw logger.error(`Received an unknown error code: ${pkgInfo.error.code} for npm info ${chalk__default.default.cyan(`"${pkgName}"`)}`),
389 logger.error(pkgInfo.error.summary), pkgInfo.error.detail && logger.error(pkgInfo.error.detail),
390 new errors.ExitError(1);
391 return {
392 published: !0,
393 pkgInfo: pkgInfo
394 };
395}
396
397let otpAskLimit = pLimit__default.default(1), askForOtpCode = twoFactorState => otpAskLimit((async () => {
398 if (null !== twoFactorState.token) return twoFactorState.token;
399 logger.info("This operation requires a one-time password from your authenticator.");
400 let val = await askQuestion("Enter one-time password:");
401 return twoFactorState.token = val, val;
402})), getOtpCode = async twoFactorState => null !== twoFactorState.token ? twoFactorState.token : askForOtpCode(twoFactorState);
403
404async function internalPublish(pkgName, opts, twoFactorState) {
405 let publishTool = await getPublishTool(opts.cwd), publishFlags = opts.access ? [ "--access", opts.access ] : [];
406 if (publishFlags.push("--tag", opts.tag), await twoFactorState.isRequired && !isCI) {
407 let otpCode = await getOtpCode(twoFactorState);
408 publishFlags.push("--otp", otpCode);
409 }
410 "pnpm" === publishTool.name && publishTool.shouldAddNoGitChecks && publishFlags.push("--no-git-checks");
411 const envOverride = {
412 npm_config_registry: getCorrectRegistry()
413 };
414 let {stdout: stdout} = await spawn__default.default(publishTool.name, [ "publish", opts.cwd, "--json", ...publishFlags ], {
415 env: Object.assign({}, process.env, envOverride)
416 }), json = jsonParse(stdout.toString().replace(/[^{]*/, ""));
417 return json.error ? ("EOTP" === json.error.code || "E401" === json.error.code && json.error.detail.includes("--otp=<code>")) && !isCI ? (null !== twoFactorState.token && (twoFactorState.token = null),
418 twoFactorState.isRequired = Promise.resolve(!0), internalPublish(pkgName, opts, twoFactorState)) : (logger.error(`an error occurred while publishing ${pkgName}: ${json.error.code}`, json.error.summary, json.error.detail ? "\n" + json.error.detail : ""),
419 {
420 published: !1
421 }) : {
422 published: !0
423 };
424}
425
426function publish(pkgName, opts, twoFactorState) {
427 return npmRequestLimit((() => internalPublish(pkgName, opts, twoFactorState)));
428}
429
430function getReleaseTag(pkgInfo, preState, tag) {
431 return tag || (void 0 !== preState && "only-pre" !== pkgInfo.publishedState ? preState.tag : "latest");
432}
433
434async function publishPackages({packages: packages, access: access, otp: otp, preState: preState, tag: tag}) {
435 const packagesByName = new Map(packages.map((x => [ x.packageJson.name, x ]))), publicPackages = packages.filter((pkg => !pkg.packageJson.private));
436 let twoFactorState = void 0 === otp ? {
437 token: null,
438 isRequired: isCI || publicPackages.some((x => x.packageJson.publishConfig && x.packageJson.publishConfig.registry && "https://registry.npmjs.org" !== x.packageJson.publishConfig.registry && "https://registry.yarnpkg.com" !== x.packageJson.publishConfig.registry)) || void 0 !== process.env.npm_config_registry && "https://registry.npmjs.org" !== process.env.npm_config_registry && "https://registry.yarnpkg.com" !== process.env.npm_config_registry ? Promise.resolve(!1) : getTokenIsRequired()
439 } : {
440 token: otp,
441 isRequired: Promise.resolve(!0)
442 };
443 const unpublishedPackagesInfo = await getUnpublishedPackages(publicPackages, preState);
444 0 === unpublishedPackagesInfo.length && logger.warn("No unpublished packages to publish");
445 let promises = [];
446 for (let pkgInfo of unpublishedPackagesInfo) {
447 let pkg = packagesByName.get(pkgInfo.name);
448 promises.push(publishAPackage(pkg, access, twoFactorState, getReleaseTag(pkgInfo, preState, tag)));
449 }
450 return Promise.all(promises);
451}
452
453async function publishAPackage(pkg, access, twoFactorState, tag) {
454 const {name: name, version: version, publishConfig: publishConfig} = pkg.packageJson, localAccess = publishConfig && publishConfig.access;
455 logger.info(`Publishing ${chalk__default.default.cyan(`"${name}"`)} at ${chalk__default.default.green(`"${version}"`)}`);
456 const publishDir = publishConfig && publishConfig.directory ? path.join(pkg.dir, publishConfig.directory) : pkg.dir;
457 return {
458 name: name,
459 newVersion: version,
460 published: (await publish(name, {
461 cwd: publishDir,
462 access: localAccess || access,
463 tag: tag
464 }, twoFactorState)).published
465 };
466}
467
468async function getUnpublishedPackages(packages, preState) {
469 const results = await Promise.all(packages.map((async pkg => {
470 const config = pkg.packageJson, response = await infoAllow404(config.name);
471 let publishedState = "never";
472 return response.published && (publishedState = "published", void 0 !== preState && response.pkgInfo.versions && response.pkgInfo.versions.every((version => semver__default.default.parse(version).prerelease[0] === preState.tag)) && (publishedState = "only-pre")),
473 {
474 name: config.name,
475 localVersion: config.version,
476 publishedState: publishedState,
477 publishedVersions: response.pkgInfo.versions || []
478 };
479 }))), packagesToPublish = [];
480 for (const pkgInfo of results) {
481 const {name: name, publishedState: publishedState, localVersion: localVersion, publishedVersions: publishedVersions} = pkgInfo;
482 publishedVersions.includes(localVersion) ? logger.warn(`${name} is not being published because version ${localVersion} is already published on npm`) : (packagesToPublish.push(pkgInfo),
483 logger.info(`${name} is being published because our local version (${localVersion}) has not been published on npm`),
484 void 0 !== preState && "only-pre" === publishedState && logger.info(`${name} is being published to ${chalk__default.default.cyan("latest")} rather than ${chalk__default.default.cyan(preState.tag)} because there has not been a regular release of it yet`));
485 }
486 return packagesToPublish;
487}
488
489function logReleases(pkgs) {
490 const mappedPkgs = pkgs.map((p => `${p.name}@${p.newVersion}`)).join("\n");
491 logger.log(mappedPkgs);
492}
493
494let importantSeparator$1 = chalk__default.default.red("===============================IMPORTANT!==============================="), importantEnd$1 = chalk__default.default.red("----------------------------------------------------------------------");
495
496function showNonLatestTagWarning(tag, preState) {
497 logger.warn(importantSeparator$1), preState ? logger.warn(`You are in prerelease mode so packages will be published to the ${chalk__default.default.cyan(preState.tag)}\n dist tag except for packages that have not had normal releases which will be published to ${chalk__default.default.cyan("latest")}`) : "latest" !== tag && logger.warn(`Packages will be released under the ${tag} tag`),
498 logger.warn(importantEnd$1);
499}
500
501async function run(cwd, {otp: otp, tag: tag}, config) {
502 const releaseTag = tag && tag.length > 0 ? tag : void 0;
503 let preState = await pre$1.readPreState(cwd);
504 if (releaseTag && preState && "pre" === preState.mode) throw logger.error("Releasing under custom tag is not allowed in pre mode"),
505 logger.log("To resolve this exit the pre mode by running `changeset pre exit`"),
506 new errors.ExitError(1);
507 (releaseTag || preState) && showNonLatestTagWarning(tag, preState);
508 const {packages: packages, tool: tool} = await getPackages.getPackages(cwd), response = await publishPackages({
509 packages: packages,
510 access: config.access,
511 otp: otp,
512 preState: preState,
513 tag: releaseTag
514 }), successful = response.filter((p => p.published)), unsuccessful = response.filter((p => !p.published));
515 if (successful.length > 0) if (logger.success("packages published successfully:"),
516 logReleases(successful), logger.log(`Creating git tag${successful.length > 1 ? "s" : ""}...`),
517 "root" !== tool) for (const pkg of successful) {
518 const tag = `${pkg.name}@${pkg.newVersion}`;
519 logger.log("New tag: ", tag), await git.tag(tag, cwd);
520 } else {
521 const tag = "v" + successful[0].newVersion;
522 logger.log("New tag: ", tag), await git.tag(tag, cwd);
523 }
524 if (unsuccessful.length > 0) throw logger.error("packages failed to publish:"),
525 logReleases(unsuccessful), new errors.ExitError(1);
526}
527
528async function getStatus(cwd, {sinceMaster: sinceMaster, since: since, verbose: verbose, output: output}, config) {
529 sinceMaster && (logger.warn("--sinceMaster is deprecated and will be removed in a future major version"),
530 logger.warn("Use --since=master instead"));
531 const releasePlan = await getReleasePlan__default.default(cwd, void 0 === since ? sinceMaster ? "master" : void 0 : since, config), {changesets: changesets, releases: releases} = releasePlan;
532 if (changesets.length < 1 && (logger.error("No changesets present"), process.exit(1)),
533 output) return void await fs__default.default.writeFile(path__default.default.join(cwd, output), JSON.stringify(releasePlan, void 0, 2));
534 const print = verbose ? verbosePrint : SimplePrint;
535 return print("patch", releases), logger.log("---"), print("minor", releases), logger.log("---"),
536 print("major", releases), releasePlan;
537}
538
539function SimplePrint(type, releases) {
540 const packages = releases.filter((r => r.type === type));
541 if (packages.length) {
542 logger.info(chalk__default.default`Packages to be bumped at {green ${type}}:\n`);
543 const pkgs = packages.map((({name: name}) => "- " + name)).join("\n");
544 logger.log(chalk__default.default.green(pkgs));
545 } else logger.info(chalk__default.default`{red NO} packages to be bumped at {green ${type}}`);
546}
547
548function verbosePrint(type, releases) {
549 const packages = releases.filter((r => r.type === type));
550 if (packages.length) {
551 logger.info(chalk__default.default`Packages to be bumped at {green ${type}}`);
552 const columns = packages.map((({name: name, newVersion: version, changesets: changesets}) => [ chalk__default.default.green(name), version, changesets.map((c => chalk__default.default.blue(` .changeset/${c}/changes.md`))).join(" +") ])), t1 = table__default.default([ {
553 value: "Package Name",
554 width: 20
555 }, {
556 value: "New Version",
557 width: 20
558 }, {
559 value: "Related Changeset Summaries",
560 width: 70
561 } ], columns, {
562 paddingLeft: 1,
563 paddingRight: 0,
564 headerAlign: "center",
565 align: "left"
566 });
567 logger.log(t1.render() + "\n");
568 } else logger.info(chalk__default.default`Running release would release {red NO} packages as a {green ${type}}`);
569}
570
571async function pre(cwd, options) {
572 if ("enter" === options.command) try {
573 await pre$1.enterPre(cwd, options.tag), logger.success("Entered pre mode with tag " + chalk__default.default.cyan(options.tag)),
574 logger.info("Run `changeset version` to version packages with prerelease versions");
575 } catch (err) {
576 if (err instanceof errors.PreEnterButInPreModeError) throw logger.error("`changeset pre enter` cannot be run when in pre mode"),
577 logger.info("If you're trying to exit pre mode, run `changeset pre exit`"), new errors.ExitError(1);
578 throw err;
579 } else try {
580 await pre$1.exitPre(cwd), logger.success("Exited pre mode"), logger.info("Run `changeset version` to version packages with normal versions");
581 } catch (err) {
582 if (err instanceof errors.PreExitButNotInPreModeError) throw logger.error("`changeset pre exit` can only be run when in pre mode"),
583 logger.info("If you're trying to enter pre mode, run `changeset pre enter`"), new errors.ExitError(1);
584 throw err;
585 }
586}
587
588async function run$1(input, flags, cwd) {
589 if ("init" === input[0]) return void await init(cwd);
590 if (!fs__default.default.existsSync(path__default.default.resolve(cwd, ".changeset"))) throw logger.error("There is no .changeset folder. "),
591 logger.error("If this is the first time `changesets` have been used in this project, run `yarn changeset init` to get set up."),
592 logger.error("If you expected there to be changesets, you should check git history for when the folder was removed to ensure you do not lose any configuration."),
593 new errors.ExitError(1);
594 const packages = await getPackages.getPackages(cwd);
595 let config$1;
596 try {
597 config$1 = await config.read(cwd, packages);
598 } catch (e) {
599 throw await fs__default.default.pathExists(path__default.default.resolve(cwd, ".changeset/config.js")) ? (logger.error("It looks like you're using the version 1 `.changeset/config.js` file"),
600 logger.error("You'll need to convert it to a `.changeset/config.json` file"), logger.error("The format of the config object has significantly changed in v2 as well"),
601 logger.error(" - we thoroughly recommend looking at the changelog for this package for what has changed"),
602 new errors.ExitError(1)) : e;
603 }
604 if (input.length < 1) {
605 const {empty: empty} = flags;
606 await add(cwd, {
607 empty: empty
608 }, config$1);
609 } else if ("pre" !== input[0] && input.length > 1) logger.error("Too many arguments passed to changesets - we only accept the command name as an argument"); else {
610 const {sinceMaster: sinceMaster, since: since, verbose: verbose, output: output, otp: otp, empty: empty, ignore: ignore, snapshot: snapshot, tag: tag} = flags;
611 switch ([ "updateChangelog", "isPublic", "skipCI", "commit" ].forEach((flag => {
612 if (flags[flag]) throw logger.error(`the flag ${flag} has been removed from changesets for version 2`),
613 logger.error("Please encode the desired value into your config"), logger.error("See our changelog for more details"),
614 new errors.ExitError(1);
615 })), input[0]) {
616 case "add":
617 return void await add(cwd, {
618 empty: empty
619 }, config$1);
620
621 case "version":
622 {
623 let ignoreArrayFromCmd;
624 ignoreArrayFromCmd = "string" == typeof ignore ? [ ignore ] : ignore;
625 let pkgNames = new Set(packages.packages.map((({packageJson: packageJson}) => packageJson.name)));
626 const messages = [];
627 for (const pkgName of ignoreArrayFromCmd || []) pkgNames.has(pkgName) || messages.push(`The package "${pkgName}" is passed to the \`--ignore\` option but it is not found in the project. You may have misspelled the package name.`);
628 config$1.ignore.length > 0 && ignoreArrayFromCmd ? messages.push("It looks like you are trying to use the `--ignore` option while ignore is defined in the config file. This is currently not allowed, you can only use one of them at a time.") : ignoreArrayFromCmd && (config$1.ignore = ignoreArrayFromCmd);
629 const dependentsGraph = getDependentsGraph.getDependentsGraph(packages);
630 for (const ignoredPackage of config$1.ignore) {
631 const dependents = dependentsGraph.get(ignoredPackage) || [];
632 for (const dependent of dependents) config$1.ignore.includes(dependent) || messages.push(`The package "${dependent}" depends on the ignored package "${ignoredPackage}", but "${dependent}" is not being ignored. Please pass "${dependent}" to the \`--ignore\` flag.`);
633 }
634 if (messages.length > 0) throw logger.error(messages.join("\n")), new errors.ExitError(1);
635 return void await version(cwd, {
636 snapshot: snapshot
637 }, config$1);
638 }
639
640 case "publish":
641 return void await run(cwd, {
642 otp: otp,
643 tag: tag
644 }, config$1);
645
646 case "status":
647 return void await getStatus(cwd, {
648 sinceMaster: sinceMaster,
649 since: since,
650 verbose: verbose,
651 output: output
652 }, config$1);
653
654 case "pre":
655 {
656 let command = input[1];
657 if ("enter" !== command && "exit" !== command) throw logger.error("`enter`, `exit` or `snapshot` must be passed after prerelease"),
658 new errors.ExitError(1);
659 let tag = input[2];
660 if ("enter" === command && "string" != typeof tag) throw logger.error("A tag must be passed when using prerelese enter"),
661 new errors.ExitError(1);
662 return void await pre(cwd, {
663 command: command,
664 tag: tag
665 });
666 }
667
668 case "bump":
669 throw logger.error('In version 2 of changesets, "bump" has been renamed to "version" - see our changelog for an explanation'),
670 logger.error("To fix this, use `changeset version` instead, and update any scripts that use changesets"),
671 new errors.ExitError(1);
672
673 case "release":
674 throw logger.error('In version 2 of changesets, "release" has been renamed to "publish" - see our changelog for an explanation'),
675 logger.error("To fix this, use `changeset publish` instead, and update any scripts that use changesets"),
676 new errors.ExitError(1);
677
678 default:
679 throw logger.error(`Invalid command ${input[0]} was provided`), new errors.ExitError(1);
680 }
681 }
682}
683
684const {input: input, flags: flags} = meow__default.default("\n Usage\n $ changesets [command]\n Commands\n init\n add [--empty]\n version [--ignore]\n publish [--otp=code]\n status [--since-master --verbose --output=JSON_FILE.json]\n pre <enter|exit> <tag>\n ", {
685 flags: {
686 sinceMaster: {
687 type: "boolean"
688 },
689 verbose: {
690 type: "boolean",
691 alias: "v"
692 },
693 output: {
694 type: "string",
695 alias: "o"
696 },
697 otp: {
698 type: "string"
699 },
700 empty: {
701 type: "boolean"
702 },
703 since: {
704 type: "string"
705 },
706 ignore: {
707 type: "string",
708 isMultiple: !0
709 },
710 tag: {
711 type: "string"
712 }
713 }
714}), cwd = process.cwd();
715
716run$1(input, flags, cwd).catch((err => {
717 if (err instanceof errors.InternalError && (logger.error("The following error is an internal unexpected error, these should never happen."),
718 logger.error("Please open an issue with the following link"), logger.error(`https://github.com/atlassian/changesets/issues/new?title=${encodeURIComponent(`Unexpected error during ${input[0] || "add"} command`)}&body=${encodeURIComponent(`## Error\n\n\`\`\`\n${util.format("", err).replace(process.cwd(), "<cwd>")}\n\`\`\`\n\n## Versions\n\n- @changesets/cli@${require("@changesets/cli/package.json").version}\n- node@${process.version}\n\n## Extra details\n\n\x3c!-- Add any extra details of what you were doing, ideas you have about what might have caused the error and reproduction steps if possible. If you have a repository we can look at that would be great. 😁 --\x3e\n`)}`)),
719 err instanceof errors.ExitError) return process.exit(err.code);
720 logger.error(err), process.exit(1);
721}));