UNPKG

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