rework config parsing, rewrite config tests by beeequeue · Pull Request #2015 · changesets/changesets · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
b7435ea
use assemble-release-plan directly
beeequeue May 19, 2026
14d4c1f
rewrite config reading
beeequeue May 19, 2026
867f2f6
update usages
beeequeue May 17, 2026
28b2f88
remove hashes from dist files
beeequeue May 17, 2026
f9fb81d
surface validation errors in `ExitError`
beeequeue May 18, 2026
95d5473
remove legacy support tests, fix other tests
beeequeue May 18, 2026
150c9b0
fix incorrect default `changelog` value
beeequeue May 18, 2026
1609d80
remove `--experimental-strip-types` usage
beeequeue May 18, 2026
890432f
fix small details
beeequeue May 18, 2026
9498ccb
Update packages/config/src/parse.ts
beeequeue May 18, 2026
c4a61f3
handle new returned config result in getReleasePlan
beeequeue May 19, 2026
472bcbd
unpin dep
beeequeue May 19, 2026
64ce897
rename `readAndValidateConfig` export to `readConfig`
beeequeue May 19, 2026
6b4cff2
use our local json schema for our own config
beeequeue May 19, 2026
8a40d3b
remove `internal` access option support
beeequeue May 19, 2026
cdcb2d9
revert temporary release age relaxation
beeequeue May 19, 2026
da30645
implement a proper solution for generating json schemas
beeequeue May 19, 2026
29857c3
test exposing child package via `exports`
beeequeue May 19, 2026
bd27256
format files
beeequeue May 19, 2026
81c5c34
remove `private` alias
beeequeue May 22, 2026
67786ce
address comments in pr
beeequeue May 22, 2026
aafc4e2
only throw errors, not warnings
beeequeue May 22, 2026
28de0d8
add property prefixes to rules where applicable
beeequeue May 22, 2026
36cf429
inline `readConfigFile`, rename `readAndValidateConfig`
beeequeue May 24, 2026
12092db
format file
beeequeue May 24, 2026
065534b
revert back from `getDefaultConfig` to `defaultConfig`
beeequeue May 25, 2026
b209968
revert removed defaults from `defaultWrittenConfig`
beeequeue May 25, 2026
6bbf821
move json schema generation to scripts/
beeequeue May 25, 2026
b2717a8
changesets
beeequeue May 25, 2026
2d9c427
return `errors: undefined` on success
beeequeue May 25, 2026
26f5e45
format changeset
beeequeue May 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .changeset/config.json
40 changes: 40 additions & 0 deletions .changeset/cool-camels-type.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
"@changesets/config": major
---

Removed `read` and `parse` functions in favor of `readConfig`, which returns `{ config, warnings, errors }` instead of throwing on issues.

```ts
// before.ts
import { parse } from "@changesets/config";
import { getPackages } from "@manypkg/get-packages";

const config = parse({ commit: true }, await getPackages());

try {
return parse({ commit: true }, packages);
} catch (err) {
if (err instanceof ValidationError) {
console.error(`Invalid config: ${err.message}`);
} else {
throw err;
}
}

// after.ts
import { readConfig } from "@changesets/config";
import { getPackages } from "@manypkg/get-packages";

// both arguments are optional
const { config, warnings, errors } = readConfig(
process.cwd(),
await getPackages(),
);

if (warnings.length !== 0) {
console.warn(warnings.join("\n"));
}
if (config == null) {
console.error(errors.join("\n"));
}
```
5 changes: 5 additions & 0 deletions .changeset/cute-deserts-like.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@changesets/errors": minor
---

Added `cause` option to `ExitError` to help surface the cause behind exiting.
5 changes: 5 additions & 0 deletions .changeset/rich-geckos-end.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@changesets/config": minor
---

Refactored config parsing to use Valibot and validation rules.
5 changes: 5 additions & 0 deletions .changeset/wicked-dryers-shave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@changesets/errors": major
---

Removed `ValidationError`.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,14 @@
"watch": "tsdown --watch",
"format": "oxfmt --check",
"format:fix": "oxfmt",
"generate-json-schema": "node packages/config/scripts/generate-json-schema.ts",
"lint": "eslint",
"test": "vitest",
"types:check": "tsc",
"check-all": "pnpm build && pnpm test run && pnpm types:check && pnpm lint && pnpm format",
"version-packages": "pnpm changeset version && pnpm format:fix",
"release": "pnpm changeset publish",
"changeset": "NODE_OPTIONS=--experimental-strip-types packages/cli/bin.js"
"changeset": "node packages/cli/bin.js"
},
"devDependencies": {
"@eslint/js": "10.0.1",
Expand Down
2 changes: 1 addition & 1 deletion packages/apply-release-plan/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { getPackages } from "@manypkg/get-packages";
import { outdent } from "outdent";
import { exec } from "tinyexec";
import { describe, expect, it, test } from "vitest";
import applyReleasePlan from "./index.ts";
import { applyReleasePlan } from "./index.ts";

const changesetsCliChangelogPath = path.resolve(
import.meta.dirname,
Expand Down
11 changes: 8 additions & 3 deletions packages/assemble-release-plan/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,21 @@ Usage
```ts
import assembleReleasePlan from "@changesets/assemble-release-plan";
import { readChangesets } from "@changesets/read";
import { read } from "@changesets/config";
import { readConfig } from "@changesets/config";
import { getPackages } from "@manypkg/get-packages";
import { readPreState } from "@changesets/pre";

const packages = await getPackages(cwd);
const preState = await readPreState(cwd);
const config = await read(cwd, packages);
const { config } = await readConfig(cwd, packages);
const changesets = await readChangesets(cwd, sinceRef);

const releasePlan = assembleReleasePlan(changesets, packages, config, preState);
const releasePlan = assembleReleasePlan(
changesets,
packages,
config!,
preState,
);
```

Signature
Expand Down
1 change: 0 additions & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
"@changesets/config": "workspace:^",
"@changesets/errors": "workspace:^",
"@changesets/get-dependents-graph": "workspace:^",
"@changesets/get-release-plan": "workspace:^",
"@changesets/git": "workspace:^",
"@changesets/pre": "workspace:^",
"@changesets/read": "workspace:^",
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/commands/add/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
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";
Expand All @@ -12,6 +11,7 @@ import launchEditor from "launch-editor";
import { getCommitFunctions } from "../../commit/getCommitFunctions.ts";
import * as cli from "../../utils/cli-utilities.ts";
import { importantWarning } from "../../utils/cli-utilities.ts";
import { readConfig } from "../../utils/read-config.ts";
import { getVersionableChangedPackages } from "../../utils/versionablePackages.ts";
import { ensureChangesetFolder } from "../shared.ts";
import { createChangeset } from "./createChangeset.ts";
Expand All @@ -37,7 +37,7 @@ export async function add(options?: AddOptions): Promise<void> {
throw new ExitError(1);
}

const config = await read(packages.rootDir, packages);
const config = await readConfig(packages);

const versionablePackages = packages.packages.filter(
(pkg) =>
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/commands/publish/__tests__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ describe("Publish command", () => {
});
await expect(
publishCommand({ cwd, tag: "experimental" }),
).rejects.toThrowError();
).rejects.toThrow();
});
});
});
4 changes: 2 additions & 2 deletions packages/cli/src/commands/publish/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
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";
Expand All @@ -8,6 +7,7 @@ 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 { readConfig } from "../../utils/read-config.ts";
import { ensureChangesetFolder } from "../shared.ts";
import { publishPackages } from "./publishPackages.ts";

Expand Down Expand Up @@ -65,7 +65,7 @@ To resolve this exit the pre mode by running ${c.cyan("changeset pre exit")}.
showNonLatestTagWarning(options?.tag, preState);
}

const config = await read(packages.rootDir, packages);
const config = await readConfig(packages);
const tagPrivatePackages =
config.privatePackages && config.privatePackages.tag;

Expand Down
18 changes: 12 additions & 6 deletions packages/cli/src/commands/status/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import fs from "node:fs/promises";
import path from "node:path";
import { assembleReleasePlan } from "@changesets/assemble-release-plan";
import c from "@changesets/color";
import { read } from "@changesets/config";
import { ExitError } from "@changesets/errors";
import { getReleasePlan } from "@changesets/get-release-plan";
import { readPreState } from "@changesets/pre";
import { readChangesets } from "@changesets/read";
import type { ComprehensiveRelease } from "@changesets/types";
import { log } from "@clack/prompts";
import { getPackages } from "@manypkg/get-packages";
import { readConfig } from "../../utils/read-config.ts";
import { getVersionableChangedPackages } from "../../utils/versionablePackages.ts";
import { ensureChangesetFolder } from "../shared.ts";

Expand All @@ -22,12 +24,16 @@ export async function status(options?: StatusOptions) {

const packages = await getPackages(cwd);
await ensureChangesetFolder(packages.rootDir);
const config = await read(packages.rootDir, packages);

const releasePlan = await getReleasePlan(
Comment thread
Andarist marked this conversation as resolved.
packages.rootDir,
options?.since,
const config = await readConfig(packages);
const preState = await readPreState(packages.rootDir);
const changesets = await readChangesets(packages.rootDir, options?.since);

const releasePlan = assembleReleasePlan(
changesets,
packages,
config,
preState,
);
const changedPackages = await getVersionableChangedPackages(config, {
cwd: packages.rootDir,
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/commands/tag/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import c from "@changesets/color";
import { read } from "@changesets/config";
import * as git from "@changesets/git";
import { shouldSkipPackage } from "@changesets/should-skip-package";
import { log, progress } from "@clack/prompts";
import { getPackages, type Tool } from "@manypkg/get-packages";
import { getUntaggedPackages } from "../../utils/getUntaggedPackages.ts";
import { readConfig } from "../../utils/read-config.ts";
import { ensureChangesetFolder } from "../shared.ts";

function buildTag(tool: Tool, pkg: { name: string; newVersion: string }) {
Expand All @@ -30,7 +30,7 @@ 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 config = await readConfig(packages);

const allExistingTags = await git.getAllTags(packages.rootDir);

Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/commands/version/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ 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";
Expand All @@ -15,6 +14,7 @@ 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 { readConfig } from "../../utils/read-config.ts";
import { ensureChangesetFolder } from "../shared.ts";

export interface VersionOptions {
Expand All @@ -29,7 +29,7 @@ export async function version(options: VersionOptions) {

const packages = await getPackages(cwd);
await ensureChangesetFolder(packages.rootDir);
const config = await read(packages.rootDir, packages);
const config = await readConfig(packages);

const messages: string[] = [];
let ignore: readonly string[] | undefined;
Expand Down
10 changes: 3 additions & 7 deletions packages/cli/src/commands/version/version.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1800,10 +1800,6 @@ describe("snapshot release", () => {
"0.0.0-alpha.1639354050879.abcdef",
],
["{datetime}-{tag}", "alpha", "0.0.0-20211213000730-alpha"],
// Legacy support
["", "test", "0.0.0-test-20211213000730"],
[undefined, "canary", "0.0.0-canary-20211213000730"],
[null, "alpha", "0.0.0-alpha-20211213000730"],
])(
"should customize release correctly based on snapshotPrereleaseTemplate template: %s (tag: '%s')",
async (snapshotTemplate, snapshotValue, expectedResult) => {
Expand Down Expand Up @@ -1890,7 +1886,7 @@ describe("snapshot release", () => {
commit: false,
snapshot: {
useCalculatedVersion: true,
prereleaseTemplate: null,
prereleaseTemplate: undefined,
},
}),
});
Expand Down Expand Up @@ -1937,7 +1933,7 @@ describe("snapshot release", () => {
...modifiedDefaultConfig,
snapshot: {
useCalculatedVersion: true,
prereleaseTemplate: null,
prereleaseTemplate: undefined,
},
}),
});
Expand Down Expand Up @@ -1987,7 +1983,7 @@ describe("snapshot release", () => {
ignore: ["pkg-a"],
snapshot: {
useCalculatedVersion: true,
prereleaseTemplate: null,
prereleaseTemplate: undefined,
},
}),
});
Expand Down
34 changes: 34 additions & 0 deletions packages/cli/src/utils/read-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import c from "@changesets/color";
import { readConfig as originalReadConfig } from "@changesets/config";
import { ExitError } from "@changesets/errors";
import type { Config, Packages } from "@changesets/types";
import { log } from "@clack/prompts";

export async function readConfig(packages: Packages): Promise<Config> {
const { config, warnings, errors } = await originalReadConfig(
packages.rootDir,
packages,
);

const messages: string[] = [];

for (const warning of warnings) {
messages.push(c.yellow(warning));
}
if (errors != null) {
for (const error of errors) {
messages.push(c.red(error));
}
}

const logFn = errors != null ? log.error : log.warn;
if (messages.length !== 0) {
logFn(`Found issues in your config:\n- ${messages.join("\n- ")}`);
}

if (errors != null) {
throw new ExitError(1, { cause: new Error(errors.join("\n")) });
}

return config;
}
22 changes: 10 additions & 12 deletions packages/config/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,17 @@
[![View changelog](https://npmx.dev/api/registry/badge/version/@changesets/cli?color=229fe4&value=View+changelog&label=+)](./CHANGELOG.md)

```tsx
import { parse, read, ValidationError } from "@changesets/config";
import { readConfig } from "@changesets/config";

let config = await read(process.cwd(), workspaces);
const { config, warnings, errors } = await readConfig(process.cwd());

let config = parse({ commit: true }, workspaces);

try {
return parse({ commit: true }, workspaces);
} catch (err) {
if (err instanceof ValidationError) {
let message = err.message;
} else {
throw err;
}
if (warnings.length !== 0) {
console.warn(warnings);
}
if (config == null) {
console.error(errors);
return;
}

console.log(config);
```
9 changes: 8 additions & 1 deletion packages/config/package.json
Loading