UNPKG

52.2 kBJavaScriptView Raw
1import mri from 'mri';
2import { ExitError, PreEnterButInPreModeError, PreExitButNotInPreModeError, InternalError } from '@changesets/errors';
3import * as logger from '@changesets/logger';
4import { prefix, error, success, log, warn, info } from '@changesets/logger';
5import { format } from 'util';
6import { defaultWrittenConfig, read } from '@changesets/config';
7import { getDependentsGraph } from '@changesets/get-dependents-graph';
8import { shouldSkipPackage } from '@changesets/should-skip-package';
9import { getPackages } from '@manypkg/get-packages';
10import fs from 'fs-extra';
11import path, { join } from 'path';
12import pc from 'picocolors';
13import { spawn } from 'child_process';
14import * as git from '@changesets/git';
15import { getChangedPackagesSinceRef, getCurrentCommitId } from '@changesets/git';
16import writeChangeset from '@changesets/write';
17import { edit, ExternalEditor } from 'external-editor';
18import resolveFrom from 'resolve-from';
19import termSize from 'term-size';
20import { prompt } from 'enquirer';
21import { symbols } from 'ansi-colors';
22import semverLt from 'semver/functions/lt';
23import { enterPre, exitPre, readPreState } from '@changesets/pre';
24import semverParse from 'semver/functions/parse';
25import pLimit from 'p-limit';
26import { detect } from 'package-manager-detector';
27import spawn$1 from 'spawndamnit';
28import { isCI } from 'ci-info';
29import getReleasePlan from '@changesets/get-release-plan';
30import applyReleasePlan from '@changesets/apply-release-plan';
31import readChangesets from '@changesets/read';
32import assembleReleasePlan from '@changesets/assemble-release-plan';
33
34function _defineProperty(obj, key, value) {
35 if (key in obj) {
36 Object.defineProperty(obj, key, {
37 value: value,
38 enumerable: true,
39 configurable: true,
40 writable: true
41 });
42 } else {
43 obj[key] = value;
44 }
45
46 return obj;
47}
48
49function ownKeys(object, enumerableOnly) {
50 var keys = Object.keys(object);
51
52 if (Object.getOwnPropertySymbols) {
53 var symbols = Object.getOwnPropertySymbols(object);
54 if (enumerableOnly) symbols = symbols.filter(function (sym) {
55 return Object.getOwnPropertyDescriptor(object, sym).enumerable;
56 });
57 keys.push.apply(keys, symbols);
58 }
59
60 return keys;
61}
62
63function _objectSpread2(target) {
64 for (var i = 1; i < arguments.length; i++) {
65 var source = arguments[i] != null ? arguments[i] : {};
66
67 if (i % 2) {
68 ownKeys(Object(source), true).forEach(function (key) {
69 _defineProperty(target, key, source[key]);
70 });
71 } else if (Object.getOwnPropertyDescriptors) {
72 Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
73 } else {
74 ownKeys(Object(source)).forEach(function (key) {
75 Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
76 });
77 }
78 }
79
80 return target;
81}
82
83function getCommitFunctions(commit, cwd) {
84 let commitFunctions = {};
85
86 if (!commit) {
87 return [commitFunctions, null];
88 }
89
90 let commitOpts = commit[1];
91 let changesetPath = path.join(cwd, ".changeset");
92 let commitPath = resolveFrom(changesetPath, commit[0]);
93
94 let possibleCommitFunc = require(commitPath);
95
96 if (possibleCommitFunc.default) {
97 possibleCommitFunc = possibleCommitFunc.default;
98 }
99
100 if (typeof possibleCommitFunc.getAddMessage === "function" || typeof possibleCommitFunc.getVersionMessage === "function") {
101 commitFunctions = possibleCommitFunc;
102 } else {
103 throw new Error("Could not resolve commit generation functions");
104 }
105
106 return [commitFunctions, commitOpts];
107}
108
109// @ts-ignore it's not worth writing a TS declaration file in this repo for a tiny module we use once like this
110// so we can make type assertions using them because `enquirer` types do no support `prefix` right now
111
112/* Notes on using inquirer:
113 * Each question needs a key, as inquirer is assembling an object behind-the-scenes.
114 * At each call, the entire responses object is returned, so we need a unique
115 * identifier for the name every time. This is why we are using serial IDs
116 */
117const serialId = function () {
118 let id = 0;
119 return () => id++;
120}();
121
122const limit = Math.max(termSize().rows - 5, 10);
123
124let cancelFlow = () => {
125 success("Cancelled... 👋 ");
126 process.exit();
127};
128
129async function askCheckboxPlus(message, choices, format) {
130 const name = `CheckboxPlus-${serialId()}`;
131 return prompt({
132 type: "autocomplete",
133 name,
134 message,
135 prefix,
136 multiple: true,
137 choices,
138 format,
139 limit,
140 onCancel: cancelFlow,
141 symbols: {
142 indicator: symbols.radioOff,
143 checked: symbols.radioOn
144 },
145
146 indicator(state, choice) {
147 return choice.enabled ? state.symbols.checked : state.symbols.indicator;
148 }
149
150 }).then(responses => responses[name]).catch(err => {
151 error(err);
152 });
153}
154
155async function askQuestion(message) {
156 const name = `Question-${serialId()}`;
157 return prompt([{
158 type: "input",
159 message,
160 name,
161 prefix,
162 onCancel: cancelFlow
163 }]).then(responses => responses[name]).catch(err => {
164 error(err);
165 });
166}
167
168function askQuestionWithEditor(message) {
169 const response = edit(message, {
170 postfix: ".md"
171 });
172 return response.replace(/^#.*\n?/gm, "").replace(/\n+$/g, "").trim();
173}
174
175async function askConfirm(message) {
176 const name = `Confirm-${serialId()}`;
177 return prompt([{
178 message,
179 name,
180 prefix,
181 type: "confirm",
182 initial: true,
183 onCancel: cancelFlow
184 }]).then(responses => responses[name]).catch(err => {
185 error(err);
186 });
187}
188
189async function askList(message, choices) {
190 const name = `List-${serialId()}`;
191 return prompt([{
192 choices,
193 message,
194 name,
195 prefix,
196 type: "select",
197 onCancel: cancelFlow
198 }]).then(responses => responses[name]).catch(err => {
199 error(err);
200 });
201}
202
203async function getVersionableChangedPackages(config, {
204 cwd,
205 ref
206}) {
207 const changedPackages = await getChangedPackagesSinceRef({
208 ref: ref !== null && ref !== void 0 ? ref : config.baseBranch,
209 changedFilePatterns: config.changedFilePatterns,
210 cwd
211 });
212 return changedPackages.filter(pkg => !shouldSkipPackage(pkg, {
213 ignore: config.ignore,
214 allowPrivatePackages: config.privatePackages.version
215 }));
216}
217
218const {
219 green,
220 yellow,
221 red,
222 bold,
223 blue,
224 cyan,
225 gray
226} = pc;
227
228async function confirmMajorRelease(pkgJSON) {
229 if (semverLt(pkgJSON.version, "1.0.0")) {
230 // prettier-ignore
231 log(yellow(`WARNING: Releasing a major version for ${green(pkgJSON.name)} will be its ${red('first major release')}.`));
232 log(yellow(`If you are unsure if this is correct, contact the package's maintainers ${red("before committing this changeset")}.`));
233 let shouldReleaseFirstMajor = await askConfirm(bold(`Are you sure you want to release the ${red("first major version")} of ${pkgJSON.name}?`));
234 return shouldReleaseFirstMajor;
235 }
236
237 return true;
238}
239
240async function getPackagesToRelease(changedPackages, allPackages) {
241 function askInitialReleaseQuestion(defaultChoiceList) {
242 return askCheckboxPlus( // TODO: Make this wording better
243 // TODO: take objects and be fancy with matching
244 `Which packages would you like to include?`, defaultChoiceList, x => {
245 // this removes changed packages and unchanged packages from the list
246 // of packages shown after selection
247 if (Array.isArray(x)) {
248 return x.filter(x => x !== "changed packages" && x !== "unchanged packages").map(x => cyan(x)).join(", ");
249 }
250
251 return x;
252 });
253 }
254
255 if (allPackages.length > 1) {
256 const unchangedPackagesNames = allPackages.map(({
257 packageJson
258 }) => packageJson.name).filter(name => !changedPackages.includes(name));
259 const defaultChoiceList = [{
260 name: "changed packages",
261 choices: changedPackages
262 }, {
263 name: "unchanged packages",
264 choices: unchangedPackagesNames
265 }].filter(({
266 choices
267 }) => choices.length !== 0);
268 let packagesToRelease = await askInitialReleaseQuestion(defaultChoiceList);
269
270 if (packagesToRelease.length === 0) {
271 do {
272 error("You must select at least one package to release");
273 error("(You most likely hit enter instead of space!)");
274 packagesToRelease = await askInitialReleaseQuestion(defaultChoiceList);
275 } while (packagesToRelease.length === 0);
276 }
277
278 return packagesToRelease.filter(pkgName => pkgName !== "changed packages" && pkgName !== "unchanged packages");
279 }
280
281 return [allPackages[0].packageJson.name];
282}
283
284function getPkgJsonsByName(packages) {
285 return new Map(packages.map(({
286 packageJson
287 }) => [packageJson.name, packageJson]));
288}
289
290function formatPkgNameAndVersion(pkgName, version) {
291 return `${bold(pkgName)}@${bold(version)}`;
292}
293
294async function createChangeset(changedPackages, allPackages) {
295 const releases = [];
296
297 if (allPackages.length > 1) {
298 const packagesToRelease = await getPackagesToRelease(changedPackages, allPackages);
299 let pkgJsonsByName = getPkgJsonsByName(allPackages);
300 let pkgsLeftToGetBumpTypeFor = new Set(packagesToRelease);
301 let pkgsThatShouldBeMajorBumped = (await askCheckboxPlus(bold(`Which packages should have a ${red("major")} bump?`), [{
302 name: "all packages",
303 choices: packagesToRelease.map(pkgName => {
304 return {
305 name: pkgName,
306 message: formatPkgNameAndVersion(pkgName, pkgJsonsByName.get(pkgName).version)
307 };
308 })
309 }], x => {
310 // this removes changed packages and unchanged packages from the list
311 // of packages shown after selection
312 if (Array.isArray(x)) {
313 return x.filter(x => x !== "all packages").map(x => cyan(x)).join(", ");
314 }
315
316 return x;
317 })).filter(x => x !== "all packages");
318
319 for (const pkgName of pkgsThatShouldBeMajorBumped) {
320 // for packages that are under v1, we want to make sure major releases are intended,
321 // as some repo-wide sweeping changes have mistakenly release first majors
322 // of packages.
323 let pkgJson = pkgJsonsByName.get(pkgName);
324 let shouldReleaseFirstMajor = await confirmMajorRelease(pkgJson);
325
326 if (shouldReleaseFirstMajor) {
327 pkgsLeftToGetBumpTypeFor.delete(pkgName);
328 releases.push({
329 name: pkgName,
330 type: "major"
331 });
332 }
333 }
334
335 if (pkgsLeftToGetBumpTypeFor.size !== 0) {
336 let pkgsThatShouldBeMinorBumped = (await askCheckboxPlus(bold(`Which packages should have a ${green("minor")} bump?`), [{
337 name: "all packages",
338 choices: [...pkgsLeftToGetBumpTypeFor].map(pkgName => {
339 return {
340 name: pkgName,
341 message: formatPkgNameAndVersion(pkgName, pkgJsonsByName.get(pkgName).version)
342 };
343 })
344 }], x => {
345 // this removes changed packages and unchanged packages from the list
346 // of packages shown after selection
347 if (Array.isArray(x)) {
348 return x.filter(x => x !== "all packages").map(x => cyan(x)).join(", ");
349 }
350
351 return x;
352 })).filter(x => x !== "all packages");
353
354 for (const pkgName of pkgsThatShouldBeMinorBumped) {
355 pkgsLeftToGetBumpTypeFor.delete(pkgName);
356 releases.push({
357 name: pkgName,
358 type: "minor"
359 });
360 }
361 }
362
363 if (pkgsLeftToGetBumpTypeFor.size !== 0) {
364 log(`The following packages will be ${blue("patch")} bumped:`);
365 pkgsLeftToGetBumpTypeFor.forEach(pkgName => {
366 log(formatPkgNameAndVersion(pkgName, pkgJsonsByName.get(pkgName).version));
367 });
368
369 for (const pkgName of pkgsLeftToGetBumpTypeFor) {
370 releases.push({
371 name: pkgName,
372 type: "patch"
373 });
374 }
375 }
376 } else {
377 let pkg = allPackages[0];
378 let type = await askList(`What kind of change is this for ${green(pkg.packageJson.name)}? (current version is ${pkg.packageJson.version})`, ["patch", "minor", "major"]);
379
380 if (type === "major") {
381 let shouldReleaseAsMajor = await confirmMajorRelease(pkg.packageJson);
382
383 if (!shouldReleaseAsMajor) {
384 throw new ExitError(1);
385 }
386 }
387
388 releases.push({
389 name: pkg.packageJson.name,
390 type
391 });
392 }
393
394 log("Please enter a summary for this change (this will be in the changelogs).");
395 log(gray(" (submit empty line to open external editor)"));
396 let summary = await askQuestion("Summary");
397
398 if (summary.length === 0) {
399 try {
400 summary = askQuestionWithEditor("\n\n# Please enter a summary for your changes.\n# An empty message aborts the editor.");
401
402 if (summary.length > 0) {
403 return {
404 confirmed: true,
405 summary,
406 releases
407 };
408 }
409 } catch (err) {
410 log("An error happened using external editor. Please type your summary here:");
411 }
412
413 summary = await askQuestion("");
414
415 while (summary.length === 0) {
416 summary = await askQuestion("\n\n# A summary is required for the changelog! 😪");
417 }
418 }
419
420 return {
421 confirmed: false,
422 summary,
423 releases
424 };
425}
426
427function printConfirmationMessage(changeset, repoHasMultiplePackages) {
428 function getReleasesOfType(type) {
429 return changeset.releases.filter(release => release.type === type).map(release => release.name);
430 }
431
432 log("\n=== Summary of changesets ===");
433 const majorReleases = getReleasesOfType("major");
434 const minorReleases = getReleasesOfType("minor");
435 const patchReleases = getReleasesOfType("patch");
436 if (majorReleases.length > 0) log(`${pc.bold(pc.green("major"))}: ${majorReleases.join(", ")}`);
437 if (minorReleases.length > 0) log(`${pc.bold(pc.green("minor"))}: ${minorReleases.join(", ")}`);
438 if (patchReleases.length > 0) log(`${pc.bold(pc.green("patch"))}: ${patchReleases.join(", ")}`);
439 log("");
440
441 if (repoHasMultiplePackages) {
442 const message = "Note: All dependents of these packages that will be incompatible with the new version will be " + pc.redBright("patch bumped") + " when this changeset is applied.";
443 log(message + "\n");
444 }
445}
446
447async function add(cwd, {
448 empty,
449 open
450}, config) {
451 const packages = await getPackages(cwd);
452
453 if (packages.packages.length === 0) {
454 throw new Error(`No packages found. You might have ${packages.tool} workspaces configured but no packages yet?`);
455 }
456
457 const versionablePackages = packages.packages.filter(pkg => !shouldSkipPackage(pkg, {
458 ignore: config.ignore,
459 allowPrivatePackages: config.privatePackages.version
460 }));
461 const changesetBase = path.resolve(cwd, ".changeset");
462 let newChangeset;
463
464 if (empty) {
465 newChangeset = {
466 confirmed: true,
467 releases: [],
468 summary: ``
469 };
470 } else {
471 const changedPackagesNames = (await getVersionableChangedPackages(config, {
472 cwd
473 })).map(pkg => pkg.packageJson.name);
474 newChangeset = await createChangeset(changedPackagesNames, versionablePackages);
475 printConfirmationMessage(newChangeset, versionablePackages.length > 1);
476
477 if (!newChangeset.confirmed) {
478 newChangeset = _objectSpread2(_objectSpread2({}, newChangeset), {}, {
479 confirmed: await askConfirm("Is this your desired changeset?")
480 });
481 }
482 }
483
484 if (newChangeset.confirmed) {
485 const changesetID = await writeChangeset(newChangeset, cwd);
486 const [{
487 getAddMessage
488 }, commitOpts] = getCommitFunctions(config.commit, cwd);
489
490 if (getAddMessage) {
491 await git.add(path.resolve(changesetBase, `${changesetID}.md`), cwd);
492 await git.commit(await getAddMessage(newChangeset, commitOpts), cwd);
493 log(pc.green(`${empty ? "Empty " : ""}Changeset added and committed`));
494 } else {
495 log(pc.green(`${empty ? "Empty " : ""}Changeset added! - you can now commit it\n`));
496 }
497
498 let hasMajorChange = [...newChangeset.releases].find(c => c.type === "major");
499
500 if (hasMajorChange) {
501 warn("This Changeset includes a major change and we STRONGLY recommend adding more information to the changeset:");
502 warn("WHAT the breaking change is");
503 warn("WHY the change was made");
504 warn("HOW a consumer should update their code");
505 } else {
506 log(pc.green("If you want to modify or expand on the changeset summary, you can find it here"));
507 }
508
509 const changesetPath = path.resolve(changesetBase, `${changesetID}.md`);
510 info(pc.blue(changesetPath));
511
512 if (open) {
513 // this is really a hack to reuse the logic embedded in `external-editor` related to determining the editor
514 const externalEditor = new ExternalEditor();
515 externalEditor.cleanup();
516 spawn(externalEditor.editor.bin, externalEditor.editor.args.concat([changesetPath]), {
517 detached: true,
518 stdio: "inherit"
519 });
520 }
521 }
522}
523
524const pkgPath = path.dirname(require.resolve("@changesets/cli/package.json")); // Modify base branch to "main" without changing defaultWrittenConfig since it also serves as a fallback
525// for config files that don't specify a base branch. Changing that to main would be a breaking change.
526
527const defaultConfig = `${JSON.stringify(_objectSpread2(_objectSpread2({}, defaultWrittenConfig), {}, {
528 baseBranch: "main"
529}), null, 2)}\n`;
530async function init(cwd) {
531 const changesetBase = path.resolve(cwd, ".changeset");
532
533 if (fs.existsSync(changesetBase)) {
534 if (!fs.existsSync(path.join(changesetBase, "config.json"))) {
535 if (fs.existsSync(path.join(changesetBase, "config.js"))) {
536 error("It looks like you're using the version 1 `.changeset/config.js` file");
537 error("The format of the config object has significantly changed in v2 as well");
538 error(" - we thoroughly recommend looking at the changelog for this package for what has changed");
539 error("Changesets will write the defaults for the new config, remember to transfer your options into the new config at `.changeset/config.json`");
540 } else {
541 error("It looks like you don't have a config file");
542 info("The default config file will be written at `.changeset/config.json`");
543 }
544
545 await fs.writeFile(path.resolve(changesetBase, "config.json"), defaultConfig);
546 } else {
547 warn("It looks like you already have changesets initialized. You should be able to run changeset commands no problems.");
548 }
549 } else {
550 await fs.copy(path.resolve(pkgPath, "./default-files"), changesetBase);
551 await fs.writeFile(path.resolve(changesetBase, "config.json"), defaultConfig);
552 log(`Thanks for choosing ${pc.green("changesets")} to help manage your versioning and publishing\n`);
553 log("You should be set up to start using changesets now!\n");
554 info("We have added a `.changeset` folder, and a couple of files to help you out:");
555 info(`- ${pc.blue(".changeset/README.md")} contains information about using changesets`);
556 info(`- ${pc.blue(".changeset/config.json")} is our default config`);
557 }
558}
559
560async function pre(cwd, options) {
561 if (options.command === "enter") {
562 try {
563 await enterPre(cwd, options.tag);
564 logger.success(`Entered pre mode with tag ${pc.cyan(options.tag)}`);
565 logger.info("Run `changeset version` to version packages with prerelease versions");
566 } catch (err) {
567 if (err instanceof PreEnterButInPreModeError) {
568 logger.error("`changeset pre enter` cannot be run when in pre mode");
569 logger.info("If you're trying to exit pre mode, run `changeset pre exit`");
570 throw new ExitError(1);
571 }
572
573 throw err;
574 }
575 } else {
576 try {
577 await exitPre(cwd);
578 logger.success(`Exited pre mode`);
579 logger.info("Run `changeset version` to version packages with normal versions");
580 } catch (err) {
581 if (err instanceof PreExitButNotInPreModeError) {
582 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`");
584 throw new ExitError(1);
585 }
586
587 throw err;
588 }
589 }
590}
591
592const getLastJsonObjectFromString = str => {
593 str = str.replace(/[^}]*$/, "");
594
595 while (str) {
596 str = str.replace(/[^{]*/, "");
597
598 try {
599 return JSON.parse(str);
600 } catch (err) {
601 // move past the potentially leading `{` so the regexp in the loop can try to match for the next `{`
602 str = str.slice(1);
603 }
604 }
605
606 return null;
607};
608
609const npmRequestLimit = pLimit(40);
610const npmPublishLimit = pLimit(10);
611
612function jsonParse(input) {
613 try {
614 return JSON.parse(input);
615 } catch (err) {
616 if (err instanceof SyntaxError) {
617 console.error("error parsing json:", input);
618 }
619
620 throw err;
621 }
622}
623
624function getCorrectRegistry(packageJson) {
625 var _packageJson$publishC, _packageJson$publishC2;
626
627 const registry = (_packageJson$publishC = packageJson === null || packageJson === void 0 ? void 0 : (_packageJson$publishC2 = packageJson.publishConfig) === null || _packageJson$publishC2 === void 0 ? void 0 : _packageJson$publishC2.registry) !== null && _packageJson$publishC !== void 0 ? _packageJson$publishC : process.env.npm_config_registry;
628 return !registry || registry === "https://registry.yarnpkg.com" ? "https://registry.npmjs.org" : registry;
629}
630
631async function getPublishTool(cwd) {
632 const pm = await detect({
633 cwd
634 });
635 if (!pm || pm.name !== "pnpm") return {
636 name: "npm"
637 };
638
639 try {
640 let result = await spawn$1("pnpm", ["--version"], {
641 cwd
642 });
643 let version = result.stdout.toString().trim();
644 let parsed = semverParse(version);
645 return {
646 name: "pnpm",
647 shouldAddNoGitChecks: (parsed === null || parsed === void 0 ? void 0 : parsed.major) === undefined ? false : parsed.major >= 5
648 };
649 } catch (e) {
650 return {
651 name: "pnpm",
652 shouldAddNoGitChecks: false
653 };
654 }
655}
656
657async function getTokenIsRequired() {
658 // Due to a super annoying issue in yarn, we have to manually override this env variable
659 // See: https://github.com/yarnpkg/yarn/issues/2935#issuecomment-355292633
660 const envOverride = {
661 npm_config_registry: getCorrectRegistry()
662 };
663 let result = await spawn$1("npm", ["profile", "get", "--json"], {
664 env: Object.assign({}, process.env, envOverride)
665 });
666
667 if (result.code !== 0) {
668 error("error while checking if token is required", result.stderr.toString().trim() || result.stdout.toString().trim());
669 return false;
670 }
671
672 let json = jsonParse(result.stdout.toString());
673
674 if (json.error || !json.tfa || !json.tfa.mode) {
675 return false;
676 }
677
678 return json.tfa.mode === "auth-and-writes";
679}
680function getPackageInfo(packageJson) {
681 return npmRequestLimit(async () => {
682 info(`npm info ${packageJson.name}`); // Due to a couple of issues with yarnpkg, we also want to override the npm registry when doing
683 // npm info.
684 // Issues: We sometimes get back cached responses, i.e old data about packages which causes
685 // `publish` to behave incorrectly. It can also cause issues when publishing private packages
686 // as they will always give a 404, which will tell `publish` to always try to publish.
687 // See: https://github.com/yarnpkg/yarn/issues/2935#issuecomment-355292633
688
689 let result = await spawn$1("npm", ["info", packageJson.name, "--registry", getCorrectRegistry(packageJson), "--json"]); // Github package registry returns empty string when calling npm info
690 // for a non-existent package instead of a E404
691
692 if (result.stdout.toString() === "") {
693 return {
694 error: {
695 code: "E404"
696 }
697 };
698 }
699
700 return jsonParse(result.stdout.toString());
701 });
702}
703async function infoAllow404(packageJson) {
704 var _pkgInfo$error;
705
706 let pkgInfo = await getPackageInfo(packageJson);
707
708 if (((_pkgInfo$error = pkgInfo.error) === null || _pkgInfo$error === void 0 ? void 0 : _pkgInfo$error.code) === "E404") {
709 warn(`Received 404 for npm info ${pc.cyan(`"${packageJson.name}"`)}`);
710 return {
711 published: false,
712 pkgInfo: {}
713 };
714 }
715
716 if (pkgInfo.error) {
717 error(`Received an unknown error code: ${pkgInfo.error.code} for npm info ${pc.cyan(`"${packageJson.name}"`)}`);
718 error(pkgInfo.error.summary);
719 if (pkgInfo.error.detail) error(pkgInfo.error.detail);
720 throw new ExitError(1);
721 }
722
723 return {
724 published: true,
725 pkgInfo
726 };
727}
728let otpAskLimit = pLimit(1);
729
730let askForOtpCode = twoFactorState => otpAskLimit(async () => {
731 if (twoFactorState.token !== null) return twoFactorState.token;
732 info("This operation requires a one-time password from your authenticator.");
733 let val = await askQuestion("Enter one-time password:");
734 twoFactorState.token = val;
735 return val;
736});
737
738let getOtpCode = async twoFactorState => {
739 if (twoFactorState.token !== null) {
740 return twoFactorState.token;
741 }
742
743 return askForOtpCode(twoFactorState);
744}; // we have this so that we can do try a publish again after a publish without
745// the call being wrapped in the npm request limit and causing the publishes to potentially never run
746
747async function internalPublish(pkgName, opts, twoFactorState) {
748 let publishTool = await getPublishTool(opts.cwd);
749 let publishFlags = opts.access ? ["--access", opts.access] : [];
750 publishFlags.push("--tag", opts.tag);
751
752 if ((await twoFactorState.isRequired) && !isCI) {
753 let otpCode = await getOtpCode(twoFactorState);
754 publishFlags.push("--otp", otpCode);
755 }
756
757 if (publishTool.name === "pnpm" && publishTool.shouldAddNoGitChecks) {
758 publishFlags.push("--no-git-checks");
759 } // Due to a super annoying issue in yarn, we have to manually override this env variable
760 // See: https://github.com/yarnpkg/yarn/issues/2935#issuecomment-355292633
761
762
763 const envOverride = {
764 npm_config_registry: getCorrectRegistry()
765 };
766 let {
767 code,
768 stdout,
769 stderr
770 } = publishTool.name === "pnpm" ? await spawn$1("pnpm", ["publish", "--json", ...publishFlags], {
771 env: Object.assign({}, process.env, envOverride),
772 cwd: opts.cwd
773 }) : await spawn$1(publishTool.name, ["publish", opts.publishDir, "--json", ...publishFlags], {
774 env: Object.assign({}, process.env, envOverride)
775 });
776
777 if (code !== 0) {
778 // NPM's --json output is included alongside the `prepublish` and `postpublish` output in terminal
779 // We want to handle this as best we can but it has some struggles:
780 // - output of those lifecycle scripts can contain JSON
781 // - npm7 has switched to printing `--json` errors to stderr (https://github.com/npm/cli/commit/1dbf0f9bb26ba70f4c6d0a807701d7652c31d7d4)
782 // Note that the `--json` output is always printed at the end so this should work
783 let json = getLastJsonObjectFromString(stderr.toString()) || getLastJsonObjectFromString(stdout.toString());
784
785 if (json !== null && json !== void 0 && json.error) {
786 // The first case is no 2fa provided, the second is when the 2fa is wrong (timeout or wrong words)
787 if ((json.error.code === "EOTP" || json.error.code === "E401" && json.error.detail.includes("--otp=<code>")) && !isCI) {
788 if (twoFactorState.token !== null) {
789 // the current otp code must be invalid since it errored
790 twoFactorState.token = null;
791 } // just in case this isn't already true
792
793
794 twoFactorState.isRequired = Promise.resolve(true);
795 return internalPublish(pkgName, opts, twoFactorState);
796 }
797
798 error(`an error occurred while publishing ${pkgName}: ${json.error.code}`, json.error.summary, json.error.detail ? "\n" + json.error.detail : "");
799 }
800
801 error(stderr.toString() || stdout.toString());
802 return {
803 published: false
804 };
805 }
806
807 return {
808 published: true
809 };
810}
811
812function publish$1(pkgName, opts, twoFactorState) {
813 // If there are many packages to be published, it's better to limit the
814 // concurrency to avoid unwanted errors, for example from npm.
815 return npmRequestLimit(() => npmPublishLimit(() => internalPublish(pkgName, opts, twoFactorState)));
816}
817
818function getReleaseTag(pkgInfo, preState, tag) {
819 if (tag) return tag;
820
821 if (preState !== undefined && pkgInfo.publishedState !== "only-pre") {
822 return preState.tag;
823 }
824
825 return "latest";
826}
827
828const isCustomRegistry = registry => !!registry && registry !== "https://registry.npmjs.org" && registry !== "https://registry.yarnpkg.com";
829
830const getTwoFactorState = ({
831 otp,
832 publicPackages
833}) => {
834 if (otp) {
835 return {
836 token: otp,
837 isRequired: Promise.resolve(true)
838 };
839 }
840
841 if (isCI || publicPackages.some(pkg => {
842 var _pkg$packageJson$publ;
843
844 return isCustomRegistry((_pkg$packageJson$publ = pkg.packageJson.publishConfig) === null || _pkg$packageJson$publ === void 0 ? void 0 : _pkg$packageJson$publ.registry);
845 }) || isCustomRegistry(process.env.npm_config_registry)) {
846 return {
847 token: null,
848 isRequired: Promise.resolve(false)
849 };
850 }
851
852 return {
853 token: null,
854 // note: we're not awaiting this here, we want this request to happen in parallel with getUnpublishedPackages
855 isRequired: getTokenIsRequired()
856 };
857};
858
859async function publishPackages({
860 packages,
861 access,
862 otp,
863 preState,
864 tag
865}) {
866 const packagesByName = new Map(packages.map(x => [x.packageJson.name, x]));
867 const publicPackages = packages.filter(pkg => !pkg.packageJson.private);
868 const unpublishedPackagesInfo = await getUnpublishedPackages(publicPackages, preState);
869
870 if (unpublishedPackagesInfo.length === 0) {
871 return [];
872 }
873
874 const twoFactorState = getTwoFactorState({
875 otp,
876 publicPackages
877 });
878 return Promise.all(unpublishedPackagesInfo.map(pkgInfo => {
879 let pkg = packagesByName.get(pkgInfo.name);
880 return publishAPackage(pkg, access, twoFactorState, getReleaseTag(pkgInfo, preState, tag));
881 }));
882}
883
884async function publishAPackage(pkg, access, twoFactorState, tag) {
885 const {
886 name,
887 version,
888 publishConfig
889 } = pkg.packageJson;
890 info(`Publishing ${pc.cyan(`"${name}"`)} at ${pc.green(`"${version}"`)}`);
891 const publishConfirmation = await publish$1(name, {
892 cwd: pkg.dir,
893 publishDir: publishConfig !== null && publishConfig !== void 0 && publishConfig.directory ? join(pkg.dir, publishConfig.directory) : pkg.dir,
894 access: (publishConfig === null || publishConfig === void 0 ? void 0 : publishConfig.access) || access,
895 tag
896 }, twoFactorState);
897 return {
898 name,
899 newVersion: version,
900 published: publishConfirmation.published
901 };
902}
903
904async function getUnpublishedPackages(packages, preState) {
905 const results = await Promise.all(packages.map(async ({
906 packageJson
907 }) => {
908 const response = await infoAllow404(packageJson);
909 let publishedState = "never";
910
911 if (response.published) {
912 publishedState = "published";
913
914 if (preState !== undefined) {
915 if (response.pkgInfo.versions && response.pkgInfo.versions.every(version => semverParse(version).prerelease[0] === preState.tag)) {
916 publishedState = "only-pre";
917 }
918 }
919 }
920
921 return {
922 name: packageJson.name,
923 localVersion: packageJson.version,
924 publishedState,
925 publishedVersions: response.pkgInfo.versions || []
926 };
927 }));
928 const packagesToPublish = [];
929
930 for (const pkgInfo of results) {
931 const {
932 name,
933 publishedState,
934 localVersion,
935 publishedVersions
936 } = pkgInfo;
937
938 if (!publishedVersions.includes(localVersion)) {
939 packagesToPublish.push(pkgInfo);
940 info(`${name} is being published because our local version (${localVersion}) has not been published on npm`);
941
942 if (preState !== undefined && publishedState === "only-pre") {
943 info(`${name} is being published to ${pc.cyan("latest")} rather than ${pc.cyan(preState.tag)} because there has not been a regular release of it yet`);
944 }
945 } else {
946 // If the local version is behind npm, something is wrong, we warn here, and by not getting published later, it will fail
947 warn(`${name} is not being published because version ${localVersion} is already published on npm`);
948 }
949 }
950
951 return packagesToPublish;
952}
953
954async function getUntaggedPackages(packages, cwd, tool) {
955 const packageWithTags = await Promise.all(packages.map(async pkg => {
956 const tagName = tool === "root" ? `v${pkg.packageJson.version}` : `${pkg.packageJson.name}@${pkg.packageJson.version}`;
957 const isMissingTag = !((await git.tagExists(tagName, cwd)) || (await git.remoteTagExists(tagName)));
958 return {
959 pkg,
960 isMissingTag
961 };
962 }));
963 const untagged = [];
964
965 for (const packageWithTag of packageWithTags) {
966 if (packageWithTag.isMissingTag) {
967 untagged.push({
968 name: packageWithTag.pkg.packageJson.name,
969 newVersion: packageWithTag.pkg.packageJson.version,
970 published: false
971 });
972 }
973 }
974
975 return untagged;
976}
977
978function logReleases(pkgs) {
979 const mappedPkgs = pkgs.map(p => `${p.name}@${p.newVersion}`).join("\n");
980 log(mappedPkgs);
981}
982
983let importantSeparator$1 = pc.red("===============================IMPORTANT!===============================");
984let importantEnd$1 = pc.red("----------------------------------------------------------------------");
985
986function showNonLatestTagWarning(tag, preState) {
987 warn(importantSeparator$1);
988
989 if (preState) {
990 warn(`You are in prerelease mode so packages will be published to the ${pc.cyan(preState.tag)}
991 dist tag except for packages that have not had normal releases which will be published to ${pc.cyan("latest")}`);
992 } else if (tag !== "latest") {
993 warn(`Packages will be released under the ${tag} tag`);
994 }
995
996 warn(importantEnd$1);
997}
998
999async function publish(cwd, {
1000 otp,
1001 tag,
1002 gitTag = true
1003}, config) {
1004 const releaseTag = tag && tag.length > 0 ? tag : undefined;
1005 let preState = await readPreState(cwd);
1006
1007 if (releaseTag && preState && preState.mode === "pre") {
1008 error("Releasing under custom tag is not allowed in pre mode");
1009 log("To resolve this exit the pre mode by running `changeset pre exit`");
1010 throw new ExitError(1);
1011 }
1012
1013 if (releaseTag || preState) {
1014 showNonLatestTagWarning(tag, preState);
1015 }
1016
1017 const {
1018 packages,
1019 tool
1020 } = await getPackages(cwd);
1021 const tagPrivatePackages = config.privatePackages && config.privatePackages.tag;
1022 const publishedPackages = await publishPackages({
1023 packages,
1024 // if not public, we won't pass the access, and it works as normal
1025 access: config.access,
1026 otp,
1027 preState,
1028 tag: releaseTag
1029 });
1030 const privatePackages = packages.filter(pkg => pkg.packageJson.private && pkg.packageJson.version);
1031 const untaggedPrivatePackageReleases = tagPrivatePackages ? await getUntaggedPackages(privatePackages, cwd, tool) : [];
1032
1033 if (publishedPackages.length === 0 && untaggedPrivatePackageReleases.length === 0) {
1034 warn("No unpublished projects to publish");
1035 }
1036
1037 const successfulNpmPublishes = publishedPackages.filter(p => p.published);
1038 const unsuccessfulNpmPublishes = publishedPackages.filter(p => !p.published);
1039
1040 if (successfulNpmPublishes.length > 0) {
1041 success("packages published successfully:");
1042 logReleases(successfulNpmPublishes);
1043
1044 if (gitTag) {
1045 // We create the tags after the push above so that we know that HEAD won't change and that pushing
1046 // won't suffer from a race condition if another merge happens in the mean time (pushing tags won't
1047 // fail if we are behind the base branch).
1048 log(`Creating git tag${successfulNpmPublishes.length > 1 ? "s" : ""}...`);
1049 await tagPublish(tool, successfulNpmPublishes, cwd);
1050 }
1051 }
1052
1053 if (untaggedPrivatePackageReleases.length > 0) {
1054 success("found untagged projects:");
1055 logReleases(untaggedPrivatePackageReleases);
1056 await tagPublish(tool, untaggedPrivatePackageReleases, cwd);
1057 }
1058
1059 if (unsuccessfulNpmPublishes.length > 0) {
1060 error("packages failed to publish:");
1061 logReleases(unsuccessfulNpmPublishes);
1062 throw new ExitError(1);
1063 }
1064}
1065
1066async function tagPublish(tool, packageReleases, cwd) {
1067 if (tool !== "root") {
1068 for (const pkg of packageReleases) {
1069 const tag = `${pkg.name}@${pkg.newVersion}`;
1070 log("New tag: ", tag);
1071 await git.tag(tag, cwd);
1072 }
1073 } else {
1074 const tag = `v${packageReleases[0].newVersion}`;
1075 log("New tag: ", tag);
1076 await git.tag(tag, cwd);
1077 }
1078}
1079
1080async function status(cwd, {
1081 sinceMaster,
1082 since,
1083 verbose,
1084 output
1085}, config) {
1086 if (sinceMaster) {
1087 warn("--sinceMaster is deprecated and will be removed in a future major version");
1088 warn("Use --since=master instead");
1089 }
1090
1091 const sinceBranch = since === undefined ? sinceMaster ? "master" : undefined : since;
1092 const releasePlan = await getReleasePlan(cwd, sinceBranch, config);
1093 const {
1094 changesets,
1095 releases
1096 } = releasePlan;
1097 const changedPackages = await getVersionableChangedPackages(config, {
1098 cwd,
1099 ref: sinceBranch
1100 });
1101
1102 if (changedPackages.length > 0 && changesets.length === 0) {
1103 error("Some packages have been changed but no changesets were found. Run `changeset add` to resolve this error.");
1104 error("If this change doesn't need a release, run `changeset add --empty`.");
1105 process.exit(1);
1106 }
1107
1108 if (output) {
1109 await fs.writeFile(path.join(cwd, output), JSON.stringify(releasePlan, undefined, 2));
1110 return;
1111 }
1112
1113 const print = verbose ? verbosePrint : SimplePrint;
1114 print("patch", releases);
1115 log("---");
1116 print("minor", releases);
1117 log("---");
1118 print("major", releases);
1119 return releasePlan;
1120}
1121
1122function SimplePrint(type, releases) {
1123 const packages = releases.filter(r => r.type === type);
1124
1125 if (packages.length) {
1126 info(`Packages to be bumped at ${pc.green(type)}:\n`);
1127 const pkgs = packages.map(({
1128 name
1129 }) => `- ${name}`).join("\n");
1130 log(pc.green(pkgs));
1131 } else {
1132 info(`${pc.green("NO")} packages to be bumped at ${pc.green(type)}`);
1133 }
1134}
1135
1136function verbosePrint(type, releases) {
1137 const packages = releases.filter(r => r.type === type);
1138
1139 if (packages.length) {
1140 info(`Packages to be bumped at ${pc.green(type)}`);
1141
1142 for (const {
1143 name,
1144 newVersion: version,
1145 changesets
1146 } of packages) {
1147 log(`- ${pc.green(name)} ${pc.cyan(version)}`);
1148
1149 for (const c of changesets) {
1150 log(` - ${pc.blue(`.changeset/${c}.md`)}`);
1151 }
1152 }
1153 } else {
1154 info(`Running release would release ${pc.red("NO")} packages as a ${pc.green(type)}`);
1155 }
1156}
1157
1158async function tag(cwd, config) {
1159 const {
1160 packages,
1161 tool
1162 } = await getPackages(cwd);
1163 const allExistingTags = await git.getAllTags(cwd);
1164 const taggablePackages = packages.filter(pkg => !shouldSkipPackage(pkg, {
1165 ignore: config.ignore,
1166 allowPrivatePackages: config.privatePackages.tag
1167 }));
1168
1169 for (const {
1170 name,
1171 newVersion
1172 } of await getUntaggedPackages(taggablePackages, cwd, tool)) {
1173 const tag = tool !== "root" ? `${name}@${newVersion}` : `v${newVersion}`;
1174
1175 if (allExistingTags.has(tag)) {
1176 log("Skipping tag (already exists): ", tag);
1177 } else {
1178 log("New tag: ", tag);
1179 await git.tag(tag, cwd);
1180 }
1181 }
1182}
1183
1184// folder, and tidy up the subfolders
1185// THIS SHOULD BE REMOVED WHEN SUPPORT FOR CHANGESETS FROM V1 IS DROPPED
1186
1187const removeEmptyFolders = async folderPath => {
1188 const dirContents = fs.readdirSync(folderPath);
1189 return Promise.all(dirContents.map(async contentPath => {
1190 const singleChangesetPath = path.resolve(folderPath, contentPath);
1191
1192 try {
1193 if ((await fs.readdir(singleChangesetPath)).length < 1) {
1194 await fs.rmdir(singleChangesetPath);
1195 }
1196 } catch (err) {
1197 if (err.code !== "ENOTDIR") {
1198 throw err;
1199 }
1200 }
1201 }));
1202};
1203
1204let importantSeparator = pc.red("===============================IMPORTANT!===============================");
1205let importantEnd = pc.red("----------------------------------------------------------------------");
1206async function version(cwd, options, config) {
1207 var _config$snapshot$prer;
1208
1209 const releaseConfig = _objectSpread2(_objectSpread2({}, config), {}, {
1210 // Disable committing when in snapshot mode
1211 commit: options.snapshot ? false : config.commit
1212 });
1213
1214 const [changesets, preState] = await Promise.all([readChangesets(cwd), readPreState(cwd), removeEmptyFolders(path.resolve(cwd, ".changeset"))]);
1215
1216 if ((preState === null || preState === void 0 ? void 0 : preState.mode) === "pre") {
1217 warn(importantSeparator);
1218
1219 if (options.snapshot !== undefined) {
1220 error("Snapshot release is not allowed in pre mode");
1221 log("To resolve this exit the pre mode by running `changeset pre exit`");
1222 throw new ExitError(1);
1223 } else {
1224 warn("You are in prerelease mode");
1225 warn("If you meant to do a normal release you should revert these changes and run `changeset pre exit`");
1226 warn("You can then run `changeset version` again to do a normal release");
1227 }
1228
1229 warn(importantEnd);
1230 }
1231
1232 if (changesets.length === 0 && (preState === undefined || preState.mode !== "exit")) {
1233 warn("No unreleased changesets found, exiting.");
1234 return;
1235 }
1236
1237 let packages = await getPackages(cwd);
1238 let releasePlan = assembleReleasePlan(changesets, packages, releaseConfig, preState, options.snapshot ? {
1239 tag: options.snapshot === true ? undefined : options.snapshot,
1240 commit: (_config$snapshot$prer = config.snapshot.prereleaseTemplate) !== null && _config$snapshot$prer !== void 0 && _config$snapshot$prer.includes("{commit}") ? await getCurrentCommitId({
1241 cwd
1242 }) : undefined
1243 } : undefined);
1244 let [...touchedFiles] = await applyReleasePlan(releasePlan, packages, releaseConfig, options.snapshot);
1245 const [{
1246 getVersionMessage
1247 }, commitOpts] = getCommitFunctions(releaseConfig.commit, cwd);
1248
1249 if (getVersionMessage) {
1250 let touchedFile; // Note, git gets angry if you try and have two git actions running at once
1251 // So we need to be careful that these iterations are properly sequential
1252
1253 while (touchedFile = touchedFiles.shift()) {
1254 await git.add(path.relative(cwd, touchedFile), cwd);
1255 }
1256
1257 const commit = await git.commit(await getVersionMessage(releasePlan, commitOpts), cwd);
1258
1259 if (!commit) {
1260 error("Changesets ran into trouble committing your files");
1261 } else {
1262 log("All files have been updated and committed. You're ready to publish!");
1263 }
1264 } else {
1265 log("All files have been updated. Review them and commit at your leisure");
1266 }
1267}
1268
1269async function run(input, flags, cwd) {
1270 if (input[0] === "init") {
1271 await init(cwd);
1272 return;
1273 }
1274
1275 if (!fs.existsSync(path.resolve(cwd, ".changeset"))) {
1276 error("There is no .changeset folder. ");
1277 error("If this is the first time `changesets` have been used in this project, run `yarn changeset init` to get set up.");
1278 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.");
1279 throw new ExitError(1);
1280 }
1281
1282 const packages = await getPackages(cwd);
1283 let config;
1284
1285 try {
1286 config = await read(cwd, packages);
1287 } catch (e) {
1288 let oldConfigExists = await fs.pathExists(path.resolve(cwd, ".changeset/config.js"));
1289
1290 if (oldConfigExists) {
1291 error("It looks like you're using the version 1 `.changeset/config.js` file");
1292 error("You'll need to convert it to a `.changeset/config.json` file");
1293 error("The format of the config object has significantly changed in v2 as well");
1294 error(" - we thoroughly recommend looking at the changelog for this package for what has changed");
1295 throw new ExitError(1);
1296 } else {
1297 throw e;
1298 }
1299 }
1300
1301 if (input.length < 1) {
1302 const {
1303 empty,
1304 open
1305 } = flags; // @ts-ignore if this is undefined, we have already exited
1306
1307 await add(cwd, {
1308 empty,
1309 open
1310 }, config);
1311 } else if (input[0] !== "pre" && input.length > 1) {
1312 error("Too many arguments passed to changesets - we only accept the command name as an argument");
1313 } else {
1314 const {
1315 sinceMaster,
1316 since,
1317 verbose,
1318 output,
1319 otp,
1320 empty,
1321 ignore,
1322 snapshot,
1323 snapshotPrereleaseTemplate,
1324 tag: tag$1,
1325 open,
1326 gitTag
1327 } = flags;
1328 const deadFlags = ["updateChangelog", "isPublic", "skipCI", "commit"];
1329 deadFlags.forEach(flag => {
1330 if (flags[flag]) {
1331 error(`the flag ${flag} has been removed from changesets for version 2`);
1332 error(`Please encode the desired value into your config`);
1333 error(`See our changelog for more details`);
1334 throw new ExitError(1);
1335 }
1336 }); // Command line options need to be undefined, otherwise their
1337 // default value overrides the user's provided config in their
1338 // config file. For this reason, we only assign them to this
1339 // object as and when they exist.
1340
1341 switch (input[0]) {
1342 case "add":
1343 {
1344 await add(cwd, {
1345 empty,
1346 open
1347 }, config);
1348 return;
1349 }
1350
1351 case "version":
1352 {
1353 let ignoreArrayFromCmd;
1354
1355 if (typeof ignore === "string") {
1356 ignoreArrayFromCmd = [ignore];
1357 } else {
1358 // undefined or an array
1359 ignoreArrayFromCmd = ignore;
1360 } // Validate that items in ignoreArrayFromCmd are valid project names
1361
1362
1363 let pkgNames = new Set(packages.packages.map(({
1364 packageJson
1365 }) => packageJson.name));
1366 const messages = [];
1367
1368 for (const pkgName of ignoreArrayFromCmd || []) {
1369 if (!pkgNames.has(pkgName)) {
1370 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.`);
1371 }
1372 }
1373
1374 if (config.ignore.length > 0 && ignoreArrayFromCmd) {
1375 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.`);
1376 } else if (ignoreArrayFromCmd) {
1377 // use the ignore flags from cli
1378 config.ignore = ignoreArrayFromCmd;
1379 }
1380
1381 const packagesByName = new Map(packages.packages.map(x => [x.packageJson.name, x])); // validate that all dependents of skipped packages are also skipped
1382
1383 const dependentsGraph = getDependentsGraph(packages, {
1384 ignoreDevDependencies: true,
1385 bumpVersionsWithWorkspaceProtocolOnly: config.bumpVersionsWithWorkspaceProtocolOnly
1386 });
1387
1388 for (const pkg of packages.packages) {
1389 if (!shouldSkipPackage(pkg, {
1390 ignore: config.ignore,
1391 allowPrivatePackages: config.privatePackages.version
1392 })) {
1393 continue;
1394 }
1395
1396 const skippedPackage = pkg.packageJson.name;
1397 const dependents = dependentsGraph.get(skippedPackage) || [];
1398
1399 for (const dependent of dependents) {
1400 const dependentPkg = packagesByName.get(dependent);
1401
1402 if (!shouldSkipPackage(dependentPkg, {
1403 ignore: config.ignore,
1404 allowPrivatePackages: config.privatePackages.version
1405 })) {
1406 messages.push(`The package "${dependent}" depends on the skipped package "${skippedPackage}" (either by \`ignore\` option or by \`privatePackages.version\`), but "${dependent}" is not being skipped. Please pass "${dependent}" to the \`--ignore\` flag.`);
1407 }
1408 }
1409 }
1410
1411 if (messages.length > 0) {
1412 error(messages.join("\n"));
1413 throw new ExitError(1);
1414 }
1415
1416 if (snapshotPrereleaseTemplate) {
1417 config.snapshot.prereleaseTemplate = snapshotPrereleaseTemplate;
1418 }
1419
1420 await version(cwd, {
1421 snapshot
1422 }, config);
1423 return;
1424 }
1425
1426 case "publish":
1427 {
1428 await publish(cwd, {
1429 otp,
1430 tag: tag$1,
1431 gitTag
1432 }, config);
1433 return;
1434 }
1435
1436 case "status":
1437 {
1438 await status(cwd, {
1439 sinceMaster,
1440 since,
1441 verbose,
1442 output
1443 }, config);
1444 return;
1445 }
1446
1447 case "tag":
1448 {
1449 await tag(cwd, config);
1450 return;
1451 }
1452
1453 case "pre":
1454 {
1455 let command = input[1];
1456
1457 if (command !== "enter" && command !== "exit") {
1458 error("`enter`, `exit` or `snapshot` must be passed after prerelease");
1459 throw new ExitError(1);
1460 }
1461
1462 let tag = input[2];
1463
1464 if (command === "enter" && typeof tag !== "string") {
1465 error(`A tag must be passed when using prerelese enter`);
1466 throw new ExitError(1);
1467 } // @ts-ignore
1468
1469
1470 await pre(cwd, {
1471 command,
1472 tag
1473 });
1474 return;
1475 }
1476
1477 case "bump":
1478 {
1479 error('In version 2 of changesets, "bump" has been renamed to "version" - see our changelog for an explanation');
1480 error("To fix this, use `changeset version` instead, and update any scripts that use changesets");
1481 throw new ExitError(1);
1482 }
1483
1484 case "release":
1485 {
1486 error('In version 2 of changesets, "release" has been renamed to "publish" - see our changelog for an explanation');
1487 error("To fix this, use `changeset publish` instead, and update any scripts that use changesets");
1488 throw new ExitError(1);
1489 }
1490
1491 default:
1492 {
1493 error(`Invalid command ${input[0]} was provided`);
1494 throw new ExitError(1);
1495 }
1496 }
1497 }
1498}
1499
1500const args = process.argv.slice(2);
1501const parsed = mri(args, {
1502 boolean: ["sinceMaster", "verbose", "empty", "open", "gitTag", "snapshot"],
1503 string: ["output", "otp", "since", "ignore", "tag", "snapshot", "snapshotPrereleaseTemplate"],
1504 alias: {
1505 // Short flags
1506 v: "verbose",
1507 o: "output",
1508 // Support kebab-case flags
1509 "since-master": "sinceMaster",
1510 "git-tag": "gitTag",
1511 "snapshot-prerelease-template": "snapshotPrereleaseTemplate",
1512 // Deprecated flags
1513 "update-changelog": "updateChangelog",
1514 "is-public": "isPublic",
1515 "skip-c-i": "skipCI"
1516 },
1517 default: {
1518 gitTag: true
1519 }
1520}); // `mri` doesn't handle mixed boolean and strings well. It'll always try to coerce it as
1521// a string even if only `--snapshot` is passed. We check here if this was the case and
1522// try to coerce it as a boolean
1523
1524if (parsed.snapshot === "" && args[args.indexOf("--snapshot") + 1] !== "") {
1525 parsed.snapshot = true;
1526} // Help message should only be shown if it's the only argument passed
1527
1528
1529if (parsed.help && args.length === 1) {
1530 console.log(`
1531 Organise your package versioning and publishing to make both contributors and maintainers happy
1532
1533 Usage
1534 $ changeset [command]
1535 Commands
1536 init
1537 add [--empty] [--open]
1538 version [--ignore] [--snapshot <?name>] [--snapshot-prerelease-template <template>]
1539 publish [--tag <name>] [--otp <code>] [--no-git-tag]
1540 status [--since <branch>] [--verbose] [--output JSON_FILE.json]
1541 pre <enter|exit> <tag>
1542 tag
1543
1544 `);
1545 process.exit(0);
1546} // Version should only be shown if it's the only argument passed
1547
1548
1549if (parsed.version && args.length === 1) {
1550 // eslint-disable-next-line import/no-extraneous-dependencies
1551 console.log(require("@changesets/cli/package.json").version);
1552 process.exit(0);
1553}
1554
1555const cwd = process.cwd();
1556run(parsed._, parsed, cwd).catch(err => {
1557 if (err instanceof InternalError) {
1558 error("The following error is an internal unexpected error, these should never happen.");
1559 error("Please open an issue with the following link");
1560 error(`https://github.com/changesets/changesets/issues/new?title=${encodeURIComponent(`Unexpected error during ${parsed._[0] || "add"} command`)}&body=${encodeURIComponent(`## Error
1561
1562\`\`\`
1563${format("", err).replace(process.cwd(), "<cwd>")}
1564\`\`\`
1565
1566## Versions
1567
1568- @changesets/cli@${// eslint-disable-next-line import/no-extraneous-dependencies
1569 require("@changesets/cli/package.json").version}
1570- node@${process.version}
1571
1572## Extra details
1573
1574<!-- 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. 😁 -->
1575`)}`);
1576 }
1577
1578 if (err instanceof ExitError) {
1579 return process.exit(err.code);
1580 }
1581
1582 error(err);
1583 process.exit(1);
1584});