diff --git a/.changeset/loose-towns-care.md b/.changeset/loose-towns-care.md new file mode 100644 index 000000000..e141cfb53 --- /dev/null +++ b/.changeset/loose-towns-care.md @@ -0,0 +1,5 @@ +--- +"@changesets/cli": patch +--- + +Use `cac` for CLI arg parsing and handling diff --git a/packages/cli/package.json b/packages/cli/package.json index fb1bbfc29..a0a2fd4f2 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -48,9 +48,9 @@ "@changesets/write": "workspace:^", "@clack/prompts": "^1.4.0", "@manypkg/get-packages": "^3.0.0", + "cac": "^7.0.0", "import-meta-resolve": "^4.2.0", "launch-editor": "^2.13.2", - "mri": "^1.2.0", "package-manager-detector": "^1.1.0", "semver": "^7.5.3", "tinyexec": "^1.0.2" diff --git a/packages/cli/src/commands/add/__tests__/add.test.ts b/packages/cli/src/commands/add/__tests__/add.test.ts index 04375576a..b3ca906d7 100644 --- a/packages/cli/src/commands/add/__tests__/add.test.ts +++ b/packages/cli/src/commands/add/__tests__/add.test.ts @@ -108,10 +108,11 @@ describe("Add command", () => { name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify(defaultConfig), }); mockUserResponses({ releases: { "pkg-a": "patch" } }); - await addChangeset(cwd, { empty: false }, defaultConfig); + await addChangeset({ cwd }); const changesets = await getChangesets(cwd); expect(changesets.length).toBe(1); @@ -148,6 +149,7 @@ describe("Add command", () => { name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify(defaultConfig), }); mockUserResponses({ @@ -155,7 +157,7 @@ describe("Add command", () => { consoleSummaries, editorSummaries, }); - await addChangeset(cwd, { empty: false }, defaultConfig); + await addChangeset({ cwd }); const changesets = await getChangesets(cwd); expect(changesets.length).toBe(1); @@ -175,6 +177,7 @@ describe("Add command", () => { name: "single-package", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify(defaultConfig), }); const summary = "summary message mock"; @@ -193,7 +196,7 @@ describe("Add command", () => { throw new Error(`An answer could not be found for ${question}`); }); - await addChangeset(cwd, { empty: false }, defaultConfig); + await addChangeset({ cwd }); const changesets = await getChangesets(cwd); expect(changesets.length).toBe(1); @@ -224,13 +227,7 @@ describe("Add command", () => { name: "pkg-b", version: "1.0.0", }), - }); - - mockUserResponses({ releases: { "pkg-a": "patch" } }); - await addChangeset( - cwd, - { empty: false }, - { + ".changeset/config.json": JSON.stringify({ ...defaultConfig, commit: [ path.resolve( @@ -243,8 +240,11 @@ describe("Add command", () => { ), null, ], - }, - ); + }), + }); + + mockUserResponses({ releases: { "pkg-a": "patch" } }); + await addChangeset({ cwd }); const result = await exec("git", ["log", "--oneline", "-1"], { nodeOptions: { cwd }, @@ -266,9 +266,10 @@ describe("Add command", () => { name: "pkg-a", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify(defaultConfig), }); - await addChangeset(cwd, { empty: true }, defaultConfig); + await addChangeset({ cwd, empty: true }); const changesets = await getChangesets(cwd); expect(changesets.length).toBe(1); @@ -287,16 +288,13 @@ describe("Add command", () => { name: "single-package", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify(defaultConfig), }); mockedUtils.askList.mockReturnValueOnce(Promise.resolve("minor")); mockedUtils.askConfirm.mockReturnValueOnce(Promise.resolve(true)); - await addChangeset( - cwd, - { empty: false, message: "summary from message" }, - defaultConfig, - ); + await addChangeset({ cwd, message: "summary from message" }); const changesets = await getChangesets(cwd); expect(changesets.length).toBe(1); @@ -320,12 +318,13 @@ describe("Add command", () => { name: "single-package", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify(defaultConfig), }); mockedUtils.askList.mockReturnValueOnce(Promise.resolve("patch")); mockedUtils.askConfirm.mockReturnValueOnce(Promise.resolve(true)); - await addChangeset(cwd, { empty: false, message: "" }, defaultConfig); + await addChangeset({ cwd, message: "" }); const changesets = await getChangesets(cwd); expect(changesets.length).toBe(1); @@ -355,14 +354,11 @@ describe("Add command", () => { name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify(defaultConfig), }); mockUserResponses({ releases: { "pkg-a": "patch" } }); - await addChangeset( - cwd, - { empty: false, message: "monorepo summary from message" }, - defaultConfig, - ); + await addChangeset({ cwd, message: "monorepo summary from message" }); const changesets = await getChangesets(cwd); expect(changesets.length).toBe(1); @@ -391,13 +387,14 @@ describe("Add command", () => { name: "pkg-a", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify(defaultConfig), }); - await addChangeset( + await addChangeset({ cwd, - { empty: true, message: "empty changeset summary" }, - defaultConfig, - ); + empty: true, + message: "empty changeset summary", + }); const changesets = await getChangesets(cwd); expect(changesets.length).toBe(1); @@ -425,7 +422,7 @@ describe("Add command", () => { name: "pkg-b", version: "1.0.0", }), - ".changeset/config.json": JSON.stringify({}), + ".changeset/config.json": JSON.stringify(defaultConfig), }); await exec("git", ["checkout", "-b", "foo"], { nodeOptions: { cwd } }); @@ -445,7 +442,7 @@ describe("Add command", () => { await git.commit("update pkg-b", cwd); mockUserResponses({ releases: { "pkg-b": "patch" } }); - await addChangeset(cwd, { empty: false, since: "foo" }, defaultConfig); + await addChangeset({ cwd, empty: false, since: "foo" }); expect(mockedUtils.askMultiselect).toHaveBeenCalledWith( expect.stringContaining("Which packages"), @@ -477,33 +474,23 @@ describe("Add command", () => { "packages/pkg-a/package.json": JSON.stringify({ name: "pkg-a", version: "1.0.3", - dependencies: { - "pkg-b": "~1.2.0", - }, }), "packages/pkg-b/package.json": JSON.stringify({ name: "pkg-b", version: "1.2.0", - dependencies: { - "pkg-c": "2.0.0", - "pkg-a": "^1.0.3", - }, }), "packages/pkg-c/package.json": JSON.stringify({ name: "pkg-c", version: "2.0.0", - dependencies: { - "pkg-a": "^1.0.3", - }, + }), + ".changeset/config.json": JSON.stringify({ + ...defaultConfig, + ignore: ["pkg-b"], }), }); mockUserResponses({ releases: { "pkg-a": "patch" } }); - await addChangeset( - cwd, - { empty: false }, - { ...defaultConfig, ignore: ["pkg-b"] }, - ); + await addChangeset({ cwd }); const choices = mockedUtils.askMultiselect.mock.calls[0][1]["unchanged packages"]; expect(choices).toMatchObject([{ value: "pkg-a" }, { value: "pkg-c" }]); @@ -529,10 +516,11 @@ describe("Add command", () => { name: "pkg-c", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify(defaultConfig), }); mockUserResponses({ releases: { "pkg-a": "patch" } }); - await addChangeset(cwd, { empty: false }, defaultConfig); + await addChangeset({ cwd }); const choices = mockedUtils.askMultiselect.mock.calls[0][1]["unchanged packages"]; expect(choices).toStrictEqual([{ value: "pkg-a" }, { value: "pkg-c" }]); @@ -559,20 +547,17 @@ describe("Add command", () => { name: "pkg-c", version: "1.0.0", }), - }); - - mockUserResponses({ releases: { "pkg-a": "patch" } }); - await addChangeset( - cwd, - { empty: false }, - { + ".changeset/config.json": JSON.stringify({ ...defaultConfig, privatePackages: { version: false, tag: false, }, - }, - ); + }), + }); + + mockUserResponses({ releases: { "pkg-a": "patch" } }); + await addChangeset({ cwd }); const choices = mockedUtils.askMultiselect.mock.calls[0][1]["unchanged packages"]; expect(choices).toStrictEqual([{ value: "pkg-a" }, { value: "pkg-c" }]); @@ -585,11 +570,12 @@ describe("Add command", () => { "package.json": JSON.stringify({ name: "test-missing-version", }), + ".changeset/config.json": JSON.stringify(defaultConfig), }); - await expect(() => - addChangeset(cwd, { empty: false }, defaultConfig), - ).rejects.toThrow("The process exited with code: 1"); + await expect(() => addChangeset({ cwd })).rejects.toThrow( + "The process exited with code: 1", + ); expect(loggerErrorSpy).toHaveBeenCalledOnce(); const output = stripVTControlCharacters(loggerErrorSpy.mock.calls[0][0]); @@ -614,11 +600,12 @@ describe("Add command", () => { "packages/pkg-b/package.json": JSON.stringify({ name: "pkg-b", }), + ".changeset/config.json": JSON.stringify(defaultConfig), }); - await expect(() => - addChangeset(cwd, { empty: false }, defaultConfig), - ).rejects.toThrow("The process exited with code: 1"); + await expect(() => addChangeset({ cwd })).rejects.toThrow( + "The process exited with code: 1", + ); expect(loggerErrorSpy).toHaveBeenCalledOnce(); const output = stripVTControlCharacters(loggerErrorSpy.mock.calls[0][0]); @@ -628,4 +615,68 @@ describe("Add command", () => { Ensure that relevant package.json files have a \`version\` field" `); }); + + it("should be able to add a changeset when called from subdirectory", async () => { + const rootDir = await testdir({ + "package.json": JSON.stringify({ + private: true, + name: "root-pkg", + workspaces: ["packages/*"], + }), + "package-lock.json": "", + "packages/pkg-a/package.json": JSON.stringify({ + name: "pkg-a", + version: "1.0.0", + }), + "packages/pkg-b/package.json": JSON.stringify({ + name: "pkg-b", + version: "1.0.0", + }), + ".changeset/config.json": JSON.stringify(defaultConfig), + }); + + const cwd = path.resolve(rootDir, "packages", "pkg-a"); + + mockUserResponses({ releases: { "pkg-a": "patch" } }); + await addChangeset({ cwd }); + + const changesets = await getChangesets(rootDir); + expect(changesets.length).toBe(1); + expect(changesets[0]).toEqual( + expect.objectContaining({ + summary: "summary message mock", + releases: [{ name: "pkg-a", type: "patch" }], + }), + ); + }); + + it("should throw when .changeset folder is missing when called from subdirectory", async () => { + const loggerErrorSpy = vi.spyOn(clack.log, "error"); + + const rootDir = await testdir({ + "package.json": JSON.stringify({ + private: true, + name: "root-pkg", + workspaces: ["packages/*"], + }), + "package-lock.json": "", + "packages/pkg-a/package.json": JSON.stringify({ + name: "pkg-a", + version: "1.0.0", + }), + }); + + const cwd = path.resolve(rootDir, "packages", "pkg-a"); + + try { + await addChangeset({ cwd, message: "test" }); + } catch { + // ignore the error. We just want to validate the error message + } + + const arg = loggerErrorSpy.mock.calls[0][0]; + expect(stripVTControlCharacters(arg)).toEqual( + expect.stringContaining("There is no .changeset folder."), + ); + }); }); diff --git a/packages/cli/src/commands/add/index.ts b/packages/cli/src/commands/add/index.ts index 91748a868..4a798535f 100644 --- a/packages/cli/src/commands/add/index.ts +++ b/packages/cli/src/commands/add/index.ts @@ -1,10 +1,10 @@ import path from "node:path"; import { fileURLToPath } from "node:url"; import c from "@changesets/color"; +import { read } from "@changesets/config"; import { ExitError } from "@changesets/errors"; import * as git from "@changesets/git"; import { shouldSkipPackage } from "@changesets/should-skip-package"; -import type { Config } from "@changesets/types"; import { writeChangeset } from "@changesets/write"; import { log } from "@clack/prompts"; import { getPackages } from "@manypkg/get-packages"; @@ -13,20 +13,23 @@ import { getCommitFunctions } from "../../commit/getCommitFunctions.ts"; import * as cli from "../../utils/cli-utilities.ts"; import { importantWarning } from "../../utils/cli-utilities.ts"; import { getVersionableChangedPackages } from "../../utils/versionablePackages.ts"; +import { ensureChangesetFolder } from "../shared.ts"; import { createChangeset } from "./createChangeset.ts"; import { printConfirmationMessage } from "./messages.ts"; -export async function add( - cwd: string, - { - empty, - open, - since, - message, - }: { empty?: boolean; open?: boolean; since?: string; message?: string }, - config: Config, -): Promise { +export interface AddOptions { + cwd?: string; + empty?: boolean; + open?: boolean; + since?: string; + message?: string; +} + +export async function add(options?: AddOptions): Promise { + const cwd = options?.cwd ?? process.cwd(); + const packages = await getPackages(cwd); + await ensureChangesetFolder(packages.rootDir); if (packages.packages.length === 0) { log.error( `No packages found. You might have ${packages.tool.type} workspaces configured but no packages yet?`, @@ -34,6 +37,8 @@ export async function add( throw new ExitError(1); } + const config = await read(packages.rootDir, packages); + const versionablePackages = packages.packages.filter( (pkg) => !shouldSkipPackage(pkg, { @@ -53,22 +58,22 @@ No versionable packages found throw new ExitError(1); } - const changesetBase = path.resolve(cwd, ".changeset"); + const changesetBase = path.resolve(packages.rootDir, ".changeset"); let newChangeset: Awaited>; - if (empty) { + if (options?.empty) { newChangeset = { confirmed: true, releases: [], - summary: message ?? "", + summary: options?.message ?? "", }; } else { let changedPackagesNames: string[] = []; try { changedPackagesNames = ( await getVersionableChangedPackages(config, { - cwd, - ref: since, + cwd: packages.rootDir, + ref: options?.since, }) ).map((pkg) => pkg.packageJson.name); } catch (error) { @@ -76,7 +81,7 @@ No versionable packages found // in the CLI. So if any error happens while we try to do so, we only log a warning and continue log.warn( ` -Failed to identify which packages have changed since the ${since ? "ref" : "base branch"} due to an error: +Failed to identify which packages have changed since the ${options?.since ? "ref" : "base branch"} due to an error: ${(error as Error).toString()} `.trim(), ); @@ -85,7 +90,7 @@ ${(error as Error).toString()} newChangeset = await createChangeset( changedPackagesNames, versionablePackages, - message, + options?.message, ); printConfirmationMessage(newChangeset, versionablePackages.length > 1); @@ -98,25 +103,37 @@ ${(error as Error).toString()} } if (newChangeset.confirmed) { - const changesetID = await writeChangeset(newChangeset, cwd, config); + const changesetID = await writeChangeset( + newChangeset, + packages.rootDir, + config, + ); const [{ getAddMessage }, commitOpts] = await getCommitFunctions( config.commit, - cwd, + packages.rootDir, path.dirname(fileURLToPath(import.meta.url)), ); const finalLogMessageLines: string[] = []; if (getAddMessage) { - await git.add(path.resolve(changesetBase, `${changesetID}.md`), cwd); - await git.commit(await getAddMessage(newChangeset, commitOpts), cwd); + await git.add( + path.resolve(changesetBase, `${changesetID}.md`), + packages.rootDir, + ); + await git.commit( + await getAddMessage(newChangeset, commitOpts), + packages.rootDir, + ); finalLogMessageLines.push( - c.green(`${empty ? "Empty " : ""}Changeset added and committed!`), + c.green( + `${options?.empty ? "Empty " : ""}Changeset added and committed!`, + ), ); } else { finalLogMessageLines.push( c.green( - `${empty ? "Empty " : ""}Changeset added - you can now commit it!`, + `${options?.empty ? "Empty " : ""}Changeset added - you can now commit it!`, ), ); } @@ -150,7 +167,7 @@ This Changeset includes a major change and we STRONGLY recommend adding more inf log.success(finalLogMessageLines.join("\n")); - if (open) { + if (options?.open) { launchEditor(changesetPath); } } diff --git a/packages/cli/src/commands/init/__tests__/command.test.ts b/packages/cli/src/commands/init/__tests__/command.test.ts index 7aae8d57b..8227a0362 100644 --- a/packages/cli/src/commands/init/__tests__/command.test.ts +++ b/packages/cli/src/commands/init/__tests__/command.test.ts @@ -14,12 +14,17 @@ const getPaths = (cwd: string) => ({ describe("init", () => { silenceLogsInBlock(); it("should initialize in a project without a .changeset folder", async () => { - const cwd = await testdir({}); + const cwd = await testdir({ + "package.json": JSON.stringify({ + private: true, + name: "root-pkg", + }), + }); const { readmePath, configPath } = getPaths(cwd); expect(existsSync(readmePath)).toBe(false); expect(existsSync(configPath)).toBe(false); - await initializeCommand(cwd); + await initializeCommand({ cwd }); expect(existsSync(readmePath)).toBe(true); expect(existsSync(configPath)).toBe(true); }); @@ -33,7 +38,7 @@ describe("init", () => { "package-lock.json": "", }); - await initializeCommand(cwd); + await initializeCommand({ cwd }); expect( JSON.parse( await fs.readFile(path.join(cwd, ".changeset/config.json"), "utf8"), @@ -50,7 +55,7 @@ describe("init", () => { "package-lock.json": "", }); - await initializeCommand(cwd); + await initializeCommand({ cwd }); const configPath = path.join(cwd, ".changeset/config.json"); const config = (await fs.readFile(configPath)).toString(); @@ -71,7 +76,7 @@ describe("init", () => { }), }); - await initializeCommand(cwd); + await initializeCommand({ cwd }); expect( JSON.parse( await fs.readFile(path.join(cwd, ".changeset/config.json"), "utf8"), diff --git a/packages/cli/src/commands/init/index.ts b/packages/cli/src/commands/init/index.ts index be76f0b98..5d365d303 100644 --- a/packages/cli/src/commands/init/index.ts +++ b/packages/cli/src/commands/init/index.ts @@ -5,6 +5,7 @@ import { fileURLToPath } from "node:url"; import c from "@changesets/color"; import { defaultWrittenConfig } from "@changesets/config"; import { log } from "@clack/prompts"; +import { getPackages } from "@manypkg/get-packages"; const pkgPath = path.dirname( fileURLToPath(import.meta.resolve("@changesets/cli/package.json")), @@ -12,8 +13,15 @@ const pkgPath = path.dirname( const defaultConfig = `${JSON.stringify(defaultWrittenConfig, null, 2)}\n`; -export async function init(cwd: string) { - const changesetBase = path.resolve(cwd, ".changeset"); +export interface InitOptions { + cwd?: string; +} + +export async function init(options?: InitOptions) { + const cwd = options?.cwd ?? process.cwd(); + + const packages = await getPackages(cwd); + const changesetBase = path.resolve(packages.rootDir, ".changeset"); if (existsSync(changesetBase)) { if (!existsSync(path.join(changesetBase, "config.json"))) { diff --git a/packages/cli/src/commands/pre/index.test.ts b/packages/cli/src/commands/pre/index.test.ts index 6b2eeb4b4..b468cfaf5 100644 --- a/packages/cli/src/commands/pre/index.test.ts +++ b/packages/cli/src/commands/pre/index.test.ts @@ -20,8 +20,9 @@ describe("enterPre", () => { workspaces: ["packages/*"], }), "package-lock.json": "", + ".changeset/config.json": JSON.stringify({}), }); - await pre(cwd, { command: "enter", tag: "next" }); + await pre({ cwd, command: "enter", tag: "next" }); expect( JSON.parse( @@ -57,7 +58,7 @@ describe("enterPre", () => { }); await expect( - pre(cwd, { command: "enter", tag: "next" }), + pre({ cwd, command: "enter", tag: "next" }), ).rejects.toBeInstanceOf(ExitError); expect(mockedLogger.error).toHaveBeenCalledWith( expect.stringContaining("changeset pre enter"), @@ -88,7 +89,7 @@ describe("enterPre", () => { }), }); - await pre(cwd, { command: "enter", tag: "next" }); + await pre({ cwd, command: "enter", tag: "next" }); expect( JSON.parse( await fs.readFile(path.join(cwd, ".changeset", "pre.json"), "utf8"), @@ -124,7 +125,7 @@ describe("exitPre", () => { tag: "next", }), }); - await pre(cwd, { command: "exit" }); + await pre({ cwd, command: "exit" }); expect( JSON.parse( @@ -145,8 +146,9 @@ describe("exitPre", () => { workspaces: ["packages/*"], }), "package-lock.json": "", + ".changeset/config.json": JSON.stringify({}), }); - await expect(pre(cwd, { command: "exit" })).rejects.toBeInstanceOf( + await expect(pre({ cwd, command: "exit" })).rejects.toBeInstanceOf( ExitError, ); expect(mockedLogger.error).toHaveBeenCalledWith( diff --git a/packages/cli/src/commands/pre/index.ts b/packages/cli/src/commands/pre/index.ts index f0748d637..44e1309d0 100644 --- a/packages/cli/src/commands/pre/index.ts +++ b/packages/cli/src/commands/pre/index.ts @@ -6,16 +6,32 @@ import { } from "@changesets/errors"; import { exitPre, enterPre } from "@changesets/pre"; import { log } from "@clack/prompts"; +import { getPackages } from "@manypkg/get-packages"; +import { ensureChangesetFolder } from "../shared.ts"; + +export type PreOptions = PreOptionsEnter | PreOptionsExit; + +interface PreOptionsEnter extends PreOptionsBase { + command: "enter"; + tag: string; +} + +interface PreOptionsExit extends PreOptionsBase { + command: "exit"; +} + +interface PreOptionsBase { + cwd?: string; +} + +export async function pre(options: PreOptions) { + const cwd = options.cwd ?? process.cwd(); + const packages = await getPackages(cwd); + await ensureChangesetFolder(packages.rootDir); -export async function pre( - rootDir: string, - options: - | { command: "enter"; tag: string } - | { command: "exit"; tag?: string }, -) { if (options.command === "enter") { try { - await enterPre(rootDir, options.tag); + await enterPre(packages.rootDir, options.tag); log.success( ` Entered pre mode with tag ${c.green(options.tag)}! @@ -36,7 +52,7 @@ If you're trying to exit pre mode, run ${c.cyan("changeset pre exit")}. } } else { try { - await exitPre(rootDir); + await exitPre(packages.rootDir); log.success( ` Exited pre mode! diff --git a/packages/cli/src/commands/publish/__tests__/index.test.ts b/packages/cli/src/commands/publish/__tests__/index.test.ts index 412255e69..67a07ab10 100644 --- a/packages/cli/src/commands/publish/__tests__/index.test.ts +++ b/packages/cli/src/commands/publish/__tests__/index.test.ts @@ -27,11 +27,12 @@ describe("Publish command", () => { version: "1.0.0", }), ".changeset/pre.json": JSON.stringify({ + ...modifiedDefaultConfig, mode: "pre", }), }); await expect( - publishCommand(cwd, { tag: "experimental" }, modifiedDefaultConfig), + publishCommand({ cwd, tag: "experimental" }), ).rejects.toThrowError(); }); }); diff --git a/packages/cli/src/commands/publish/__tests__/releaseCommand.test.ts b/packages/cli/src/commands/publish/__tests__/releaseCommand.test.ts deleted file mode 100644 index 2b3257f2e..000000000 --- a/packages/cli/src/commands/publish/__tests__/releaseCommand.test.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { defaultConfig } from "@changesets/config"; -import * as git from "@changesets/git"; -import { silenceLogsInBlock, testdir } from "@changesets/test-utils"; -import { afterEach, describe, expect, it, vi } from "vitest"; -import { publish as runRelease } from "../index.ts"; -import { publishPackages } from "../publishPackages.ts"; - -vi.mock("../../../utils/cli-utilities"); -vi.mock("@changesets/git"); -const mockGit = vi.mocked(git); -vi.mock("../publishPackages"); -const mockedPublishPackages = vi.mocked(publishPackages); - -mockGit.tag.mockImplementation(async () => true); - -describe("running release", () => { - silenceLogsInBlock(); - - afterEach(() => { - vi.clearAllMocks(); - }); - - describe("When there is no changeset commits", () => { - // we make sure we still do this so that a later build can clean up after a previously - // failed one (where the change was pushed back but not released and the next build has no - // changeset commits) - it("should still run publishPackages", async () => { - const cwd = await testdir({ - "package.json": JSON.stringify({ - private: true, - workspaces: ["packages/*"], - }), - "package-lock.json": "", - "packages/pkg-a/package.json": JSON.stringify({ - name: "pkg-a", - version: "1.0.0", - dependencies: { - "pkg-b": "1.0.0", - }, - }), - "packages/pkg-b/package.json": JSON.stringify({ - name: "pkg-b", - version: "1.0.0", - }), - }); - - mockedPublishPackages.mockImplementation(async () => [ - { name: "pkg-a", newVersion: "1.1.0", result: "published" }, - { name: "pkg-b", newVersion: "1.0.1", result: "published" }, - ]); - - await runRelease(cwd, {}, defaultConfig); - - expect(publishPackages).toHaveBeenCalled(); - }); - }); -}); diff --git a/packages/cli/src/commands/publish/index.ts b/packages/cli/src/commands/publish/index.ts index e86322bf7..34658525b 100644 --- a/packages/cli/src/commands/publish/index.ts +++ b/packages/cli/src/commands/publish/index.ts @@ -1,12 +1,14 @@ import c from "@changesets/color"; +import { read } from "@changesets/config"; import { ExitError } from "@changesets/errors"; import * as git from "@changesets/git"; import { readPreState } from "@changesets/pre"; -import type { Config, PreState } from "@changesets/types"; +import type { PreState } from "@changesets/types"; import { log, spinner } from "@clack/prompts"; import { getPackages } from "@manypkg/get-packages"; import { importantWarning } from "../../utils/cli-utilities.ts"; import { getUntaggedPackages } from "../../utils/getUntaggedPackages.ts"; +import { ensureChangesetFolder } from "../shared.ts"; import { publishPackages } from "./publishPackages.ts"; function formatPackageList( @@ -32,13 +34,22 @@ ${c.red("except")} for packages that have not had normal releases, which will be } } -export async function publish( - cwd: string, - { otp, tag, gitTag = true }: { otp?: string; tag?: string; gitTag?: boolean }, - config: Config, -) { - const releaseTag = tag && tag.length > 0 ? tag : undefined; - const preState = await readPreState(cwd); +export interface PublishOptions { + cwd?: string; + otp?: string; + tag?: string; + gitTag?: boolean; +} + +export async function publish(options?: PublishOptions) { + const cwd = options?.cwd ?? process.cwd(); + + const packages = await getPackages(cwd); + await ensureChangesetFolder(packages.rootDir); + + const releaseTag = + options?.tag && options.tag.length > 0 ? options.tag : undefined; + const preState = await readPreState(packages.rootDir); if (releaseTag && preState && preState.mode === "pre") { log.error( @@ -51,27 +62,31 @@ To resolve this exit the pre mode by running ${c.cyan("changeset pre exit")}. } if (releaseTag || preState) { - showNonLatestTagWarning(tag, preState); + showNonLatestTagWarning(options?.tag, preState); } - const { packages, tool } = await getPackages(cwd); + const config = await read(packages.rootDir, packages); const tagPrivatePackages = config.privatePackages && config.privatePackages.tag; const publishedPackages = await publishPackages({ - packages, + packages: packages.packages, // if not public, we won't pass the access, and it works as normal access: config.access, - otp, + otp: options?.otp, preState, tag: releaseTag, }); - const privatePackages = packages.filter( + const privatePackages = packages.packages.filter( (pkg) => pkg.packageJson.private && pkg.packageJson.version, ); const untaggedPrivatePackageReleases = tagPrivatePackages - ? await getUntaggedPackages(privatePackages, cwd, tool) + ? await getUntaggedPackages( + privatePackages, + packages.rootDir, + packages.tool, + ) : []; if ( @@ -99,12 +114,16 @@ ${formatPackageList(successfulNpmPublishes)} // We create the tags after the push above so that we know that HEAD won't change and that pushing // won't suffer from a race condition if another merge happens in the mean time (pushing tags won't // fail if we are behind the base branch). - if (gitTag) { + if (options?.gitTag) { const p = spinner(); p.start( `Creating git tag${successfulNpmPublishes.length > 1 ? "s" : ""}...`, ); - await tagPublish(tool.type, successfulNpmPublishes, cwd); + await tagPublish( + packages.tool.type, + successfulNpmPublishes, + packages.rootDir, + ); p.stop(`Created git tag${successfulNpmPublishes.length > 1 ? "s" : ""}.`); } } @@ -121,7 +140,11 @@ ${formatPackageList(untaggedPrivatePackageReleases, c.yellowBright)} p.start( `Creating git tag${untaggedPrivatePackageReleases.length > 1 ? "s" : ""}...`, ); - await tagPublish(tool.type, untaggedPrivatePackageReleases, cwd); + await tagPublish( + packages.tool.type, + untaggedPrivatePackageReleases, + packages.rootDir, + ); p.stop( `Created git tag${untaggedPrivatePackageReleases.length > 1 ? "s" : ""}.`, ); diff --git a/packages/cli/src/commands/shared.ts b/packages/cli/src/commands/shared.ts new file mode 100644 index 000000000..e0027ec89 --- /dev/null +++ b/packages/cli/src/commands/shared.ts @@ -0,0 +1,20 @@ +import fs from "node:fs/promises"; +import path from "node:path"; +import c from "@changesets/color"; +import { ExitError } from "@changesets/errors"; +import { log } from "@clack/prompts"; + +export async function ensureChangesetFolder(rootDir: string) { + try { + await fs.access(path.resolve(rootDir, ".changeset")); + } catch { + log.error( + ` + There is no .changeset folder. + If this is the first time ${c.green("Changesets")} have been used in this project, run ${c.cyan("changeset init")} to get set up. + 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. + `.trim(), + ); + throw new ExitError(1); + } +} diff --git a/packages/cli/src/commands/status/__tests__/status.test.ts b/packages/cli/src/commands/status/__tests__/status.test.ts index eaea2e81d..9d4fc5c9d 100644 --- a/packages/cli/src/commands/status/__tests__/status.test.ts +++ b/packages/cli/src/commands/status/__tests__/status.test.ts @@ -1,20 +1,13 @@ import fs from "node:fs/promises"; import path from "node:path"; -import { read } from "@changesets/config"; import * as git from "@changesets/git"; import { gitdir, outputFile, silenceLogsInBlock } from "@changesets/test-utils"; import type { ReleasePlan } from "@changesets/types"; import { writeChangeset } from "@changesets/write"; -import { getPackages } from "@manypkg/get-packages"; import { exec } from "tinyexec"; import { afterEach, describe, expect, it, vi } from "vitest"; import { status } from "../index.ts"; -async function readConfig(cwd: string) { - const packages = await getPackages(cwd); - return read(cwd, packages); -} - function replaceHumanIds(releaseObj: ReleasePlan | undefined) { if (!releaseObj) { return; @@ -82,11 +75,7 @@ describe("status", () => { await git.add(".", cwd); await git.commit("updated a", cwd); - const releaseObj = await status( - cwd, - { since: "main" }, - await readConfig(cwd), - ); + const releaseObj = await status({ cwd, since: "main" }); expect(replaceHumanIds(releaseObj)).toMatchInlineSnapshot(` { "changesets": [ @@ -152,11 +141,7 @@ describe("status", () => { await git.add(".", cwd); await git.commit("updated a", cwd); - const releaseObj = await status( - cwd, - { since: undefined }, - await readConfig(cwd), - ); + const releaseObj = await status({ cwd }); expect(replaceHumanIds(releaseObj)).toMatchInlineSnapshot(` { "changesets": [ @@ -214,7 +199,7 @@ describe("status", () => { await git.add(".", cwd); await git.commit("updated a", cwd); - const promise = status(cwd, { since: "main" }, await readConfig(cwd)); + const promise = status({ cwd, since: "main" }); await expect(promise).rejects.toThrow(); }); @@ -239,11 +224,7 @@ describe("status", () => { nodeOptions: { cwd }, }); - const releaseObj = await status( - cwd, - { since: "main" }, - await readConfig(cwd), - ); + const releaseObj = await status({ cwd, since: "main" }); expect(process.exit).not.toHaveBeenCalled(); expect(releaseObj).toEqual({ @@ -289,7 +270,7 @@ describe("status", () => { await git.add(".", cwd); await git.commit("updated a", cwd); - await status(cwd, { since: "main" }, await readConfig(cwd)); + await status({ cwd, since: "main" }); expect(process.exit).not.toHaveBeenCalled(); }); @@ -333,11 +314,7 @@ describe("status", () => { const output = "nonsense.json"; - const probsUndefined = await status( - cwd, - { since: "main", output }, - await readConfig(cwd), - ); + const probsUndefined = await status({ cwd, since: "main", output }); const releaseObj = await fs.readFile(path.join(cwd, output), "utf8"); @@ -402,11 +379,7 @@ describe("status", () => { await git.add(".", cwd); await git.commit("add unrelated thing", cwd); - const releaseObj = await status( - cwd, - { since: "main" }, - await readConfig(cwd), - ); + const releaseObj = await status({ cwd, since: "main" }); expect(process.exit).not.toHaveBeenCalled(); expect(releaseObj).toEqual({ @@ -446,7 +419,7 @@ describe("status", () => { await git.add(".", cwd); await git.commit("updated a", cwd); - const promise = status(cwd, { since: "main" }, await readConfig(cwd)); + const promise = status({ cwd, since: "main" }); await expect(promise).rejects.toThrow(); }); @@ -486,11 +459,7 @@ describe("status", () => { await git.add(".", cwd); await git.commit("updated a", cwd); - const releaseObj = await status( - cwd, - { since: "main" }, - await readConfig(cwd), - ); + const releaseObj = await status({ cwd, since: "main" }); expect(replaceHumanIds(releaseObj)).toMatchInlineSnapshot(` { "changesets": [ @@ -557,11 +526,7 @@ describe("status", () => { await git.add(".", cwd); await git.commit("updated b", cwd); - const releaseObj = await status( - cwd, - { since: "main" }, - await readConfig(cwd), - ); + const releaseObj = await status({ cwd, since: "main" }); expect(process.exit).not.toHaveBeenCalled(); expect(releaseObj).toEqual({ @@ -610,11 +575,7 @@ describe("status", () => { await git.add(".", cwd); await git.commit("updated b", cwd); - const releaseObj = await status( - cwd, - { since: "main" }, - await readConfig(cwd), - ); + const releaseObj = await status({ cwd, since: "main" }); expect(process.exit).not.toHaveBeenCalled(); expect(releaseObj).toEqual({ diff --git a/packages/cli/src/commands/status/index.ts b/packages/cli/src/commands/status/index.ts index 7b7c89192..5fb362b81 100644 --- a/packages/cli/src/commands/status/index.ts +++ b/packages/cli/src/commands/status/index.ts @@ -1,29 +1,37 @@ import fs from "node:fs/promises"; import path from "node:path"; import c from "@changesets/color"; +import { read } from "@changesets/config"; import { ExitError } from "@changesets/errors"; import { getReleasePlan } from "@changesets/get-release-plan"; -import type { ComprehensiveRelease, Config } from "@changesets/types"; +import type { ComprehensiveRelease } from "@changesets/types"; import { log } from "@clack/prompts"; +import { getPackages } from "@manypkg/get-packages"; import { getVersionableChangedPackages } from "../../utils/versionablePackages.ts"; +import { ensureChangesetFolder } from "../shared.ts"; -export async function status( - cwd: string, - { - since, - verbose, - output, - }: { - since?: string; - verbose?: boolean; - output?: string; - }, - config: Config, -) { - const releasePlan = await getReleasePlan(cwd, since, config); +export interface StatusOptions { + cwd?: string; + since?: string; + verbose?: boolean; + output?: string; +} + +export async function status(options?: StatusOptions) { + const cwd = options?.cwd ?? process.cwd(); + + const packages = await getPackages(cwd); + await ensureChangesetFolder(packages.rootDir); + const config = await read(packages.rootDir, packages); + + const releasePlan = await getReleasePlan( + packages.rootDir, + options?.since, + config, + ); const changedPackages = await getVersionableChangedPackages(config, { - cwd, - ref: since, + cwd: packages.rootDir, + ref: options?.since, }); if (changedPackages.length > 0 && releasePlan.changesets.length === 0) { @@ -36,9 +44,9 @@ If this change doesn't need a release, run ${c.cyan("changeset add --empty")}. throw new ExitError(1); } - if (output) { + if (options?.output) { await fs.writeFile( - path.resolve(cwd, output), + path.resolve(cwd, options.output), JSON.stringify(releasePlan, undefined, 2), ); return; @@ -46,7 +54,7 @@ If this change doesn't need a release, run ${c.cyan("changeset add --empty")}. printStatus( releasePlan.releases.toSorted((a, b) => a.name.localeCompare(b.name)), - verbose, + options?.verbose, ); return releasePlan; diff --git a/packages/cli/src/commands/tag/__tests__/index.test.ts b/packages/cli/src/commands/tag/__tests__/index.test.ts index 92c9d4a7a..9e9bb7f4b 100644 --- a/packages/cli/src/commands/tag/__tests__/index.test.ts +++ b/packages/cli/src/commands/tag/__tests__/index.test.ts @@ -1,16 +1,10 @@ -import { read } from "@changesets/config"; import * as git from "@changesets/git"; import { silenceLogsInBlock, testdir } from "@changesets/test-utils"; -import { getPackages } from "@manypkg/get-packages"; import { describe, expect, it, type Mock, vi } from "vitest"; import { tag } from "../index.ts"; vi.mock("@changesets/git"); -async function readConfig(cwd: string) { - return read(cwd, await getPackages(cwd)); -} - describe("tag command", () => { silenceLogsInBlock(); @@ -39,7 +33,7 @@ describe("tag command", () => { (git.getAllTags as Mock).mockReturnValue(new Set()); expect(git.tag).not.toHaveBeenCalled(); - await tag(cwd, await readConfig(cwd)); + await tag({ cwd }); expect(git.tag).toHaveBeenCalledTimes(2); expect((git.tag as Mock).mock.calls[0][0]).toEqual("pkg-a@1.0.0"); expect((git.tag as Mock).mock.calls[1][0]).toEqual("pkg-b@1.0.0"); @@ -74,7 +68,7 @@ describe("tag command", () => { ); expect(git.tag).not.toHaveBeenCalled(); - await tag(cwd, await readConfig(cwd)); + await tag({ cwd }); expect(git.tag).toHaveBeenCalledOnce(); expect((git.tag as Mock).mock.calls[0][0]).toEqual("pkg-b@1.0.0"); }); @@ -98,7 +92,7 @@ describe("tag command", () => { (git.getAllTags as Mock).mockReturnValue(new Set()); expect(git.tag).not.toHaveBeenCalled(); - await tag(cwd, await readConfig(cwd)); + await tag({ cwd }); expect(git.tag).toHaveBeenCalledOnce(); expect((git.tag as Mock).mock.calls[0][0]).toEqual("v1.0.0"); }); @@ -115,7 +109,7 @@ describe("tag command", () => { (git.getAllTags as Mock).mockReturnValue(new Set()); expect(git.tag).not.toHaveBeenCalled(); - await tag(cwd, await readConfig(cwd)); + await tag({ cwd }); expect(git.tag).toHaveBeenCalledTimes(0); }); }); diff --git a/packages/cli/src/commands/tag/index.ts b/packages/cli/src/commands/tag/index.ts index bae134dd3..4379c290e 100644 --- a/packages/cli/src/commands/tag/index.ts +++ b/packages/cli/src/commands/tag/index.ts @@ -1,10 +1,11 @@ import c from "@changesets/color"; +import { read } from "@changesets/config"; import * as git from "@changesets/git"; import { shouldSkipPackage } from "@changesets/should-skip-package"; -import type { Config } from "@changesets/types"; import { log, progress } from "@clack/prompts"; import { getPackages, type Tool } from "@manypkg/get-packages"; import { getUntaggedPackages } from "../../utils/getUntaggedPackages.ts"; +import { ensureChangesetFolder } from "../shared.ts"; function buildTag(tool: Tool, pkg: { name: string; newVersion: string }) { return tool.type !== "root" @@ -21,12 +22,19 @@ function buildTagMessage( : c.cyan(`v${pkg.newVersion}`); } -export async function tag(cwd: string, config: Config) { - const { packages, tool } = await getPackages(cwd); +export interface TagOptions { + cwd?: string; +} + +export async function tag(options?: TagOptions) { + const cwd = options?.cwd ?? process.cwd(); + const packages = await getPackages(cwd); + await ensureChangesetFolder(packages.rootDir); + const config = await read(packages.rootDir, packages); - const allExistingTags = await git.getAllTags(cwd); + const allExistingTags = await git.getAllTags(packages.rootDir); - const taggablePackages = packages.filter( + const taggablePackages = packages.packages.filter( (pkg) => !shouldSkipPackage(pkg, { ignore: config.ignore, @@ -36,11 +44,11 @@ export async function tag(cwd: string, config: Config) { const untaggedPackages = await getUntaggedPackages( taggablePackages, - cwd, - tool, + packages.rootDir, + packages.tool, ); const skippedTags = untaggedPackages.filter((pkg) => - allExistingTags.has(buildTag(tool, pkg)), + allExistingTags.has(buildTag(packages.tool, pkg)), ); if (untaggedPackages.length === 0) { log.info("Did not find any packages that need to be tagged."); @@ -51,24 +59,24 @@ export async function tag(cwd: string, config: Config) { p.start("Creating tags..."); for (const pkg of untaggedPackages) { - const tag = buildTag(tool, pkg); + const tag = buildTag(packages.tool, pkg); if (allExistingTags.has(tag)) continue; - await git.tag(tag, cwd); + await git.tag(tag, packages.rootDir); - p.advance(1, buildTagMessage(tool, pkg)); + p.advance(1, buildTagMessage(packages.tool, pkg)); } const lines = [ "Created tags:", untaggedPackages - .map((pkg) => ` - ${buildTagMessage(tool, pkg)}`) + .map((pkg) => ` - ${buildTagMessage(packages.tool, pkg)}`) .join(`\n`), ]; if (skippedTags.length !== 0) { lines.push( "Skipped tags (already exist):", - ...skippedTags.map((pkg) => buildTagMessage(tool, pkg)), + ...skippedTags.map((pkg) => buildTagMessage(packages.tool, pkg)), ); } diff --git a/packages/cli/src/commands/version/index.ts b/packages/cli/src/commands/version/index.ts index 58504623a..c16cd2cdd 100644 --- a/packages/cli/src/commands/version/index.ts +++ b/packages/cli/src/commands/version/index.ts @@ -3,6 +3,7 @@ import { fileURLToPath } from "node:url"; import { applyReleasePlan } from "@changesets/apply-release-plan"; import { assembleReleasePlan } from "@changesets/assemble-release-plan"; import c from "@changesets/color"; +import { read } from "@changesets/config"; import { ExitError } from "@changesets/errors"; import { getDependentsGraph } from "@changesets/get-dependents-graph"; import * as git from "@changesets/git"; @@ -14,16 +15,22 @@ import { log } from "@clack/prompts"; import { getPackages } from "@manypkg/get-packages"; import { getCommitFunctions } from "../../commit/getCommitFunctions.ts"; import { importantWarning } from "../../utils/cli-utilities.ts"; +import { ensureChangesetFolder } from "../shared.ts"; + +export interface VersionOptions { + cwd?: string; + ignore?: string[]; + snapshot?: string | boolean; + snapshotPrereleaseTemplate?: string; +} + +export async function version(options: VersionOptions) { + const cwd = options.cwd ?? process.cwd(); + + const packages = await getPackages(cwd); + await ensureChangesetFolder(packages.rootDir); + const config = await read(packages.rootDir, packages); -export async function version( - cwd: string, - options: { - ignore?: string[]; - snapshot?: string | boolean; - snapshotPrereleaseTemplate?: string; - }, - config: Config, -) { const messages: string[] = []; let ignore: readonly string[] | undefined; @@ -49,8 +56,6 @@ export async function version( commit: options.snapshot ? false : config.commit, }; - const packages = await getPackages(cwd); - validateIgnoredPackageNames(packages, options.ignore, messages); validateSkippedDependents(packages, releaseConfig, messages); diff --git a/packages/cli/src/commands/version/version.test.ts b/packages/cli/src/commands/version/version.test.ts index 5a981975f..94813a1a0 100644 --- a/packages/cli/src/commands/version/version.test.ts +++ b/packages/cli/src/commands/version/version.test.ts @@ -26,10 +26,6 @@ const modifiedDefaultConfig: Config = { changelog: ["@changesets/cli/changelog", null], }; -const defaultOptions = { - snapshot: undefined, -}; - vi.mock("human-id"); vi.mock("@changesets/git"); @@ -93,11 +89,9 @@ describe("running version in a simple project", () => { name: "pkg-a", version: "1.0.0", }), - ".changeset/config.json": JSON.stringify({}), + ".changeset/config.json": JSON.stringify(modifiedDefaultConfig), }); - await expect( - version(cwd, defaultOptions, modifiedDefaultConfig), - ).rejects.toThrow(ExitError); + await expect(version({ cwd })).rejects.toThrow(ExitError); expect(mockedLogger.warn).toHaveBeenCalledExactlyOnceWith( "No unreleased changesets found.", ); @@ -115,15 +109,12 @@ describe("running version in a simple project", () => { name: "pkg-a", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify(modifiedDefaultConfig), }); - await expect( - version( - cwd, - { ...defaultOptions, ignore: ["pkg-c"] }, - modifiedDefaultConfig, - ), - ).rejects.toThrow(ExitError); + await expect(version({ cwd, ignore: ["pkg-c"] })).rejects.toThrow( + ExitError, + ); expect(mockedLogger.error).toHaveBeenCalledOnce(); const arg = mockedLogger.error.mock.calls[0][0]; @@ -150,15 +141,12 @@ describe("running version in a simple project", () => { name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify(modifiedDefaultConfig), }); - await expect( - version( - cwd, - { ...defaultOptions, ignore: ["pkg-b"] }, - modifiedDefaultConfig, - ), - ).rejects.toThrow(ExitError); + await expect(version({ cwd, ignore: ["pkg-b"] })).rejects.toThrow( + ExitError, + ); expect(mockedLogger.error).toHaveBeenCalledOnce(); const arg = mockedLogger.error.mock.calls[0][0]; @@ -185,15 +173,15 @@ describe("running version in a simple project", () => { name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify({ + ...modifiedDefaultConfig, + ignore: ["pkg-a"], + }), }); - await expect( - version( - cwd, - { ...defaultOptions, ignore: ["pkg-b"] }, - { ...modifiedDefaultConfig, ignore: ["pkg-a"] }, - ), - ).rejects.toThrow(ExitError); + await expect(version({ cwd, ignore: ["pkg-b"] })).rejects.toThrow( + ExitError, + ); expect(mockedLogger.error).toHaveBeenCalledOnce(); const arg = mockedLogger.error.mock.calls[0][0]; @@ -221,6 +209,7 @@ describe("running version in a simple project", () => { name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify(modifiedDefaultConfig), }); await writeChangesets( [ @@ -235,7 +224,7 @@ describe("running version in a simple project", () => { cwd, ); - await version(cwd, defaultOptions, modifiedDefaultConfig); + await version({ cwd }); expect(await getPkgJSON("pkg-a", cwd)).toEqual( expect.objectContaining({ name: "pkg-a", version: "1.1.0" }), @@ -265,6 +254,10 @@ describe("running version in a simple project", () => { name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify({ + ...modifiedDefaultConfig, + ignore: ["pkg-a"], + }), }); await writeChangesets( [ @@ -276,10 +269,7 @@ describe("running version in a simple project", () => { cwd, ); - await version(cwd, defaultOptions, { - ...modifiedDefaultConfig, - ignore: ["pkg-a"], - }); + await version({ cwd }); // no change expect(await getPkgJSON("pkg-a", cwd)).toEqual({ @@ -310,6 +300,10 @@ describe("running version in a simple project", () => { name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify({ + ...modifiedDefaultConfig, + ignore: ["pkg-a"], + }), }); await writeChangesets( [ @@ -325,10 +319,7 @@ describe("running version in a simple project", () => { cwd, ); - await version(cwd, defaultOptions, { - ...modifiedDefaultConfig, - ignore: ["pkg-a"], - }); + await version({ cwd }); expect((await getPackages(cwd)).packages.map((x) => x.packageJson)) .toMatchInlineSnapshot(` @@ -360,6 +351,7 @@ describe("running version in a simple project", () => { name: "pkg-a", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify(modifiedDefaultConfig), }); await writeChangesets( [ @@ -372,7 +364,7 @@ describe("running version in a simple project", () => { ); const spy = vi.spyOn(git, "commit"); - await version(cwd, defaultOptions, modifiedDefaultConfig); + await version({ cwd }); expect(spy).not.toHaveBeenCalled(); }); @@ -396,6 +388,10 @@ describe("running version in a simple project", () => { name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify({ + ...modifiedDefaultConfig, + commit: ["@changesets/cli/commit", null], + }), }); await linkNodeModules(cwd); @@ -416,10 +412,7 @@ describe("running version in a simple project", () => { expect(spy).not.toHaveBeenCalled(); - await version(cwd, defaultOptions, { - ...modifiedDefaultConfig, - commit: ["@changesets/cli/commit", null], - }); + await version({ cwd }); expect(spy).toHaveBeenCalled(); @@ -466,6 +459,10 @@ describe("running version in a simple project", () => { name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify({ + ...modifiedDefaultConfig, + commit: ["@changesets/cli/commit", null], + }), }); await linkNodeModules(cwd); @@ -486,10 +483,7 @@ describe("running version in a simple project", () => { expect(spy).not.toHaveBeenCalled(); - await version(cwd, defaultOptions, { - ...modifiedDefaultConfig, - commit: ["@changesets/cli/commit", null], - }); + await version({ cwd }); expect(spy).toHaveBeenCalled(); expect(spy.mock.calls[0][0]).toMatchInlineSnapshot(` @@ -521,6 +515,7 @@ describe("running version in a simple project", () => { name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify(modifiedDefaultConfig), ".changeset/changesets-are-beautiful.md": `--- "pkg-a": minor --- @@ -535,7 +530,7 @@ Awesome feature, hidden behind a feature flag `, }); - await version(cwd, defaultOptions, modifiedDefaultConfig); + await version({ cwd }); const packages = await getPackages(cwd); expect(packages.packages.map((x) => x.packageJson)).toEqual([ @@ -575,6 +570,7 @@ Awesome feature, hidden behind a feature flag "pkg-a": "latest", }, }), + ".changeset/config.json": JSON.stringify(modifiedDefaultConfig), }); await writeChangeset( @@ -585,7 +581,7 @@ Awesome feature, hidden behind a feature flag cwd, ); - await version(cwd, defaultOptions, modifiedDefaultConfig); + await version({ cwd }); expect((await getPackages(cwd)).packages.map((x) => x.packageJson)) .toMatchInlineSnapshot(` @@ -617,6 +613,7 @@ Awesome feature, hidden behind a feature flag name: "pkg-a", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify(modifiedDefaultConfig), "packages/pkg-a/CHANGELOG.md": `# pkg-a ## 1.0.0 @@ -635,7 +632,7 @@ Awesome feature, hidden behind a feature flag cwd, ); - await version(cwd, defaultOptions, modifiedDefaultConfig); + await version({ cwd }); expect(await getChangelog("pkg-a", cwd)).toMatchInlineSnapshot(` "# pkg-a @@ -673,6 +670,7 @@ Awesome feature, hidden behind a feature flag name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify(modifiedDefaultConfig), }); await writeChangesets( [ @@ -691,7 +689,7 @@ Awesome feature, hidden behind a feature flag cwd, ); - await version(cwd, defaultOptions, modifiedDefaultConfig); + await version({ cwd }); expect(await getPkgJSON("pkg-a", cwd)).toEqual( expect.objectContaining({ name: "pkg-a", version: "1.1.0" }), @@ -719,6 +717,7 @@ Awesome feature, hidden behind a feature flag name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify(modifiedDefaultConfig), }); await writeChangesets( [ @@ -736,7 +735,7 @@ Awesome feature, hidden behind a feature flag ], cwd, ); - await version(cwd, defaultOptions, modifiedDefaultConfig); + await version({ cwd }); // first call should be minor bump expect(await getPkgJSON("pkg-a", cwd)).toEqual( @@ -771,7 +770,7 @@ Awesome feature, hidden behind a feature flag name: "pkg-b", version: "1.0.0", }), - ".changeset/config.json": JSON.stringify({}), + ".changeset/config.json": JSON.stringify(modifiedDefaultConfig), }); await writeChangesets( @@ -794,7 +793,7 @@ Awesome feature, hidden behind a feature flag 3, ); - await version(cwd, defaultOptions, modifiedDefaultConfig); + await version({ cwd }); expect((await fs.readdir(path.resolve(cwd, ".changeset"))).length).toBe( 1, ); @@ -822,6 +821,10 @@ describe("fixed", () => { name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify({ + ...modifiedDefaultConfig, + fixed: [["pkg-a", "pkg-b"]], + }), }); await writeChangesets( [ @@ -833,10 +836,7 @@ describe("fixed", () => { cwd, ); - await version(cwd, defaultOptions, { - ...modifiedDefaultConfig, - fixed: [["pkg-a", "pkg-b"]], - }); + await version({ cwd }); expect(await getPkgJSON("pkg-a", cwd)).toEqual( expect.objectContaining({ name: "pkg-a", version: "1.1.0" }), @@ -865,6 +865,11 @@ describe("fixed", () => { name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify({ + ...modifiedDefaultConfig, + fixed: [["pkg-a", "pkg-b"]], + ignore: ["pkg-a"], + }), }); await writeChangesets( [ @@ -876,11 +881,7 @@ describe("fixed", () => { cwd, ); - await version(cwd, defaultOptions, { - ...modifiedDefaultConfig, - fixed: [["pkg-a", "pkg-b"]], - ignore: ["pkg-a"], - }); + await version({ cwd }); expect((await getPackages(cwd)).packages.map((x) => x.packageJson)) .toMatchInlineSnapshot(` @@ -919,6 +920,10 @@ describe("fixed", () => { name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify({ + ...modifiedDefaultConfig, + fixed: [["pkg-a", "pkg-b"]], + }), }); await writeChangesets( @@ -931,10 +936,7 @@ describe("fixed", () => { cwd, ); - await version(cwd, defaultOptions, { - ...modifiedDefaultConfig, - fixed: [["pkg-a", "pkg-b"]], - }); + await version({ cwd }); expect(await getChangelog("pkg-a", cwd)).toMatchInlineSnapshot(` "# pkg-a @@ -966,10 +968,7 @@ describe("fixed", () => { cwd, ); - await version(cwd, defaultOptions, { - ...modifiedDefaultConfig, - fixed: [["pkg-a", "pkg-b"]], - }); + await version({ cwd }); expect(await getChangelog("pkg-a", cwd)).toMatchInlineSnapshot(` "# pkg-a @@ -1024,6 +1023,10 @@ describe("linked", () => { name: "pkg-b", version: "0.1.0", }), + ".changeset/config.json": JSON.stringify({ + ...modifiedDefaultConfig, + linked: [["pkg-a", "pkg-b"]], + }), }); await writeChangesets( [ @@ -1038,10 +1041,7 @@ describe("linked", () => { cwd, ); - await version(cwd, defaultOptions, { - ...modifiedDefaultConfig, - linked: [["pkg-a", "pkg-b"]], - }); + await version({ cwd }); expect(await getPkgJSON("pkg-a", cwd)).toEqual( expect.objectContaining({ name: "pkg-a", version: "1.1.0" }), @@ -1070,6 +1070,10 @@ describe("linked", () => { name: "pkg-b", version: "0.1.0", }), + ".changeset/config.json": JSON.stringify({ + ...modifiedDefaultConfig, + linked: [["pkg-a", "pkg-b"]], + }), }); await writeChangesets( [ @@ -1081,10 +1085,7 @@ describe("linked", () => { cwd, ); - await version(cwd, defaultOptions, { - ...modifiedDefaultConfig, - linked: [["pkg-1", "pkg-2"]], - }); + await version({ cwd }); expect(await getPkgJSON("pkg-a", cwd)).toEqual( expect.objectContaining({ name: "pkg-a", version: "1.1.0" }), @@ -1112,6 +1113,10 @@ describe("workspace range", () => { name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify({ + ...modifiedDefaultConfig, + commit: false, + }), }); await writeChangesets( @@ -1126,7 +1131,7 @@ describe("workspace range", () => { ], cwd, ); - await version(cwd, defaultOptions, modifiedDefaultConfig); + await version({ cwd }); const packages = await getPackages(cwd); expect(packages.packages.map((x) => x.packageJson)).toEqual([ @@ -1163,6 +1168,7 @@ describe("workspace range", () => { name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify(modifiedDefaultConfig), }); await writeChangeset( @@ -1172,7 +1178,7 @@ describe("workspace range", () => { }, cwd, ); - await version(cwd, defaultOptions, modifiedDefaultConfig); + await version({ cwd }); const packages = await getPackages(cwd); expect(packages.packages.map((x) => x.packageJson)).toEqual([ @@ -1209,6 +1215,7 @@ describe("workspace range", () => { name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify(modifiedDefaultConfig), }); await writeChangeset( @@ -1218,7 +1225,7 @@ describe("workspace range", () => { }, cwd, ); - await version(cwd, defaultOptions, modifiedDefaultConfig); + await version({ cwd }); const packages = await getPackages(cwd); expect(packages.packages.map((x) => x.packageJson)).toEqual([ @@ -1255,6 +1262,7 @@ describe("workspace range", () => { name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify(modifiedDefaultConfig), }); await writeChangeset( @@ -1264,7 +1272,7 @@ describe("workspace range", () => { }, cwd, ); - await version(cwd, defaultOptions, modifiedDefaultConfig); + await version({ cwd }); const packages = await getPackages(cwd); expect(packages.packages.map((x) => x.packageJson)).toEqual([ @@ -1299,6 +1307,13 @@ describe("workspace range", () => { version: "1.0.0", dependencies: { "pkg-a": "workspace:*" }, }), + ".changeset/config.json": JSON.stringify({ + ...modifiedDefaultConfig, + ___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH: { + ...defaultConfig.___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH, + updateInternalDependents: "always", + }, + }), }); await writeChangeset( @@ -1308,13 +1323,7 @@ describe("workspace range", () => { }, cwd, ); - await version(cwd, defaultOptions, { - ...modifiedDefaultConfig, - ___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH: { - ...defaultConfig.___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH, - updateInternalDependents: "always", - }, - }); + await version({ cwd }); expect(await getChangelog("pkg-a", cwd)).toMatchInlineSnapshot(` "# pkg-a @@ -1355,6 +1364,7 @@ describe("workspace range", () => { version: "1.0.0", peerDependencies: { "pkg-a": "workspace:^" }, }), + ".changeset/config.json": JSON.stringify(modifiedDefaultConfig), }); await writeChangeset( @@ -1364,7 +1374,7 @@ describe("workspace range", () => { }, cwd, ); - await version(cwd, defaultOptions, modifiedDefaultConfig); + await version({ cwd }); expect(await getChangelog("pkg-a", cwd)).toMatchInlineSnapshot(` "# pkg-a @@ -1405,6 +1415,13 @@ describe("workspace range", () => { version: "1.0.0", peerDependencies: { "pkg-a": "workspace:^" }, }), + ".changeset/config.json": JSON.stringify({ + ...modifiedDefaultConfig, + ___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH: { + ...defaultConfig.___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH, + onlyUpdatePeerDependentsWhenOutOfRange: true, + }, + }), }); await writeChangeset( @@ -1414,13 +1431,7 @@ describe("workspace range", () => { }, cwd, ); - await version(cwd, defaultOptions, { - ...modifiedDefaultConfig, - ___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH: { - ...defaultConfig.___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH, - onlyUpdatePeerDependentsWhenOutOfRange: true, - }, - }); + await version({ cwd }); expect(await getChangelog("pkg-a", cwd)).toMatchInlineSnapshot(` "# pkg-a @@ -1460,6 +1471,7 @@ describe("same package in different dependency types", () => { name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify(modifiedDefaultConfig), }); await writeChangeset( { @@ -1474,7 +1486,7 @@ describe("same package in different dependency types", () => { cwd, ); - await version(cwd, defaultOptions, modifiedDefaultConfig); + await version({ cwd }); const packages = await getPackages(cwd); expect(packages.packages.map((x) => x.packageJson)).toEqual([ @@ -1516,6 +1528,10 @@ describe("snapshot release", () => { name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify({ + ...modifiedDefaultConfig, + commit: false, + }), }); await writeChangesets( [ @@ -1529,16 +1545,7 @@ describe("snapshot release", () => { ], cwd, ); - await version( - cwd, - { - snapshot: "experimental", - }, - { - ...modifiedDefaultConfig, - commit: false, - }, - ); + await version({ cwd, snapshot: "experimental" }); expect(await getPkgJSON("pkg-a", cwd)).toEqual( expect.objectContaining({ name: "pkg-a", @@ -1573,6 +1580,10 @@ describe("snapshot release", () => { name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify({ + ...modifiedDefaultConfig, + commit: ["@changesets/cli/commit", null], + }), }); await writeChangesets( [ @@ -1590,16 +1601,7 @@ describe("snapshot release", () => { expect(spy).not.toHaveBeenCalled(); - await version( - cwd, - { - snapshot: "experimental", - }, - { - ...modifiedDefaultConfig, - commit: ["@changesets/cli/commit", null], - }, - ); + await version({ cwd, snapshot: "experimental" }); expect(spy).not.toHaveBeenCalled(); }); @@ -1616,6 +1618,7 @@ describe("snapshot release", () => { name: "pkg-a", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify(modifiedDefaultConfig), }); await writeChangeset( { @@ -1625,13 +1628,7 @@ describe("snapshot release", () => { cwd, ); - await version( - cwd, - { - snapshot: true, - }, - modifiedDefaultConfig, - ); + await version({ cwd, snapshot: true }); expect((await getPackages(cwd)).packages.map((x) => x.packageJson)) .toMatchInlineSnapshot(` @@ -1665,6 +1662,10 @@ describe("snapshot release", () => { name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify({ + ...modifiedDefaultConfig, + ignore: ["pkg-a"], + }), }); await writeChangeset( { @@ -1674,16 +1675,7 @@ describe("snapshot release", () => { cwd, ); - await version( - cwd, - { - snapshot: true, - }, - { - ...modifiedDefaultConfig, - ignore: ["pkg-a"], - }, - ); + await version({ cwd, snapshot: true }); expect((await getPackages(cwd)).packages.map((x) => x.packageJson)) .toMatchInlineSnapshot(` @@ -1722,6 +1714,14 @@ describe("snapshot release", () => { name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify({ + ...modifiedDefaultConfig, + commit: false, + snapshot: { + ...modifiedDefaultConfig.snapshot, + prereleaseTemplate: `{tag}.{commit}`, + }, + }), }); await writeChangesets( [ @@ -1736,20 +1736,7 @@ describe("snapshot release", () => { cwd, ); - await expect( - version( - cwd, - { snapshot: true }, - { - ...modifiedDefaultConfig, - commit: false, - snapshot: { - ...modifiedDefaultConfig.snapshot, - prereleaseTemplate: `{tag}.{commit}`, - }, - }, - ), - ).rejects.toThrow( + await expect(version({ cwd, snapshot: true })).rejects.toThrow( 'Failed to compose snapshot version: "{tag}" placeholder is used without having a value defined!', ); }); @@ -1772,6 +1759,14 @@ describe("snapshot release", () => { name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify({ + ...modifiedDefaultConfig, + commit: false, + snapshot: { + ...modifiedDefaultConfig.snapshot, + prereleaseTemplate: `{commit}`, + }, + }), }); await writeChangesets( [ @@ -1786,20 +1781,7 @@ describe("snapshot release", () => { cwd, ); - await expect( - version( - cwd, - { snapshot: "test" }, - { - ...modifiedDefaultConfig, - commit: false, - snapshot: { - ...modifiedDefaultConfig.snapshot, - prereleaseTemplate: `{commit}`, - }, - }, - ), - ).rejects.toThrow( + await expect(version({ cwd, snapshot: "test" })).rejects.toThrow( "Failed to compose snapshot version: \"{tag}\" placeholder is missing, but the snapshot parameter is defined (value: 'test')", ); }); @@ -1844,6 +1826,14 @@ describe("snapshot release", () => { name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify({ + ...modifiedDefaultConfig, + commit: false, + snapshot: { + ...modifiedDefaultConfig.snapshot, + prereleaseTemplate: snapshotTemplate as string, + }, + }), }); await writeChangesets( [ @@ -1857,18 +1847,7 @@ describe("snapshot release", () => { ], cwd, ); - await version( - cwd, - { snapshot: snapshotValue }, - { - ...modifiedDefaultConfig, - commit: false, - snapshot: { - ...modifiedDefaultConfig.snapshot, - prereleaseTemplate: snapshotTemplate as string, - }, - }, - ); + await version({ cwd, snapshot: snapshotValue }); expect(await getPkgJSON("pkg-a", cwd)).toEqual( expect.objectContaining({ @@ -1906,6 +1885,14 @@ describe("snapshot release", () => { name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify({ + ...modifiedDefaultConfig, + commit: false, + snapshot: { + useCalculatedVersion: true, + prereleaseTemplate: null, + }, + }), }); await writeChangesets( [ @@ -1919,20 +1906,7 @@ describe("snapshot release", () => { ], cwd, ); - await version( - cwd, - { - snapshot: "experimental", - }, - { - ...modifiedDefaultConfig, - commit: false, - snapshot: { - useCalculatedVersion: true, - prereleaseTemplate: null, - }, - }, - ); + await version({ cwd, snapshot: "experimental" }); expect(await getPkgJSON("pkg-a", cwd)).toEqual( expect.objectContaining({ name: "pkg-a", @@ -1959,6 +1933,13 @@ describe("snapshot release", () => { name: "pkg-a", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify({ + ...modifiedDefaultConfig, + snapshot: { + useCalculatedVersion: true, + prereleaseTemplate: null, + }, + }), }); await writeChangeset( { @@ -1968,19 +1949,7 @@ describe("snapshot release", () => { cwd, ); - await version( - cwd, - { - snapshot: true, - }, - { - ...modifiedDefaultConfig, - snapshot: { - useCalculatedVersion: true, - prereleaseTemplate: null, - }, - }, - ); + await version({ cwd, snapshot: true }); expect((await getPackages(cwd)).packages.map((x) => x.packageJson)) .toMatchInlineSnapshot(` @@ -2013,6 +1982,14 @@ describe("snapshot release", () => { name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify({ + ...modifiedDefaultConfig, + ignore: ["pkg-a"], + snapshot: { + useCalculatedVersion: true, + prereleaseTemplate: null, + }, + }), }); await writeChangeset( { @@ -2022,20 +1999,7 @@ describe("snapshot release", () => { cwd, ); - await version( - cwd, - { - snapshot: true, - }, - { - ...modifiedDefaultConfig, - ignore: ["pkg-a"], - snapshot: { - useCalculatedVersion: true, - prereleaseTemplate: null, - }, - }, - ); + await version({ cwd, snapshot: true }); expect((await getPackages(cwd)).packages.map((x) => x.packageJson)) .toMatchInlineSnapshot(` @@ -2077,6 +2041,13 @@ describe("updateInternalDependents: always", () => { name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify({ + ...modifiedDefaultConfig, + ___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH: { + ...defaultConfig.___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH, + updateInternalDependents: "always", + }, + }), }); await writeChangeset( { @@ -2085,13 +2056,7 @@ describe("updateInternalDependents: always", () => { }, cwd, ); - await version(cwd, defaultOptions, { - ...modifiedDefaultConfig, - ___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH: { - ...defaultConfig.___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH, - updateInternalDependents: "always", - }, - }); + await version({ cwd }); expect(await getPkgJSON("pkg-a", cwd)).toEqual( expect.objectContaining({ @@ -2151,6 +2116,13 @@ describe("updateInternalDependents: always", () => { version: "1.0.0", dependencies: { "pkg-b": "1.0.0" }, }), + ".changeset/config.json": JSON.stringify({ + ...modifiedDefaultConfig, + ___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH: { + ...defaultConfig.___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH, + updateInternalDependents: "always", + }, + }), }); await writeChangeset( @@ -2160,13 +2132,7 @@ describe("updateInternalDependents: always", () => { }, cwd, ); - await version(cwd, defaultOptions, { - ...modifiedDefaultConfig, - ___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH: { - ...defaultConfig.___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH, - updateInternalDependents: "always", - }, - }); + await version({ cwd }); expect(await getPkgJSON("pkg-a", cwd)).toEqual( expect.objectContaining({ @@ -2229,6 +2195,13 @@ describe("updateInternalDependents: always", () => { version: "1.0.0", dependencies: { "pkg-a": "bulbasaur" }, // using tag version from npm }), + ".changeset/config.json": JSON.stringify({ + ...modifiedDefaultConfig, + ___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH: { + ...defaultConfig.___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH, + updateInternalDependents: "always", + }, + }), }); await writeChangeset( @@ -2238,13 +2211,7 @@ describe("updateInternalDependents: always", () => { }, cwd, ); - await version(cwd, defaultOptions, { - ...modifiedDefaultConfig, - ___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH: { - ...defaultConfig.___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH, - updateInternalDependents: "always", - }, - }); + await version({ cwd }); // `pkg-a` should not be touched expect(await getPkgJSON("pkg-a", cwd)).toEqual( @@ -2295,6 +2262,13 @@ describe("updateInternalDependents: always", () => { version: "1.0.0", dependencies: { "pkg-a": "bulbasaur" }, // using tag version from npm }), + ".changeset/config.json": JSON.stringify({ + ...modifiedDefaultConfig, + ___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH: { + ...defaultConfig.___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH, + updateInternalDependents: "always", + }, + }), }); await writeChangeset( @@ -2311,13 +2285,7 @@ describe("updateInternalDependents: always", () => { }, cwd, ); - await version(cwd, defaultOptions, { - ...modifiedDefaultConfig, - ___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH: { - ...defaultConfig.___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH, - updateInternalDependents: "always", - }, - }); + await version({ cwd }); expect(await getPkgJSON("pkg-a", cwd)).toEqual( expect.objectContaining({ @@ -2375,8 +2343,9 @@ describe("pre", () => { name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify(modifiedDefaultConfig), }); - await pre(cwd, { command: "enter", tag: "next" }); + await pre({ cwd, command: "enter", tag: "next" }); await writeChangeset( { releases: [{ name: "pkg-b", type: "patch" }], @@ -2384,7 +2353,7 @@ describe("pre", () => { }, cwd, ); - await version(cwd, defaultOptions, modifiedDefaultConfig); + await version({ cwd }); let packages = await getPackages(cwd); expect(packages.packages.map((x) => x.packageJson)).toEqual([ { @@ -2407,7 +2376,7 @@ describe("pre", () => { cwd, ); - await version(cwd, defaultOptions, modifiedDefaultConfig); + await version({ cwd }); packages = (await getPackages(cwd))!; expect(packages.packages.map((x) => x.packageJson)).toEqual([ { @@ -2429,7 +2398,7 @@ describe("pre", () => { }, cwd, ); - await version(cwd, defaultOptions, modifiedDefaultConfig); + await version({ cwd }); packages = (await getPackages(cwd))!; expect(packages.packages.map((x) => x.packageJson)).toEqual([ { @@ -2451,7 +2420,7 @@ describe("pre", () => { }, cwd, ); - await version(cwd, defaultOptions, modifiedDefaultConfig); + await version({ cwd }); packages = (await getPackages(cwd))!; expect(packages.packages.map((x) => x.packageJson)).toMatchInlineSnapshot(` [ @@ -2468,8 +2437,8 @@ describe("pre", () => { }, ] `); - await pre(cwd, { command: "exit" }); - await version(cwd, defaultOptions, modifiedDefaultConfig); + await pre({ cwd, command: "exit" }); + await version({ cwd }); packages = (await getPackages(cwd))!; expect(packages.packages.map((x) => x.packageJson)).toEqual([ { @@ -2565,8 +2534,9 @@ describe("pre", () => { name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify(modifiedDefaultConfig), }); - await pre(cwd, { command: "enter", tag: "next" }); + await pre({ cwd, command: "enter", tag: "next" }); await writeChangeset( { releases: [{ name: "pkg-b", type: "patch" }], @@ -2575,7 +2545,7 @@ describe("pre", () => { cwd, ); - await version(cwd, defaultOptions, modifiedDefaultConfig); + await version({ cwd }); let packages = await getPackages(cwd); expect(packages.packages.map((x) => x.packageJson)).toEqual([ { @@ -2612,7 +2582,7 @@ describe("pre", () => { }, cwd, ); - await version(cwd, defaultOptions, modifiedDefaultConfig); + await version({ cwd }); packages = (await getPackages(cwd))!; expect(packages.packages.map((x) => x.packageJson)).toEqual([ @@ -2652,6 +2622,7 @@ describe("pre", () => { name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify(modifiedDefaultConfig), }); await writeChangeset( { @@ -2660,7 +2631,7 @@ describe("pre", () => { }, cwd, ); - await version(cwd, defaultOptions, modifiedDefaultConfig); + await version({ cwd }); let packages = await getPackages(cwd); expect(packages.packages.map((x) => x.packageJson)).toEqual([ @@ -2675,7 +2646,7 @@ describe("pre", () => { }, ]); - await pre(cwd, { command: "enter", tag: "next" }); + await pre({ cwd, command: "enter", tag: "next" }); await writeChangeset( { releases: [{ name: "pkg-a", type: "patch" }], @@ -2683,7 +2654,7 @@ describe("pre", () => { }, cwd, ); - await version(cwd, defaultOptions, modifiedDefaultConfig); + await version({ cwd }); packages = (await getPackages(cwd))!; expect(packages.packages.map((x) => x.packageJson)).toEqual([ @@ -2718,8 +2689,9 @@ describe("pre", () => { name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify(modifiedDefaultConfig), }); - await pre(cwd, { command: "enter", tag: "next" }); + await pre({ cwd, command: "enter", tag: "next" }); await writeChangeset( { releases: [{ name: "pkg-b", type: "patch" }], @@ -2727,7 +2699,7 @@ describe("pre", () => { }, cwd, ); - await version(cwd, defaultOptions, modifiedDefaultConfig); + await version({ cwd }); let packages = await getPackages(cwd); expect(packages.packages.map((x) => x.packageJson)).toEqual([ @@ -2744,8 +2716,8 @@ describe("pre", () => { }, ]); - await pre(cwd, { command: "exit" }); - await version(cwd, defaultOptions, modifiedDefaultConfig); + await pre({ cwd, command: "exit" }); + await version({ cwd }); packages = (await getPackages(cwd))!; expect(packages.packages.map((x) => x.packageJson)).toEqual([ @@ -2782,6 +2754,7 @@ describe("pre", () => { name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify(modifiedDefaultConfig), }); await writeChangeset( { @@ -2791,8 +2764,8 @@ describe("pre", () => { cwd, ); - await pre(cwd, { command: "enter", tag: "next" }); - await version(cwd, defaultOptions, modifiedDefaultConfig); + await pre({ cwd, command: "enter", tag: "next" }); + await version({ cwd }); let packages = await getPackages(cwd); expect(packages.packages.map((x) => x.packageJson)).toEqual([ @@ -2814,7 +2787,7 @@ describe("pre", () => { }, cwd, ); - await version(cwd, defaultOptions, modifiedDefaultConfig); + await version({ cwd }); packages = (await getPackages(cwd))!; expect(packages.packages.map((x) => x.packageJson)).toEqual([ @@ -2848,8 +2821,9 @@ describe("pre", () => { name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify(modifiedDefaultConfig), }); - await pre(cwd, { command: "enter", tag: "next" }); + await pre({ cwd, command: "enter", tag: "next" }); await writeChangeset( { @@ -2859,7 +2833,7 @@ describe("pre", () => { cwd, ); - await version(cwd, defaultOptions, modifiedDefaultConfig); + await version({ cwd }); let packages = await getPackages(cwd); expect(packages.packages.map((x) => x.packageJson)).toEqual([ @@ -2881,7 +2855,7 @@ describe("pre", () => { }, cwd, ); - await version(cwd, defaultOptions, modifiedDefaultConfig); + await version({ cwd }); packages = (await getPackages(cwd))!; expect(packages.packages.map((x) => x.packageJson)).toEqual([ @@ -2916,6 +2890,7 @@ describe("pre", () => { name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify(modifiedDefaultConfig), }); await writeChangeset( { @@ -2925,8 +2900,8 @@ describe("pre", () => { cwd, ); - await pre(cwd, { command: "enter", tag: "next" }); - await version(cwd, defaultOptions, modifiedDefaultConfig); + await pre({ cwd, command: "enter", tag: "next" }); + await version({ cwd }); const packages = await getPackages(cwd); expect(packages.packages.map((x) => x.packageJson)).toEqual([ @@ -2960,6 +2935,10 @@ describe("pre", () => { name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify({ + ...modifiedDefaultConfig, + ignore: ["pkg-a"], + }), }); await writeChangeset( { @@ -2977,11 +2956,8 @@ describe("pre", () => { cwd, ); - await pre(cwd, { command: "enter", tag: "next" }); - await version(cwd, defaultOptions, { - ...modifiedDefaultConfig, - ignore: ["pkg-a"], - }); + await pre({ cwd, command: "enter", tag: "next" }); + await version({ cwd }); const packages = await getPackages(cwd); expect(packages.packages.map((x) => x.packageJson)).toEqual([ @@ -3015,8 +2991,9 @@ describe("pre", () => { name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify(modifiedDefaultConfig), }); - await pre(cwd, { command: "enter", tag: "alpha" }); + await pre({ cwd, command: "enter", tag: "alpha" }); await writeChangeset( { @@ -3025,7 +3002,7 @@ describe("pre", () => { }, cwd, ); - await version(cwd, defaultOptions, modifiedDefaultConfig); + await version({ cwd }); const packages = await getPackages(cwd); expect(packages.packages.map((x) => x.packageJson)).toEqual([ @@ -3062,9 +3039,10 @@ describe("pre", () => { name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify(modifiedDefaultConfig), }); - await pre(cwd, { command: "enter", tag: "alpha" }); + await pre({ cwd, command: "enter", tag: "alpha" }); await writeChangeset( { @@ -3074,7 +3052,7 @@ describe("pre", () => { cwd, ); - await version(cwd, defaultOptions, modifiedDefaultConfig); + await version({ cwd }); expect((await getPackages(cwd)).packages.map((x) => x.packageJson)) .toMatchInlineSnapshot(` @@ -3101,7 +3079,7 @@ describe("pre", () => { cwd, ); - await version(cwd, defaultOptions, modifiedDefaultConfig); + await version({ cwd }); expect((await getPackages(cwd)).packages.map((x) => x.packageJson)) .toMatchInlineSnapshot(` @@ -3140,9 +3118,10 @@ describe("pre", () => { name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify(modifiedDefaultConfig), }); - await pre(cwd, { command: "enter", tag: "alpha" }); + await pre({ cwd, command: "enter", tag: "alpha" }); await writeChangeset( { @@ -3152,7 +3131,7 @@ describe("pre", () => { cwd, ); - await version(cwd, defaultOptions, modifiedDefaultConfig); + await version({ cwd }); expect((await getPackages(cwd)).packages.map((x) => x.packageJson)) .toMatchInlineSnapshot(` @@ -3191,9 +3170,10 @@ describe("pre", () => { name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify(modifiedDefaultConfig), }); - await pre(cwd, { command: "enter", tag: "alpha" }); + await pre({ cwd, command: "enter", tag: "alpha" }); await writeChangeset( { @@ -3203,7 +3183,7 @@ describe("pre", () => { cwd, ); - await version(cwd, defaultOptions, modifiedDefaultConfig); + await version({ cwd }); expect((await getPackages(cwd)).packages.map((x) => x.packageJson)) .toMatchInlineSnapshot(` @@ -3242,9 +3222,10 @@ describe("pre", () => { name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify(modifiedDefaultConfig), }); - await pre(cwd, { command: "enter", tag: "alpha" }); + await pre({ cwd, command: "enter", tag: "alpha" }); await writeChangeset( { @@ -3254,7 +3235,7 @@ describe("pre", () => { cwd, ); - await version(cwd, defaultOptions, modifiedDefaultConfig); + await version({ cwd }); expect((await getPackages(cwd)).packages.map((x) => x.packageJson)) .toMatchInlineSnapshot(` @@ -3293,9 +3274,10 @@ describe("pre", () => { name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify(modifiedDefaultConfig), }); - await pre(cwd, { command: "enter", tag: "alpha" }); + await pre({ cwd, command: "enter", tag: "alpha" }); await writeChangeset( { @@ -3305,7 +3287,7 @@ describe("pre", () => { cwd, ); - await version(cwd, defaultOptions, modifiedDefaultConfig); + await version({ cwd }); expect((await getPackages(cwd)).packages.map((x) => x.packageJson)) .toMatchInlineSnapshot(` @@ -3342,8 +3324,9 @@ describe("pre", () => { name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify(modifiedDefaultConfig), }); - await pre(cwd, { command: "enter", tag: "next" }); + await pre({ cwd, command: "enter", tag: "next" }); await writeChangeset( { releases: [{ name: "pkg-b", type: "patch" }], @@ -3351,7 +3334,7 @@ describe("pre", () => { }, cwd, ); - await version(cwd, defaultOptions, modifiedDefaultConfig); + await version({ cwd }); const packages = await getPackages(cwd); expect(packages.packages.map((x) => x.packageJson)).toEqual([ { @@ -3378,8 +3361,15 @@ describe("pre", () => { private: true, version: "1.0.0", }), + ".changeset/config.json": JSON.stringify({ + ...modifiedDefaultConfig, + privatePackages: { + tag: false, + version: true, + }, + }), }); - await pre(cwd, { command: "enter", tag: "next" }); + await pre({ cwd, command: "enter", tag: "next" }); await writeChangeset( { releases: [{ name: "pkg-a", type: "patch" }], @@ -3387,13 +3377,7 @@ describe("pre", () => { }, cwd, ); - await version(cwd, defaultOptions, { - ...modifiedDefaultConfig, - privatePackages: { - tag: false, - version: true, - }, - }); + await version({ cwd }); const packages = await getPackages(cwd); expect(packages.packages.map((x) => x.packageJson)).toEqual([ { @@ -3427,6 +3411,7 @@ describe("pre", () => { name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify(linkedConfig), }); await writeChangeset( { @@ -3435,7 +3420,7 @@ describe("pre", () => { }, cwd, ); - await version(cwd, defaultOptions, linkedConfig); + await version({ cwd }); let packages = await getPackages(cwd); expect(packages.packages.map((x) => x.packageJson)).toEqual([ @@ -3450,7 +3435,7 @@ describe("pre", () => { }, ]); - await pre(cwd, { command: "enter", tag: "next" }); + await pre({ cwd, command: "enter", tag: "next" }); await writeChangeset( { releases: [{ name: "pkg-b", type: "patch" }], @@ -3458,7 +3443,7 @@ describe("pre", () => { }, cwd, ); - await version(cwd, defaultOptions, linkedConfig); + await version({ cwd }); packages = (await getPackages(cwd))!; expect(packages.packages.map((x) => x.packageJson)).toEqual([ @@ -3479,7 +3464,7 @@ describe("pre", () => { }, cwd, ); - await version(cwd, defaultOptions, linkedConfig); + await version({ cwd }); packages = (await getPackages(cwd))!; expect(packages.packages.map((x) => x.packageJson)).toEqual([ { @@ -3499,7 +3484,7 @@ describe("pre", () => { }, cwd, ); - await version(cwd, defaultOptions, linkedConfig); + await version({ cwd }); packages = (await getPackages(cwd))!; expect(packages.packages.map((x) => x.packageJson)).toEqual([ { @@ -3540,8 +3525,9 @@ describe("pre", () => { name: "pkg-c", version: "0.1.0", }), + ".changeset/config.json": JSON.stringify(linkedConfig), }); - await pre(cwd, { command: "enter", tag: "next" }); + await pre({ cwd, command: "enter", tag: "next" }); await writeChangeset( { releases: [{ name: "pkg-a", type: "minor" }], @@ -3549,7 +3535,7 @@ describe("pre", () => { }, cwd, ); - await version(cwd, defaultOptions, linkedConfig); + await version({ cwd }); let packages = await getPackages(cwd); expect(packages.packages.map((x) => x.packageJson)).toEqual([ @@ -3575,7 +3561,7 @@ describe("pre", () => { }, cwd, ); - await version(cwd, defaultOptions, linkedConfig); + await version({ cwd }); packages = (await getPackages(cwd))!; expect(packages.packages.map((x) => x.packageJson)).toEqual([ @@ -3616,6 +3602,7 @@ describe("pre", () => { name: "pkg-b", version: "1.0.0", }), + ".changeset/config.json": JSON.stringify(linkedConfig), }); await writeChangeset( { @@ -3624,7 +3611,7 @@ describe("pre", () => { }, cwd, ); - await version(cwd, defaultOptions, linkedConfig); + await version({ cwd }); const packages = await getPackages(cwd); expect(packages.packages.map((x) => x.packageJson)).toEqual([ @@ -3669,15 +3656,16 @@ describe("with privatePackages", () => { Nice simple summary, much wow `, + ".changeset/config.json": JSON.stringify({ + ...modifiedDefaultConfig, + privatePackages: { + version: false, + tag: false, + }, + }), }); - await version(cwd, defaultOptions, { - ...modifiedDefaultConfig, - privatePackages: { - version: false, - tag: false, - }, - }); + await version({ cwd }); const packages = await getPackages(cwd); expect(packages.packages.map((x) => x.packageJson)).toEqual([ @@ -3695,4 +3683,140 @@ Nice simple summary, much wow }, ]); }); + + it("should not throw if dependents of unversioned private packages are not explicitly listed by the ignore flag", async () => { + const cwd = await testdir({ + "package.json": JSON.stringify({ + private: true, + workspaces: ["packages/*"], + }), + "package-lock.json": "", + "packages/pkg-a/package.json": JSON.stringify({ + name: "pkg-a", + version: "1.0.0", + private: true, + dependencies: { + "pkg-b": "1.0.0", + }, + }), + "packages/pkg-b/package.json": JSON.stringify({ + name: "pkg-b", + version: "1.0.0", + }), + ".changeset/config.json": JSON.stringify({ + privatePackages: { + tag: false, + version: false, + }, + }), + }); + try { + await version({ cwd, ignore: ["pkg-b"] }); + } catch { + // ignore the error. We just want to validate the error message + } + + expect(mockedLogger.error).not.toHaveBeenCalled(); + }); + + it("should not throw on a dev dependent on an unversioned private package", async () => { + const cwd = await testdir({ + "package.json": JSON.stringify({ + private: true, + workspaces: ["packages/*"], + }), + "package-lock.json": "", + "packages/pkg-a/package.json": JSON.stringify({ + name: "pkg-a", + version: "1.0.0", + devDependencies: { + "pkg-b": "1.0.0", + }, + }), + "packages/pkg-b/package.json": JSON.stringify({ + name: "pkg-b", + version: "1.0.0", + private: true, + }), + ".changeset/config.json": JSON.stringify({ + privatePackages: { + tag: false, + version: false, + }, + }), + }); + try { + await version({ cwd }); + } catch { + // ignore the error. We just want to validate the error message + } + + expect(mockedLogger.error).not.toHaveBeenCalled(); + }); + + it("should not throw if a versioned private package depends on an ignored package", async () => { + const cwd = await testdir({ + "package.json": JSON.stringify({ + private: true, + workspaces: ["packages/*"], + }), + "yarn.lock": "", + "packages/pkg-a/package.json": JSON.stringify({ + name: "pkg-a", + version: "1.0.0", + private: true, + dependencies: { + "pkg-b": "1.0.0", + }, + }), + "packages/pkg-b/package.json": JSON.stringify({ + name: "pkg-b", + version: "1.0.0", + private: true, + }), + ".changeset/changesets-are-beautiful.md": `--- +"pkg-a": minor +--- + +Nice simple summary, much wow +`, + ".changeset/config.json": JSON.stringify({}), + }); + + await version({ cwd, ignore: ["pkg-b"] }); + + expect(mockedLogger.error).not.toHaveBeenCalled(); + }); + + it("should not throw if a package only has a devDependency on an ignored package", async () => { + const cwd = await testdir({ + "package.json": JSON.stringify({ + private: true, + workspaces: ["packages/*"], + }), + "yarn.lock": "", + "packages/pkg-a/package.json": JSON.stringify({ + name: "pkg-a", + version: "1.0.0", + devDependencies: { + "pkg-b": "1.0.0", + }, + }), + "packages/pkg-b/package.json": JSON.stringify({ + name: "pkg-b", + version: "1.0.0", + }), + ".changeset/changesets-are-beautiful.md": `--- +"pkg-a": minor +--- + +Nice simple summary, much wow +`, + ".changeset/config.json": JSON.stringify({}), + }); + + await version({ cwd, ignore: ["pkg-b"] }); + + expect(mockedLogger.error).not.toHaveBeenCalled(); + }); }); diff --git a/packages/cli/src/help.ts b/packages/cli/src/help.ts deleted file mode 100644 index 29948d23f..000000000 --- a/packages/cli/src/help.ts +++ /dev/null @@ -1,10 +0,0 @@ -export const COMMAND_HELP: Record = { - init: "init", - add: "add [--empty] [--open] [--since ] [--message ]", - version: - "version [--ignore] [--snapshot ] [--snapshot-prerelease-template