diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 5c40aaf49..fec7d6a23 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -15,39 +15,13 @@ updates: update-types: - minor - patch - # Group TypeScript-related packages - typescript: - patterns: - - "typescript" - - "@types/*" - update-types: - - minor - - patch - # Group webpack and build tools - build-tools: - patterns: - - "webpack*" - - "*-loader" - - "ts-loader" - update-types: - - minor - - patch - # Group testing packages - testing: - patterns: - - "@vscode/test-*" - - "mocha" - - "glob" - update-types: - - minor - - patch # Group ESLint and linting packages linting: patterns: - "eslint" - "eslint-*" - "@eslint/*" - - "@typescript-eslint/*" + - "typescript-eslint" update-types: - minor - patch diff --git a/.npmrc b/.npmrc deleted file mode 100644 index 42fba9562..000000000 --- a/.npmrc +++ /dev/null @@ -1 +0,0 @@ -preid=preview diff --git a/.vscode/launch.json b/.vscode/launch.json index 7e0e15ea1..342670820 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -38,7 +38,7 @@ "--extensionTestsPath=${workspaceFolder}/out/test/suite/index" ], "outFiles": ["${workspaceFolder}/out/test/**/*.js"], - "preLaunchTask": "npm: test-compile" + "preLaunchTask": "npm: compile:test" }, { "name": "Run Web Extension", diff --git a/CLAUDE.md b/CLAUDE.md index bd336d025..029c2e08f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -34,7 +34,7 @@ pnpm test pnpm test:web # Compile tests only -pnpm test-compile +pnpm compile:test ``` ## Running Tests diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7f96a723f..fc20e4d8c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -35,11 +35,11 @@ Thank you for your interest in contributing to the Prettier VS Code extension! F ```bash pnpm install # Install dependencies pnpm compile # Build for development (esbuild + type checking) +pnpm compile:test # Compile tests only pnpm watch # Build and watch for changes pnpm lint # Run ESLint pnpm prettier # Format code with Prettier pnpm test # Run tests -pnpm test-compile # Compile tests only ``` ### Running the Extension diff --git a/docs/publishing.md b/docs/publishing.md index c4a65ec27..08ddda84d 100644 --- a/docs/publishing.md +++ b/docs/publishing.md @@ -16,51 +16,60 @@ Publishing is automated via GitHub Actions. When you push a tag starting with `v - Write access to the repository - `MARKETPLACE_TOKEN` secret configured in GitHub (for automated publishing) +## Release Commands + +All releases are handled through the `pnpm release` command: + +```bash +# Patch release (bug fixes): 11.0.0 → 11.0.1 +pnpm release patch + +# Minor release (new features): 11.0.0 → 11.1.0 +pnpm release minor + +# Major release (breaking changes): 11.0.0 → 12.0.0 +pnpm release major + +# Preview release: 11.0.0 → 11.1.0-preview.1 +pnpm release preview +``` + ## Version Types -| Type | Example | When to Use | -| ---------- | ------------------- | --------------------------------- | -| Major | `11.0.0` → `12.0.0` | Breaking changes | -| Minor | `11.0.0` → `11.1.0` | New features, backward compatible | -| Patch | `11.0.0` → `11.0.1` | Bug fixes | -| Prerelease | `12.0.0-preview.1` | Testing before stable release | +| Type | Command | Example | +| ------- | ---------------------- | ----------------------------- | +| Major | `pnpm release major` | `11.0.0` → `12.0.0` | +| Minor | `pnpm release minor` | `11.0.0` → `11.1.0` | +| Patch | `pnpm release patch` | `11.0.0` → `11.0.1` | +| Preview | `pnpm release preview` | `11.0.0` → `11.1.0-preview.1` | ## Publishing a Stable Release ### 1. Update the Changelog -Add your changes under the `[unreleased]` section in `CHANGELOG.md` as you make changes: +Add your changes under the `[Unreleased]` section in `CHANGELOG.md` as you make changes: ```markdown -## [unreleased] +## [Unreleased] - Added new feature X - Fixed bug Y ``` -### 2. Bump the Version - -Run one of these commands depending on the release type: +### 2. Run the Release Command ```bash -# Patch release (bug fixes): 11.0.0 → 11.0.1 -npm version patch - -# Minor release (new features): 11.0.0 → 11.1.0 -npm version minor - -# Major release (breaking changes): 11.0.0 → 12.0.0 -npm version major +pnpm release patch # or minor, major ``` This automatically: -1. Updates `version` in `package.json` -2. Runs the `version` script which updates `CHANGELOG.md` (converts `[unreleased]` to the new version number) -3. Stages both `package.json` and `CHANGELOG.md` +1. Calculates the new version number +2. Updates `version` in `package.json` +3. Updates `CHANGELOG.md` (converts `[Unreleased]` to the new version) 4. Creates a git commit with message `v` 5. Creates a git tag `v` -6. Pushes the commit and tag to origin (via `postversion` script) +6. Pushes the commit and tag to origin The GitHub Actions workflow will then automatically: @@ -68,46 +77,36 @@ The GitHub Actions workflow will then automatically: - Create a GitHub Release with auto-generated notes - Publish to the VS Code Marketplace -## Publishing a Prerelease +## Publishing a Preview Release -Prereleases allow testing new features before a stable release. VS Code users can opt-in to receive prerelease versions. +Preview releases allow testing new features before a stable release. VS Code users can opt-in to receive preview versions. -### 1. Bump to a Prerelease Version +### Creating a Preview ```bash -# First prerelease for a new major version: 11.0.0 → 12.0.0-preview.0 -npm version premajor - -# First prerelease for a new minor version: 11.0.0 → 11.1.0-preview.0 -npm version preminor - -# First prerelease for a patch: 11.0.0 → 11.0.1-preview.0 -npm version prepatch +# First preview (from stable): 11.0.0 → 11.1.0-preview.1 +pnpm release preview -# Increment existing prerelease: 12.0.0-preview.0 → 12.0.0-preview.1 -npm version prerelease +# Subsequent previews: 11.1.0-preview.1 → 11.1.0-preview.2 +pnpm release preview ``` -> The `.npmrc` file configures `preview` as the default prerelease identifier. - -> **Note:** The version script automatically skips changelog updates for prereleases. The `[unreleased]` section is preserved until the final stable release. - -The commit and tag are automatically pushed. The workflow detects prerelease tags (containing `-preview`, `-beta`, `-alpha`, or `-rc`) and: +The workflow detects preview tags and: - Packages with `vsce package --pre-release` - Creates a GitHub Release marked as prerelease - Publishes to Marketplace with `vsce publish --pre-release` -### 2. Promote to Stable +### Promoting to Stable When ready to release the stable version: ```bash -# Release the stable version: 12.0.0-preview.3 → 12.0.0 -npm version major # or minor/patch depending on what changed +# From preview to stable: 11.1.0-preview.3 → 11.1.0 +pnpm release minor ``` -This will update the changelog, moving all `[unreleased]` entries to the new stable version, and push automatically. +This will update the changelog (moving all `[Unreleased]` entries to the new stable version) and push automatically. ## Manual Publishing (Emergency) @@ -149,6 +148,6 @@ If `package.json` version doesn't match the tag: git tag -d v12.0.0 git push origin :refs/tags/v12.0.0 -# Fix version and re-tag (will push automatically) -npm version 12.0.0 +# Re-run the release +pnpm release minor # or appropriate type ``` diff --git a/esbuild.mjs b/esbuild.mjs index 4009fe235..e0ae6bd78 100644 --- a/esbuild.mjs +++ b/esbuild.mjs @@ -5,6 +5,13 @@ import path from "path"; const production = process.argv.includes("--production"); const watch = process.argv.includes("--watch"); +// Clean dist directory (skip in watch mode) +if (!watch) { + if (fs.existsSync("dist")) { + fs.rmSync("dist", { recursive: true, force: true }); + } +} + const extensionPackage = JSON.parse( fs.readFileSync(new URL("./package.json", import.meta.url), "utf-8"), ); @@ -47,7 +54,11 @@ const browserAliasPlugin = { }, }; -// Node extension configuration + +/** + * Node extension configuration + * @type {import('esbuild').BuildOptions} + */ const nodeConfig = { entryPoints: ["src/extension.ts"], bundle: true, @@ -89,7 +100,11 @@ const browserShimsPlugin = { }, }; -// Browser/web extension configuration + +/** + * Browser/web extension configurationn + * @type {import('esbuild').BuildOptions} + */ const browserConfig = { entryPoints: ["src/extension.ts"], bundle: true, @@ -122,7 +137,10 @@ const browserConfig = { ], }; -// Web test bundle configuration +/** + * Web test bundle configuration + * @type {import('esbuild').BuildOptions} + */ const webTestConfig = { entryPoints: ["src/test/web/suite/index.ts"], bundle: true, diff --git a/package.json b/package.json index 2af973755..50c895b19 100644 --- a/package.json +++ b/package.json @@ -69,21 +69,19 @@ "main": "./dist/extension", "browser": "./dist/web-extension", "scripts": { - "clean": "node ./scripts/clean.mjs", + "compile:test": "tsc -p ./ && node esbuild.mjs", + "check-types": "tsc --noEmit", + "chrome": "pnpm compile && vscode-test-web --browserType=chromium --extensionDevelopmentPath=. .", + "compile": "pnpm check-types && node esbuild.mjs", "lint": "eslint src *.mjs scripts", - "pretest": "pnpm test-compile && node scripts/install-fixtures.mjs", + "pretest": "pnpm compile:test && node scripts/install-fixtures.mjs", "prettier": "prettier --write .", - "compile": "pnpm check-types && node esbuild.mjs", - "check-types": "tsc --noEmit", - "watch": "pnpm run --parallel watch:esbuild watch:tsc", + "release": "node ./scripts/release.mjs", + "test:web": "pnpm compile:test && node ./out/test/web/runTests.js", + "test": "node ./out/test/runTests.js", "watch:esbuild": "node esbuild.mjs --watch", "watch:tsc": "tsc --noEmit --watch --project tsconfig.json", - "test-compile": "pnpm clean && tsc -p ./ && pnpm compile", - "test": "node ./out/test/runTests.js", - "version": "node ./scripts/version.mjs && git add CHANGELOG.md", - "postversion": "git push origin main --tags", - "chrome": "pnpm compile && vscode-test-web --browserType=chromium --extensionDevelopmentPath=. .", - "test:web": "pnpm test-compile && node ./out/test/web/runTests.js" + "watch": "pnpm run --parallel watch:esbuild watch:tsc" }, "devDependencies": { "@eslint/js": "^9.28.0", @@ -110,7 +108,7 @@ "process": "^0.11.10", "sinon": "^17.0.1", "tmp": "^0.2.3", - "typescript": "^4.6.3", + "typescript": "^5.9.3", "typescript-eslint": "^8.33.0", "util": "^0.12.4" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 38f6581ee..417b4da3f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -97,11 +97,11 @@ importers: specifier: ^0.2.3 version: 0.2.5 typescript: - specifier: ^4.6.3 - version: 4.9.5 + specifier: ^5.9.3 + version: 5.9.3 typescript-eslint: specifier: ^8.33.0 - version: 8.47.0(eslint@9.39.1)(typescript@4.9.5) + version: 8.47.0(eslint@9.39.1)(typescript@5.9.3) util: specifier: ^0.12.4 version: 0.12.5 @@ -2415,9 +2415,9 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - typescript@4.9.5: - resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} - engines: {node: '>=4.2.0'} + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} hasBin: true uc.micro@2.1.0: @@ -2997,41 +2997,41 @@ snapshots: '@types/vscode@1.106.1': {} - '@typescript-eslint/eslint-plugin@8.47.0(@typescript-eslint/parser@8.47.0(eslint@9.39.1)(typescript@4.9.5))(eslint@9.39.1)(typescript@4.9.5)': + '@typescript-eslint/eslint-plugin@8.47.0(@typescript-eslint/parser@8.47.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.47.0(eslint@9.39.1)(typescript@4.9.5) + '@typescript-eslint/parser': 8.47.0(eslint@9.39.1)(typescript@5.9.3) '@typescript-eslint/scope-manager': 8.47.0 - '@typescript-eslint/type-utils': 8.47.0(eslint@9.39.1)(typescript@4.9.5) - '@typescript-eslint/utils': 8.47.0(eslint@9.39.1)(typescript@4.9.5) + '@typescript-eslint/type-utils': 8.47.0(eslint@9.39.1)(typescript@5.9.3) + '@typescript-eslint/utils': 8.47.0(eslint@9.39.1)(typescript@5.9.3) '@typescript-eslint/visitor-keys': 8.47.0 eslint: 9.39.1 graphemer: 1.4.0 ignore: 7.0.5 natural-compare: 1.4.0 - ts-api-utils: 2.1.0(typescript@4.9.5) - typescript: 4.9.5 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.47.0(eslint@9.39.1)(typescript@4.9.5)': + '@typescript-eslint/parser@8.47.0(eslint@9.39.1)(typescript@5.9.3)': dependencies: '@typescript-eslint/scope-manager': 8.47.0 '@typescript-eslint/types': 8.47.0 - '@typescript-eslint/typescript-estree': 8.47.0(typescript@4.9.5) + '@typescript-eslint/typescript-estree': 8.47.0(typescript@5.9.3) '@typescript-eslint/visitor-keys': 8.47.0 debug: 4.4.3(supports-color@8.1.1) eslint: 9.39.1 - typescript: 4.9.5 + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.47.0(typescript@4.9.5)': + '@typescript-eslint/project-service@8.47.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.47.0(typescript@4.9.5) + '@typescript-eslint/tsconfig-utils': 8.47.0(typescript@5.9.3) '@typescript-eslint/types': 8.47.0 debug: 4.4.3(supports-color@8.1.1) - typescript: 4.9.5 + typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -3040,28 +3040,28 @@ snapshots: '@typescript-eslint/types': 8.47.0 '@typescript-eslint/visitor-keys': 8.47.0 - '@typescript-eslint/tsconfig-utils@8.47.0(typescript@4.9.5)': + '@typescript-eslint/tsconfig-utils@8.47.0(typescript@5.9.3)': dependencies: - typescript: 4.9.5 + typescript: 5.9.3 - '@typescript-eslint/type-utils@8.47.0(eslint@9.39.1)(typescript@4.9.5)': + '@typescript-eslint/type-utils@8.47.0(eslint@9.39.1)(typescript@5.9.3)': dependencies: '@typescript-eslint/types': 8.47.0 - '@typescript-eslint/typescript-estree': 8.47.0(typescript@4.9.5) - '@typescript-eslint/utils': 8.47.0(eslint@9.39.1)(typescript@4.9.5) + '@typescript-eslint/typescript-estree': 8.47.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.47.0(eslint@9.39.1)(typescript@5.9.3) debug: 4.4.3(supports-color@8.1.1) eslint: 9.39.1 - ts-api-utils: 2.1.0(typescript@4.9.5) - typescript: 4.9.5 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 transitivePeerDependencies: - supports-color '@typescript-eslint/types@8.47.0': {} - '@typescript-eslint/typescript-estree@8.47.0(typescript@4.9.5)': + '@typescript-eslint/typescript-estree@8.47.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.47.0(typescript@4.9.5) - '@typescript-eslint/tsconfig-utils': 8.47.0(typescript@4.9.5) + '@typescript-eslint/project-service': 8.47.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.47.0(typescript@5.9.3) '@typescript-eslint/types': 8.47.0 '@typescript-eslint/visitor-keys': 8.47.0 debug: 4.4.3(supports-color@8.1.1) @@ -3069,19 +3069,19 @@ snapshots: is-glob: 4.0.3 minimatch: 9.0.5 semver: 7.7.3 - ts-api-utils: 2.1.0(typescript@4.9.5) - typescript: 4.9.5 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.47.0(eslint@9.39.1)(typescript@4.9.5)': + '@typescript-eslint/utils@8.47.0(eslint@9.39.1)(typescript@5.9.3)': dependencies: '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1) '@typescript-eslint/scope-manager': 8.47.0 '@typescript-eslint/types': 8.47.0 - '@typescript-eslint/typescript-estree': 8.47.0(typescript@4.9.5) + '@typescript-eslint/typescript-estree': 8.47.0(typescript@5.9.3) eslint: 9.39.1 - typescript: 4.9.5 + typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -5043,9 +5043,9 @@ snapshots: toidentifier@1.0.1: {} - ts-api-utils@2.1.0(typescript@4.9.5): + ts-api-utils@2.1.0(typescript@5.9.3): dependencies: - typescript: 4.9.5 + typescript: 5.9.3 tslib@2.8.1: {} @@ -5080,18 +5080,18 @@ snapshots: tunnel: 0.0.6 underscore: 1.13.7 - typescript-eslint@8.47.0(eslint@9.39.1)(typescript@4.9.5): + typescript-eslint@8.47.0(eslint@9.39.1)(typescript@5.9.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.47.0(@typescript-eslint/parser@8.47.0(eslint@9.39.1)(typescript@4.9.5))(eslint@9.39.1)(typescript@4.9.5) - '@typescript-eslint/parser': 8.47.0(eslint@9.39.1)(typescript@4.9.5) - '@typescript-eslint/typescript-estree': 8.47.0(typescript@4.9.5) - '@typescript-eslint/utils': 8.47.0(eslint@9.39.1)(typescript@4.9.5) + '@typescript-eslint/eslint-plugin': 8.47.0(@typescript-eslint/parser@8.47.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3) + '@typescript-eslint/parser': 8.47.0(eslint@9.39.1)(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.47.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.47.0(eslint@9.39.1)(typescript@5.9.3) eslint: 9.39.1 - typescript: 4.9.5 + typescript: 5.9.3 transitivePeerDependencies: - supports-color - typescript@4.9.5: {} + typescript@5.9.3: {} uc.micro@2.1.0: {} diff --git a/scripts/clean.mjs b/scripts/clean.mjs deleted file mode 100644 index 04288529f..000000000 --- a/scripts/clean.mjs +++ /dev/null @@ -1,14 +0,0 @@ -import fs from "fs"; -import path from "path"; -import { fileURLToPath } from "url"; - -const __dirname = path.dirname(fileURLToPath(import.meta.url)); - -const folders = ["../dist", "../out"]; - -folders.forEach((folder) => { - const dir = path.join(__dirname, folder); - if (fs.existsSync(dir)) { - fs.rmSync(dir, { recursive: true, force: true }); - } -}); diff --git a/scripts/release.mjs b/scripts/release.mjs new file mode 100644 index 000000000..9b4f1891f --- /dev/null +++ b/scripts/release.mjs @@ -0,0 +1,217 @@ +#!/usr/bin/env node +/** + * Automated release script for prettier-vscode + * + * Usage: + * pnpm release major - Bump major version (11.0.0 -> 12.0.0) + * pnpm release minor - Bump minor version (11.0.0 -> 11.1.0) + * pnpm release patch - Bump patch version (11.0.0 -> 11.0.1) + * pnpm release preview - Create preview release (11.0.0 -> 11.1.0-preview.1) + */ +import fs from "fs/promises"; +import { execSync } from "child_process"; + +const RELEASE_TYPES = ["major", "minor", "patch", "preview"]; + +function exec(cmd, options = {}) { + console.log(`$ ${cmd}`); + return execSync(cmd, { stdio: "inherit", ...options }); +} + +function execQuiet(cmd) { + return execSync(cmd, { encoding: "utf8" }).trim(); +} + +function parseVersion(version) { + // Handle prerelease versions like "11.1.0-preview.1" + const match = version.match(/^(\d+)\.(\d+)\.(\d+)(?:-([a-z]+)\.(\d+))?$/i); + if (!match) { + throw new Error(`Invalid version format: ${version}`); + } + return { + major: parseInt(match[1], 10), + minor: parseInt(match[2], 10), + patch: parseInt(match[3], 10), + prerelease: match[4] || null, + prereleaseNum: match[5] ? parseInt(match[5], 10) : null, + }; +} + +function formatVersion({ major, minor, patch, prerelease, prereleaseNum }) { + let version = `${major}.${minor}.${patch}`; + if (prerelease) { + version += `-${prerelease}.${prereleaseNum}`; + } + return version; +} + +async function updateChangelog(version) { + const CHANGELOG = "CHANGELOG.md"; + const isPrerelease = version.includes("-"); + + if (isPrerelease) { + console.log(`Skipping changelog update for prerelease version ${version}`); + return; + } + + const data = await fs.readFile(CHANGELOG, "utf8"); + const updated = data.replace( + /## \[Unreleased\](?!\s*## )/, // Non-empty Unreleased block + `## [Unreleased]\n\n## [${version}]`, + ); + + if (updated !== data) { + await fs.writeFile(CHANGELOG, updated); + console.log(`Updated CHANGELOG.md with [${version}]`); + } else { + console.log("No changelog changes to record"); + } +} + +function calculateNewVersion(currentVersion, releaseType) { + const v = parseVersion(currentVersion); + const isCurrentPrerelease = v.prerelease !== null; + + switch (releaseType) { + case "major": + // If current is prerelease of this major, just drop prerelease suffix + // e.g., 12.0.0-preview.1 -> 12.0.0 + if (isCurrentPrerelease && v.minor === 0 && v.patch === 0) { + return formatVersion({ ...v, prerelease: null, prereleaseNum: null }); + } + // Otherwise bump major + return formatVersion({ + major: v.major + 1, + minor: 0, + patch: 0, + prerelease: null, + prereleaseNum: null, + }); + + case "minor": + // If current is prerelease of this minor, just drop prerelease suffix + // e.g., 11.1.0-preview.1 -> 11.1.0 + if (isCurrentPrerelease && v.patch === 0) { + return formatVersion({ ...v, prerelease: null, prereleaseNum: null }); + } + // Otherwise bump minor + return formatVersion({ + major: v.major, + minor: v.minor + 1, + patch: 0, + prerelease: null, + prereleaseNum: null, + }); + + case "patch": + // If current is prerelease of this patch, just drop prerelease suffix + // e.g., 11.0.1-preview.1 -> 11.0.1 + if (isCurrentPrerelease) { + return formatVersion({ ...v, prerelease: null, prereleaseNum: null }); + } + // Otherwise bump patch + return formatVersion({ + major: v.major, + minor: v.minor, + patch: v.patch + 1, + prerelease: null, + prereleaseNum: null, + }); + + case "preview": + // If already a preview, increment the preview number + if (isCurrentPrerelease && v.prerelease === "preview") { + return formatVersion({ + ...v, + prereleaseNum: v.prereleaseNum + 1, + }); + } + // Otherwise, bump minor and start preview.1 + return formatVersion({ + major: v.major, + minor: v.minor + 1, + patch: 0, + prerelease: "preview", + prereleaseNum: 1, + }); + + default: + throw new Error(`Unknown release type: ${releaseType}`); + } +} + +async function main() { + const releaseType = process.argv[2]; + + if (!releaseType || !RELEASE_TYPES.includes(releaseType)) { + console.error("Usage: pnpm release "); + console.error(""); + console.error("Examples:"); + console.error(" pnpm release major - 11.0.0 -> 12.0.0"); + console.error(" pnpm release minor - 11.0.0 -> 11.1.0"); + console.error(" pnpm release patch - 11.0.0 -> 11.0.1"); + console.error(" pnpm release preview - 11.0.0 -> 11.1.0-preview.1"); + process.exit(1); + } + + // Check for uncommitted changes + const status = execQuiet("git status --porcelain"); + if (status) { + console.error("Error: Working directory is not clean."); + console.error("Please commit or stash your changes before releasing."); + process.exit(1); + } + + // Stable releases must be on main branch + const currentBranch = execQuiet("git rev-parse --abbrev-ref HEAD"); + if (releaseType !== "preview" && currentBranch !== "main") { + console.error( + `Error: ${releaseType} releases must be run from the main branch.`, + ); + console.error(`Current branch: ${currentBranch}`); + console.error("\nTo release a preview from this branch, use:"); + console.error(" pnpm release preview"); + process.exit(1); + } + + // Read current version + const packageJson = JSON.parse(await fs.readFile("package.json", "utf8")); + const currentVersion = packageJson.version; + const newVersion = calculateNewVersion(currentVersion, releaseType); + + console.log(`\nRelease: ${releaseType}`); + console.log(`Current version: ${currentVersion}`); + console.log(`New version: ${newVersion}\n`); + + // Update package.json + packageJson.version = newVersion; + await fs.writeFile( + "package.json", + JSON.stringify(packageJson, null, 2) + "\n", + ); + console.log("Updated package.json"); + + // Update changelog (skip for prereleases) + await updateChangelog(newVersion); + + // Stage changes + exec("git add package.json CHANGELOG.md"); + + // Create commit and tag + const tagName = `v${newVersion}`; + exec(`git commit -m "${tagName}"`); + exec(`git tag ${tagName}`); + + console.log(`\nCreated commit and tag: ${tagName}`); + + // Push to origin + exec(`git push origin ${currentBranch} --tags`); + + console.log(`\nRelease ${tagName} pushed successfully!`); + console.log("GitHub Actions will now build, test, and publish the release."); +} + +main().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/scripts/version.mjs b/scripts/version.mjs deleted file mode 100644 index c0f122a89..000000000 --- a/scripts/version.mjs +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Replace [Unreleased] with [${package.version}] - * sed like. - * - * For prereleases (versions containing -), skip changelog update - * since the final release should include all unreleased changes. - */ -import fs from "fs/promises"; - -const v = process.env.npm_package_version; -const CHANGELOG = "CHANGELOG.md"; - -// Skip changelog update for prereleases (e.g., 12.0.0-preview.1) -const isPrerelease = v.includes("-"); - -if (isPrerelease) { - console.log(`CHANGELOG: Skipping update for prerelease version ${v}`); - process.exit(0); -} - -const data = await fs.readFile(CHANGELOG, "utf8"); -const updated = data.replace( - /## \[Unreleased\](?!\s*## )/, // None empty Unreleased block - `## [Unreleased]\n\n## [${v}]`, -); - -if (updated !== data) { - console.log(`CHANGELOG: [Unreleased] updated with [${v}]`); -} - -await fs.writeFile(CHANGELOG, updated); diff --git a/tsconfig.json b/tsconfig.json index 79d33fb0b..4213ba76a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,7 +10,8 @@ "removeComments": true, "lib": ["ES2020"], "sourceMap": true, - "rootDir": "src" + "rootDir": "src", + "skipLibCheck": true }, "exclude": ["node_modules", ".vscode-test", "test-fixtures"], "include": [