Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
41 changes: 41 additions & 0 deletions .chronus/changes/pnpm-catalogs-2026-2-24-15-9-10.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
# Change versionKind to one of: internal, fix, dependencies, feature, deprecation, breaking
changeKind: internal
packages:
- "@typespec/asset-emitter"
- "@typespec/bundler"
- "@typespec/compiler"
- "@typespec/emitter-framework"
- "@typespec/eslint-plugin"
- "@typespec/events"
- "@typespec/html-program-viewer"
- "@typespec/http-canonicalization"
- "@typespec/http-client-js"
- "@typespec/http-client"
- "@typespec/http-server-csharp"
- "@typespec/http-server-js"
- "@typespec/http-specs"
- "@typespec/http"
- "@typespec/internal-build-utils"
- "@typespec/json-schema"
- "@typespec/library-linter"
- "@typespec/mutator-framework"
- "@typespec/openapi"
- "@typespec/openapi3"
- "@typespec/playground"
- "@typespec/prettier-plugin-typespec"
- "@typespec/protobuf"
- "@typespec/rest"
- "@typespec/spec-api"
- "@typespec/spec-coverage-sdk"
- "@typespec/spector"
- "@typespec/sse"
- "@typespec/streams"
- tmlanguage-generator
- "@typespec/tspd"
- typespec-vscode
- "@typespec/versioning"
- "@typespec/xml"
---

Migrate to catalogs
2 changes: 1 addition & 1 deletion .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ TypeSpec is a language for defining cloud service APIs and shapes. This monorepo
- Generate external signatures: `pnpm gen-compiler-extern-signature`
- Regenerate samples: `pnpm regen-samples`
- Regenerate docs: `pnpm regen-docs`
- Sync dependency versions: `pnpm fix-version-mismatch`
- Check catalog usage: `pnpm check-catalog`

## Troubleshooting

Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/consistency.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ jobs:
- run: pnpm run lint
name: Lint

# Check version mismatch
# Check that all dependencies use the pnpm catalog
version-consistency:
name: Versions consistency
runs-on: ubuntu-latest
Expand All @@ -117,5 +117,5 @@ jobs:
- run: pnpm install
name: Install dependencies

- run: pnpm run check-version-mismatch
name: Check version mismatch
- run: pnpm run check-catalog
name: Check catalog usage
2 changes: 1 addition & 1 deletion cspell.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,6 @@ words:
- strs
- stubbed
- swaggerui
- syncpack
- TCGC
- terlson
- timegm
Expand Down Expand Up @@ -337,6 +336,7 @@ ignorePaths:
- packages/mutator-framework/**/*.test.ts
- packages/typespec-vscode/test/scenarios/**
- pnpm-lock.yaml
- pnpm-workspace.yaml
- "**/*.mp4"
- "**/*.plist"
- .git/**
Expand Down
2 changes: 1 addition & 1 deletion docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ RUN pnpm install --filter "@typespec/compiler..."
RUN pnpm --filter "@typespec/compiler..." run build

WORKDIR /app/packages/compiler
RUN npm pack
RUN pnpm pack

# --------------------------------
# Setup final image
Expand Down
115 changes: 115 additions & 0 deletions eng/common/scripts/check-catalog.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { execSync } from "child_process";
import { readFileSync } from "fs";
import { join, relative } from "path";
import { parse } from "yaml";
import { repoRoot } from "./utils/common.js";

/**
* Validates that all workspace package dependencies use `catalog:` or `workspace:` protocols,
* ensuring versions are centrally managed via the pnpm catalog in pnpm-workspace.yaml.
*/

interface WorkspaceConfig {
catalog?: Record<string, string>;
}

const workspaceConfig: WorkspaceConfig = parse(
readFileSync(join(repoRoot, "pnpm-workspace.yaml"), "utf8"),
);
const catalog = workspaceConfig.catalog ?? {};

/**
* Dependencies that are allowed to use explicit versions instead of catalog:.
* Each entry maps a package.json path (relative to repo root) to a set of dependency names.
*/
const exceptions: Record<string, Set<string>> = {
// vsce needs a real semver for @types/vscode to determine VS Code engine compatibility
"packages/typespec-vscode/package.json": new Set(["@types/vscode"]),
};

const depTypes = ["dependencies", "devDependencies", "peerDependencies"] as const;

const errors: string[] = [];
const warnings: string[] = [];

// Resolve workspace packages from pnpm
const pnpmOutput = execSync("pnpm ls -r --json --depth -1", {
cwd: repoRoot,
encoding: "utf8",
});
const workspacePackages: { path: string }[] = JSON.parse(pnpmOutput);
const packageJsonPaths: string[] = workspacePackages.map((p) =>
join(relative(repoRoot, p.path), "package.json"),
);

for (const relPath of packageJsonPaths) {
const fullPath = join(repoRoot, relPath);
const pkg = JSON.parse(readFileSync(fullPath, "utf8"));
const fileExceptions = exceptions[relPath] ?? new Set();

for (const depType of depTypes) {
const deps: Record<string, string> | undefined = pkg[depType];
if (!deps) continue;

for (const [name, version] of Object.entries(deps)) {
if (version === "catalog:" || version.startsWith("workspace:")) {
continue;
}
if (fileExceptions.has(name)) {
// Allowed exception — but warn if it drifts from the catalog
if (catalog[name] && catalog[name] !== version) {
warnings.push(
`${relPath}: ${depType}.${name} has version "${version}" but catalog has "${catalog[name]}". Keep them in sync.`,
);
}
continue;
}
errors.push(
`${relPath}: ${depType}.${name} uses explicit version "${version}" instead of "catalog:".`,
);
}
}
}

// Check that every catalog entry is actually used somewhere
const usedCatalogEntries = new Set<string>();
for (const relPath of packageJsonPaths) {
const fullPath = join(repoRoot, relPath);
const pkg = JSON.parse(readFileSync(fullPath, "utf8"));
for (const depType of depTypes) {
const deps: Record<string, string> | undefined = pkg[depType];
if (!deps) continue;
for (const [name, version] of Object.entries(deps)) {
if (version === "catalog:") {
usedCatalogEntries.add(name);
}
}
}
}

for (const name of Object.keys(catalog)) {
if (!usedCatalogEntries.has(name)) {
warnings.push(`pnpm-workspace.yaml: catalog entry "${name}" is not used by any package.`);
}
}

// Report results
if (warnings.length > 0) {
console.log(`\n⚠ Warnings (${warnings.length}):`);
for (const w of warnings) {
console.log(` ${w}`);
}
}

if (errors.length > 0) {
console.log(`\n✘ Errors (${errors.length}):`);
for (const e of errors) {
console.log(` ${e}`);
}
console.log(
'\nAll external dependencies must use "catalog:" protocol. Add the version to the catalog in pnpm-workspace.yaml and use "catalog:" in package.json.',
);
process.exit(1);
}

console.log("✔ All dependencies are using catalog: or workspace: protocols.");
74 changes: 31 additions & 43 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,11 @@
"build:all": "pnpm -r --workspace-concurrency=Infinity build",
"check:eng": "tsc -p ./tsconfig.eng.json --noEmit",
"setup:min": "pnpm install && pnpm --filter \"@typespec/prettier-plugin-typespec...\" --filter \"@typespec/tspd...\" run build",
"check-version-mismatch": "syncpack list-mismatches",
"check-catalog": "tsx eng/common/scripts/check-catalog.ts",
"change": "chronus",
"clean": "pnpm -r run clean",
"cspell": "cspell --no-progress .",
"dogfood": "pnpm install && pnpm build && pnpm -r dogfood",
"fix-version-mismatch": "syncpack fix-mismatches",
"format": "prettier . --write",
"format:check": "prettier . --check",
"format:dir": "prettier --write",
Expand All @@ -41,46 +40,35 @@
"tsp-integration": "node packages/tsp-integration/cmd/tsp-integration.js"
},
"devDependencies": {
"@chronus/chronus": "^1.3.1",
"@chronus/github": "^1.0.6",
"@chronus/github-pr-commenter": "^1.0.6",
"@eslint/js": "^10.0.1",
"@microsoft/api-extractor": "^7.57.7",
"@octokit/core": "^7.0.6",
"@octokit/plugin-paginate-graphql": "^6.0.0",
"@octokit/plugin-rest-endpoint-methods": "^17.0.0",
"@types/micromatch": "^4.0.10",
"@types/node": "~25.5.0",
"@vitest/coverage-v8": "^4.1.0",
"@vitest/eslint-plugin": "^1.6.12",
"c8": "^11.0.0",
"cspell": "^9.7.0",
"eslint": "^10.0.3",
"eslint-plugin-react-hooks": "7.0.1",
"eslint-plugin-unicorn": "^63.0.0",
"micromatch": "^4.0.8",
"picocolors": "~1.1.1",
"playwright": "^1.58.2",
"prettier": "~3.8.1",
"prettier-plugin-astro": "^0.14.1",
"prettier-plugin-organize-imports": "~4.3.0",
"prettier-plugin-sh": "^0.18.0",
"rimraf": "~6.1.3",
"syncpack": "^13.0.3",
"tsx": "^4.21.0",
"typescript": "~5.9.3",
"typescript-eslint": "^8.57.0",
"vitest": "^4.1.0",
"yaml": "~2.8.2"
},
"syncpack": {
"dependencyTypes": [
"dev",
"overrides",
"peer",
"pnpmOverrides",
"prod",
"resolutions"
]
"@chronus/chronus": "catalog:",
"@chronus/github": "catalog:",
"@chronus/github-pr-commenter": "catalog:",
"@eslint/js": "catalog:",
"@microsoft/api-extractor": "catalog:",
"@octokit/core": "catalog:",
"@octokit/plugin-paginate-graphql": "catalog:",
"@octokit/plugin-rest-endpoint-methods": "catalog:",
"@types/micromatch": "catalog:",
"@types/node": "catalog:",
"@vitest/coverage-v8": "catalog:",
"@vitest/eslint-plugin": "catalog:",
"c8": "catalog:",
"cspell": "catalog:",
"eslint": "catalog:",
"eslint-plugin-react-hooks": "catalog:",
"eslint-plugin-unicorn": "catalog:",
"micromatch": "catalog:",
"picocolors": "catalog:",
"playwright": "catalog:",
"prettier": "catalog:",
"prettier-plugin-astro": "catalog:",
"prettier-plugin-organize-imports": "catalog:",
"prettier-plugin-sh": "catalog:",
"rimraf": "catalog:",
"tsx": "catalog:",
"typescript": "catalog:",
"typescript-eslint": "catalog:",
"vitest": "catalog:",
"yaml": "catalog:"
}
}
14 changes: 7 additions & 7 deletions packages/asset-emitter/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,13 @@
"@typespec/compiler": "workspace:^"
},
"devDependencies": {
"@types/node": "~25.5.0",
"@types/node": "catalog:",
"@typespec/compiler": "workspace:^",
"@vitest/coverage-v8": "^4.1.0",
"@vitest/ui": "^4.1.0",
"c8": "^11.0.0",
"rimraf": "~6.1.3",
"typescript": "~5.9.3",
"vitest": "^4.1.0"
"@vitest/coverage-v8": "catalog:",
"@vitest/ui": "catalog:",
"c8": "catalog:",
"rimraf": "catalog:",
"typescript": "catalog:",
"vitest": "catalog:"
}
}
20 changes: 10 additions & 10 deletions packages/astro-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,20 @@
"watch": "tsc -p ./tsconfig.build.json --watch"
},
"devDependencies": {
"@types/react": "~19.2.14",
"astro": "^6.0.4"
"@types/react": "catalog:",
"astro": "catalog:"
},
"peerDependencies": {
"astro": "^6.0.4"
"astro": "catalog:"
},
"dependencies": {
"@astrojs/check": "^0.9.7",
"@astrojs/starlight": "^0.38.1",
"@expressive-code/core": "^0.41.7",
"@astrojs/check": "catalog:",
"@astrojs/starlight": "catalog:",
"@expressive-code/core": "catalog:",
"@typespec/playground": "workspace:^",
"astro-expressive-code": "^0.41.7",
"pathe": "^2.0.3",
"react": "~19.2.4",
"typescript": "~5.9.3"
"astro-expressive-code": "catalog:",
"pathe": "catalog:",
"react": "catalog:",
"typescript": "catalog:"
}
}
14 changes: 7 additions & 7 deletions packages/best-practices/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,13 @@
"@typespec/compiler": "workspace:^"
},
"devDependencies": {
"@types/node": "~25.5.0",
"@types/node": "catalog:",
"@typespec/compiler": "workspace:^",
"@vitest/coverage-v8": "^4.1.0",
"@vitest/ui": "^4.1.0",
"c8": "^11.0.0",
"rimraf": "~6.1.3",
"typescript": "~5.9.3",
"vitest": "^4.1.0"
"@vitest/coverage-v8": "catalog:",
"@vitest/ui": "catalog:",
"c8": "catalog:",
"rimraf": "catalog:",
"typescript": "catalog:",
"vitest": "catalog:"
}
}
Loading
Loading