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