Skip to content

Releases

v2.3.0

Choose a tag to compare

@prosdev prosdev released this 27 Oct 19:29

Dependency updates now come from GitHub's built-in Dependabot, with no app or third-party account to set up, and CI checks every install for known vulnerabilities.

Changed

  • Dependabot replaces Renovate (#23). .github/dependabot.yml opens update PRs every Monday, only for releases at least 3 days old. Minor and patch dev-tool updates arrive as one PR, and GitHub Actions updates as another. Exact pins stay exact. The Renovate app now requires registering a Mend account, which a starter template shouldn't require.
  • Some versions are raised by hand. Dependabot doesn't update the Node version in devEngines.runtime, the zizmor version in ci.yml, or @types/node majors. There is no weekly lockfile refresh.
  • CI runs pnpm audit --audit-level high (#23) after installing, and fails on high or critical advisories. GitHub's Dependabot alerts can't see this template's dependencies: pnpm 12 writes a two-document pnpm-lock.yaml, and GitHub's dependency graph reads only the first document (dependabot-core#15904).
  • No known advisories. fast-uri 3.1.9, which arrives through commitlint, clears two moderate and two low advisories.

Fixed

  • The marker check ignores prose (#22). Lines like ## Example: usage or // For example: no longer fail the template's own CI. This only affects contributors to the template.

Changes for existing projects

Before (v2.2.0) After (v2.3.0)
renovate.json .github/dependabot.yml; uninstall the Renovate app if you installed it
ci.yml # renovate: comment on ZIZMOR_VERSION a plain comment; raise the version by hand
no audit step ci.yml: pnpm audit --audit-level high after "Install dependencies"
no zizmor config .github/zizmor.yml: rules.dependabot-cooldown.config.days: 3; without it, zizmor fails CI on the 3-day cooldown

Notes:

  • Keep three settings equal: minimumReleaseAge in pnpm-workspace.yaml, cooldown.default-days in dependabot.yml, and days in .github/zizmor.yml.
  • Run pnpm audit before adding the CI step, and fix what it finds first. Moderate and low advisories don't fail CI.

v2.2.0

Choose a tag to compare

@prosdev prosdev released this 27 Oct 17:16

The examples are named by role, each owns its own tools, and every example-specific line is marked. This prepares the template for a scaffolding CLI that generates any combination of examples. Nothing here forces an existing project to change.

Changed

  • Examples named by role (#17, #18):
    • shared and lib: a library other tools import, built on shared code
    • cli: a command people run
    • web: a library loaded in web pages by a script tag
  • The web example owns its tools (#19). Playwright, axe, size-limit, the Playwright config and the browser tsconfig live in packages/web, so deleting the folder removes them. pnpm test:browser and pnpm size run through turbo, which builds first, and pass CI and PLAYWRIGHT_* through to Playwright.
  • Example markers (#20, #21). Every line that belongs to one example sits between <!-- example:NAME -->, # example:NAME or // example:NAME markers. A template-only CI check keeps it that way, and CONTRIBUTING.md documents the rules. Removing any combination of examples leaves a repo that passes its own CI; all seven combinations were tested.
  • web has a unit test (#21), so a web-only project passes pnpm test.

Node stays pinned at 26.12.1. Moving to the first Node 26 LTS release comes in a later update.

Changes for existing projects

These changes only matter if you want your project to match the template's new layout.

Before (v2.1.x) After (v2.2.0)
packages/core, packages/utils (@monorepo/core, @monorepo/utils) packages/shared (@monorepo/shared); update imports
packages/feature-a (@monorepo/feature-a) packages/lib (@monorepo/lib)
packages/cli-example, bin cli-example packages/cli (@monorepo/cli), bin cli
packages/browser-example, window.BrowserExample, data-browser-example packages/web (@monorepo/web), window.Web, data-web
root playwright.config.ts, tsconfig.browser.json, and the @playwright/test, @axe-core/playwright, size-limit, @size-limit/file devDependencies inside packages/web
knip.json deleted; knip finds the browser tests through packages/web/playwright.config.ts
root "test:browser": "playwright test", size via pnpm -r turbo test:browser, turbo size; turbo.json adds both tasks (test:browser with cache: false and passThroughEnv: ["CI", "PLAYWRIGHT_*"])
ci.yml pnpm exec playwright … pnpm --filter @monorepo/web exec playwright …

Notes:

  • Without the turbo passThroughEnv, Playwright's forbidOnly and its GitHub reporter turn off silently in CI.
  • Don't port the markers or scripts/check-examples.*. They are template machinery for generating projects.
  • Projects still on v2.1.0 should also take v2.1.1's engines.pnpm >=12 guard.

v2.1.1

Choose a tag to compare

@prosdev prosdev released this 27 Oct 10:46

A patch release: the template has a new name, refuses old pnpm, and its pnpm and publishing docs are corrected.

Changed

  • Renamed to ts-monorepo-starter (#15). GitHub redirects the old prosdevlab/ts-monorepo URLs. The root package name, the README's clone commands and folder tree, and the CONTRIBUTING heading follow.
  • pnpm older than 12 is refused (#16). Root engines.pnpm is >=12. pnpm 9 used to install silently with itself, ignoring the pinned pnpm, the 3-day release rule and the pinned Node, and rewriting the lockfile. It now stops with ERR_PNPM_UNSUPPORTED_ENGINE.
  • pnpm 10 docs corrected (#16). pnpm 10.26 or newer switches itself to pnpm 12 from packageManager, so a global pnpm 12 install isn't needed. The ENOEXEC failure came from a Volta-managed pnpm 10, and its fix is volta install pnpm@12.15.0.
  • The publishing steps add a repository field (#16). npm provenance checks it against the repository that publishes.

Changes for existing projects

  1. Add "pnpm": ">=12" to the root package.json engines.
  2. Before publishing a package, add "repository": {"type": "git", "url": "git+https://github.com/<owner>/<repo>.git", "directory": "packages/<name>"} to its package.json.
  3. Optionally, copy the corrected install step and the ENOEXEC and ERR_PNPM_UNSUPPORTED_ENGINE troubleshooting entries from README.md and AGENTS.md.

The rename changes nothing in existing projects.

v2.1.0

Choose a tag to compare

@prosdev prosdev released this 27 Oct 08:59

Parity with the tooling used by the most-starred TypeScript library monorepos, plus a security fix for the release workflow.

Security

  • The release workflow could run code from a fork with write access (#11). It triggered on workflow_run for CI runs on a branch named main. GitHub matches that name against the branch the run came from, so a fork's PR from its own main branch qualified. Templates v1.1.0 through v2.0.0 then checked out the fork's commit and ran its install scripts in jobs holding write tokens, and in the publish job, npm publishing credentials. Release now runs only as the last job of CI, on pushes to main. v1.0.0 also fired for fork PRs, but it checked out main, so it never ran fork code.

Added

  • Published packages support Node.js 24 and later (#9). The tests run on Node 24 and 26, on Linux, macOS and Windows. Development stays on the pinned Node 26.12.1.
  • Preview packages for every PR (#14). pkg.pr.new publishes each package per commit and comments the install commands. It's off until the PKG_PR_NEW repository variable is set, and fork PRs never run it.
  • Workflow security audit (#12). zizmor checks every workflow on every PR and fails on any finding.
  • Unused code checks (#13). knip fails on unused files, exports and dependencies, and on undeclared dependencies.
  • Repository health files (#10): issue forms, a PR template, SECURITY.md (private vulnerability reporting) and .editorconfig.

Changed

  • npm publishing is gated by the NPM_PUBLISH repository variable. Packages are no longer marked "private": true, because previews skip private packages.
  • The npm trusted publisher names ci.yml, not release.yml: npm checks the calling workflow.
  • CI on a PR cancels the PR's older run when a new push arrives.

Changes for existing projects

  1. Security (projects from v1.0.0 to v2.0.0): in release.yml, replace the workflow_run trigger with workflow_call: and remove the ref: ${{ github.event.workflow_run.head_sha ... }} lines. Gate select-mode on github.ref == 'refs/heads/main'. In ci.yml, add the release job: needs on every other job, if: github.event_name == 'push' && github.ref == 'refs/heads/main', the three write permissions, and uses: ./.github/workflows/release.yml. If you have a trusted publisher on npmjs.com, change its workflow file to ci.yml.
  2. If you publish to npm today, set the repository variable NPM_PUBLISH to true when you take step 1's release.yml. The pack job now requires it, and without it releases stop.
  3. Node 24 support:
    • Set engines.node to >=24 in Node packages.
    • In the root tsconfig.json, extend @tsconfig/node24.
    • Use @types/node 24.x in packages, and add the two Renovate @types/node rules.
    • Replace rm -rf dist and chmod +x with the node -e forms.
    • Add the compat job.
  4. zizmor: add the workflows job with ZIZMOR_VERSION, and customManagers:githubActionsVersions to the Renovate extends. Make release need workflows.
  5. knip: add knip 6.43.0, the check:unused script, the CI step and knip.json. Then fix what it reports, or add entries for false positives.
  6. Previews (optional): install the pkg.pr.new GitHub App and set PKG_PR_NEW to true. Add pkg-pr-new 0.0.91 and the preview job. Keep "private": true only on packages that must never be previewed or published. Leave previews off in private repositories, because previews are public.
  7. Health files: copy .github/ISSUE_TEMPLATE/, .github/PULL_REQUEST_TEMPLATE.md, SECURITY.md and .editorconfig. Then turn on Settings → Advanced Security → Private vulnerability reporting, and set the reply time in SECURITY.md.

v2.0.0

Choose a tag to compare

@prosdev prosdev released this 26 Oct 20:12

A new generation of the template:

  • Node 26 and TypeScript 7, the native compiler.
  • Every package builds with the TypeScript compiler; tsup is gone.
  • pnpm 12 pins the Node version, which replaces Volta.
  • The README has diagrams of how the pieces fit together.

Changed

  • TypeScript 7.0.2. It ships no JavaScript compiler API, and tsup calls that API, so tsup is removed.
  • tsc builds every package. It writes one ES module and one declaration file per source file, with maps. Build scripts delete dist first.
  • Rollup builds the script-tag file. Rollup 4.65.0 with terser bundles tsc's output for the browser example. The minified file dropped from 731 B to 511 B.
  • Node 26. It becomes LTS on 2026-10-28. The template uses the @tsconfig/node26 preset, @types/node 26.6.5 and engines.node >=26. Node packages no longer see browser globals: DOM is set only in the browser tests' config.
  • pnpm 12.15.0 replaces Volta, which is unmaintained.
    • devEngines.runtime pins Node 26.12.1. pnpm downloads that Node and records it in the lockfile, and CI reads the same pin.
    • npm and npx refuse to run in the repository unless Node matches the pin exactly. Use pnpm dlx or pnpm exec instead.
  • Stricter installs.
    • minimumReleaseAgeStrict: true makes a too-new version fail the install.
    • pnpm 12 rejects misspelled settings.
    • Install scripts run only for packages listed in allowBuilds.
  • Renovate holds @types/node to 26.x.
  • README diagrams cover the package graph and build order, build outputs, where each version is pinned, the dependency update flow, and the release jobs with their permissions.

Changes for existing projects

A project created from v1.2.1 would change the following to match:

  1. Install pnpm 12: npm install -g pnpm@12.15.0. pnpm 10 can't switch itself to pnpm 12.
  2. package.json (root).
    • Set packageManager to pnpm@12.15.0 and engines.node to >=26.
    • Remove the volta block.
    • Add "devEngines": { "runtime": { "name": "node", "version": "26.12.1", "onFail": "download" } }.
    • Don't add devEngines.packageManager: npm pack rejects it.
  3. TypeScript 7.
    • Pin typescript to 7.0.2. Replace @tsconfig/node-lts with @tsconfig/node26@26.0.1, and pin @types/node to 26.6.5.
    • In the root tsconfig.json, extend @tsconfig/node26/tsconfig.json, and remove composite and the lib override.
    • Remove project references from package tsconfigs.
    • Put "lib": ["ES2025", "DOM"] in any tsconfig that needs browser globals.
  4. Builds.
    • Remove tsup and every tsup.config.ts.
    • Set each package's build to rm -rf dist && tsc -p tsconfig.json, and its dev to tsc -p tsconfig.json --watch.
    • Command packages append && chmod +x dist/cli.js to build.
    • Script-tag builds append && rollup -c, with a rollup.config.js that bundles dist/index.js as iife through @rollup/plugin-terser.
    • Imports between source files need file extensions (./thing.js), which NodeNext already enforces.
  5. pnpm-workspace.yaml. Add minimumReleaseAgeStrict: true, and an allowBuilds map with an entry for each dependency that has an install script (esbuild: false here). Then reinstall.
  6. Renovate. Change the @types/node rule's allowedVersions to 26.x.

Declarations change shape: one .d.ts per source file, where tsup produced one bundled file per entry. Run pnpm check:packages to confirm the packages still resolve for consumers.

v1.2.1

Choose a tag to compare

@prosdev prosdev released this 26 Oct 19:26

Node 24.22.1, with every Node 24 security fix to date, plus current pnpm 10 and dev tool releases. Renovate now keeps @types/node on the Node major the template runs.

Changed

  • Node 24.12.0 → 24.22.1. This picks up five security releases: 24.13.0, 24.14.1, 24.17.0, 24.18.1 and 24.22.1. CI reads the same version from the Volta pin. It bundles npm 11.19.1, which supports trusted publishing.
  • pnpm 10.26.2 → 10.34.8.
  • Dev tools: commitlint 21.2.4, Vitest 5.0.5, Playwright 1.64.1, Turborepo 2.11.10, axe 4.14.0, Biome 2.6.2, size-limit 14.3.0.
  • @types/node stays on 24.x. A Renovate rule holds it to the Node major Volta pins. Types for a newer Node let code typecheck against APIs the runtime lacks, so Node and its types are upgraded together.

Changes for existing projects

A project created from v1.2.0 would change the following to match:

  1. Node and pnpm. In package.json, set volta.node to 24.22.1, volta.pnpm to 10.34.8 and packageManager to pnpm@10.34.8.
  2. Dev tools. Pin the versions listed above. If you moved to Biome 2.6, update the $schema URL in biome.json to 2.6.2. Then run pnpm install and pnpm lint; minor Biome releases can add rules.
  3. Renovate. Add this package rule to renovate.json: { "matchPackageNames": ["@types/node"], "allowedVersions": "24.x" }.

v1.2.0

Choose a tag to compare

@prosdev prosdev released this 26 Oct 18:59

Dependencies can now install once they are 3 days old, down from 14, and an urgent security fix has a documented way through on its release day. Packages build with tsup 9, which removes the TypeScript 6 workaround from every package config.

Changed

  • 3-day dependency age rule. pnpm refuses any version published in the last 3 days, including transitive ones, and Renovate waits the same 3 days before proposing an update. The two values must stay equal, or Renovate opens PRs that pnpm refuses.
  • Urgent security fixes. One exact version may be added to minimumReleaseAgeExclude, with its advisory linked, and is removed once it is 3 days old. A person approves each exception, and AGENTS.md tells agents never to add one. The README's "Dependency Updates" section has the steps.
  • Security-fix PRs. Renovate labels vulnerability fixes security and opens them outside its weekly schedule. This needs Dependabot alerts turned on for the repository.
  • tsup 9.0.0. JavaScript output is byte-identical to tsup 8.5.1. Declaration files now put type-only exports in a separate export type { … } statement, which is equivalent.

Changes for existing projects

A project created from v1.1.0 would change the following to match:

  1. Dependency age. In pnpm-workspace.yaml, set minimumReleaseAge: 4320. In renovate.json, set "minimumReleaseAge": "3 days" and add "vulnerabilityAlerts": { "labels": ["security"] }.
  2. tsup 9. Pin tsup to 9.0.0 in every package.json that lists it. In each tsup.config.ts, replace dts: { compilerOptions: { ignoreDeprecations: '6.0' } } with dts: true, keeping any other dts options such as entry. tsup 9 requires Node 22.18 or later; the template already requires Node 24.

v1.1.0

Choose a tag to compare

@prosdev prosdev released this 26 Oct 11:33

The template now builds browser libraries and command-line tools as well as Node libraries. It publishes without any stored npm secret, checks every package before it ships, and refuses dependencies published in the last 14 days.

Added

  • Browser library example (packages/browser-example): an ES module plus a script-tag build, Playwright browser tests with an axe accessibility check, and a size budget that fails CI when the build grows.
  • Command-line example (packages/cli-example): a bin entry, a Node 24 build target, and a test that runs the built command.
  • Package checks (pnpm check:packages): each package is packed as it would publish, then checked with publint and Are The Types Wrong. CI and the release workflow both run them.
  • 14-day dependency age rule: pnpm refuses any version published in the last 14 days, including transitive ones. Renovate (opt-in, via its GitHub App) proposes weekly grouped updates once releases pass that age.
  • Agent instructions: AGENTS.md covers setup, the definition of done, lockfile hygiene and troubleshooting. CLAUDE.md imports it.

Changed

  • Publishing uses npm trusted publishing. No npm secret is stored, and public packages get provenance. The release workflow runs as four jobs; only the publish job can obtain npm credentials, and it never builds.
  • CI hardening: least-privilege permissions, and every action pinned to a commit SHA.
  • Tooling: Node 24 (Volta 24.12.0), pnpm 10.26.2, TypeScript 6.0.3, tsup 8.5.1, Turborepo 2.11.7, Biome 2.5.15, Vitest 5.0.4, Changesets 3.0.3, commitlint 21.2.3.
  • Versioning: template versions mark milestones, not compatibility, because a template is copied once and never upgraded in place. This section is where compatibility is described.

Changes for existing projects

A project created from v1.0.0 would change the following to match:

  1. Node 24 and pnpm 10. Raise engines.node to >=24. Set packageManager to pnpm@10.26.2 and the Volta pins. Reinstall to regenerate the lockfile.
  2. Builds use tsup. Packages built with tsc move to a tsup.config.ts. Add "type": "module", an exports map into dist/, and "files": ["dist"]. Without files, npm pack ships no dist/.
  3. TypeScript 6 deprecates baseUrl. Remove it from the root tsconfig.json and prefix paths with ./. Packages built with tsup 8 also need dts: { compilerOptions: { ignoreDeprecations: '6.0' } } until tsup 9.
  4. Biome 2 and Turborepo 2. Migrate biome.json (biome migrate) and rename pipeline to tasks in turbo.json.
  5. Changesets 3 no longer versions private packages by default. To keep that behavior, set "privatePackages": { "version": true, "tag": false } in .changeset/config.json.
  6. Publishing. Delete the NPM_TOKEN secret. Replace release.yml. Add a trusted publisher on npmjs.com for release.yml and the npm environment, then set the package to "disallow tokens". The README's "Setting Up npm Publishing" section covers each step.
  7. Dependency age. Add minimumReleaseAge: 20160 to pnpm-workspace.yaml. If the lockfile already holds young versions, delete node_modules and the lockfile, then reinstall.

v1.0.0

Choose a tag to compare

@prosdev prosdev released this 08 Oct 08:56