Skip to main content

Migrating from Presetter v8 to v9 🚀

Presetter v9 is a major release that modernizes the toolchain, reorganizes the preset ecosystem, and hardens releases. This guide walks you through the breaking changes - the scoped namespace migration, the TypeScript 6 upgrade, the script renames, and the Node support policy shift - plus the new presets and features you get along the way.

Overview of Changes​

🎯 Breaking Changes​

  • Scoped namespace: every preset moves from presetter-preset-* to @presetter/preset-*. The main presetter CLI package keeps its unscoped name.
  • TypeScript 6 required: TypeScript 6 is now the required peer dependency, replacing TypeScript 5.
  • ES2024 default target: the default compile target is now ES2024 (was ES2022).
  • noUncheckedIndexedAccess enabled: the essentials preset turns on noUncheckedIndexedAccess for safer indexed access.
  • Simplified tsconfig: the explicit strictNullChecks: false and noImplicitAny: false overrides were removed — the templates now rely on TypeScript 6 defaults.
  • Standardized script names: the generated npm scripts were renamed for consistency (see Step 4).
  • Node 20 CI support dropped: Presetter's own CI now follows active Node LTS lines plus latest. Keep production projects on an actively supported Node LTS even though package engines still accept Node 20.

🆕 New Presets​

  • @presetter/preset-node — Node-tuned TypeScript configuration on top of essentials.
  • @presetter/preset-bun — standalone preset for Bun projects.
  • @presetter/preset-storybook — standalone Storybook configuration with a11y checks and Vitest browser testing.

✨ New Features​

  • presetter bootstrap --projects now accepts package names, not just globs.
  • lint-staged pre-commit and pre-push hooks out of the box.
  • git-cliff powered changelog generation (replacing Standard Version).
  • Monorepo sub-level Vitest configuration support.
  • Package info displayed during bootstrap.
  • Provenance publishing for released packages.
  • Native typecheck path in the Presetter monorepo via tsgo and @typescript/native-preview.

Prerequisites​

Before starting the migration:

  • Node.js 20+ and npm 7+ installed; an active Node LTS is recommended because v9 CI no longer covers Node 20
  • A working Presetter v8 project
  • A Git repository with a clean working tree (so you can roll back)

Migration Steps​

Step 1: Rename Preset Packages​

Every preset now lives under the @presetter scope. Use this find-and-replace table:

v8 (old)v9 (new)
presetterpresetter (unchanged)
presetter-preset-essentials@presetter/preset-essentials
presetter-preset-esm@presetter/preset-esm
presetter-preset-cjs@presetter/preset-cjs
presetter-preset-hybrid@presetter/preset-hybrid
presetter-preset-web@presetter/preset-web
presetter-preset-react@presetter/preset-react
presetter-preset-next@presetter/preset-next
presetter-preset-rollup@presetter/preset-rollup
presetter-preset-strict@presetter/preset-strict
presetter-preset-monorepo@presetter/preset-monorepo
presetter-types@presetter/types

A repository-wide rename covers package.json, presetter.config.ts, and any import statements:

# preview the matches first
grep -rl 'presetter-preset-\|presetter-types' . --exclude-dir=node_modules

# then rename (macOS/BSD sed shown; use `sed -i` on GNU/Linux)
grep -rl 'presetter-preset-' . --exclude-dir=node_modules \
| xargs sed -i '' 's#presetter-preset-#@presetter/preset-#g'
grep -rl 'presetter-types' . --exclude-dir=node_modules \
| xargs sed -i '' 's#presetter-types#@presetter/types#g'

Before (package.json):

package.json
{
"devDependencies": {
"presetter": "^8.3.0",
"presetter-preset-essentials": "^8.3.0",
"presetter-preset-esm": "^8.3.0"
}
}

After (package.json):

package.json
{
"devDependencies": {
"presetter": "^9.0.0",
"@presetter/preset-essentials": "^9.0.0",
"@presetter/preset-esm": "^9.0.0"
}
}

Before (presetter.config.ts):

presetter.config.ts
import { preset } from 'presetter-types';
import esm from 'presetter-preset-esm';
import strict from 'presetter-preset-strict';

export default preset('my-project', {
extends: [esm, strict],
});

After (presetter.config.ts):

presetter.config.ts
import { preset } from '@presetter/types';
import esm from '@presetter/preset-esm';
import strict from '@presetter/preset-strict';

export default preset('my-project', {
extends: [esm, strict],
});

Step 2: Bump Versions to v9​

Update every Presetter dependency to ^9.0.0:

# core CLI (unscoped)
npm install --save-dev presetter@^9.0.0

# presets (scoped) — update those your project uses
npm install --save-dev @presetter/preset-essentials@^9.0.0
npm install --save-dev @presetter/preset-esm@^9.0.0
npm install --save-dev @presetter/preset-react@^9.0.0
npm install --save-dev @presetter/preset-monorepo@^9.0.0

Step 3: Upgrade to TypeScript 6​

TypeScript 6 is the new required peer. With npm 7+ it is pulled in automatically, but you can upgrade it explicitly:

npm install --save-dev typescript@^6.0.0

The generated tsconfig templates were simplified. If you previously relied on the implicit strictNullChecks: false / noImplicitAny: false behavior, note that v9 follows TypeScript 6 strict defaults instead. You may also see new errors from noUncheckedIndexedAccess:

For maintainers following Presetter's own repository setup, the root workspace now runs typechecking through tsgo from @typescript/native-preview. That is an internal repository workflow improvement, not an additional dependency required for ordinary preset consumers.

// v9 flags this — record[key] is now `string | undefined`
const value: string = record[key];

// fix with a guard or a non-null assertion when you are sure
const value = record[key] ?? '';

If you need to opt out temporarily, override tsconfig.json in your presetter.config.ts:

presetter.config.ts
export default preset('my-project', {
extends: [esm],
override: {
assets: {
'tsconfig.json': {
compilerOptions: {
noUncheckedIndexedAccess: false,
},
},
},
},
});

Step 4: Update npm Scripts​

The generated script delegates were standardized. Update your package.json scripts to the v9 names:

v8 (old)v9 (new)
coveragetest:coverage
watchtest:watch
developstart
formatfolded into lint

Before (package.json):

package.json
{
"scripts": {
"build": "run build",
"test": "run test",
"coverage": "run coverage",
"watch": "run watch",
"format": "run format",
"typecheck": "run typecheck"
}
}

After (package.json):

package.json
{
"scripts": {
"prepare": "run prepare",
"build": "run build",
"lint": "run lint --",
"test": "run test --",
"test:coverage": "run test:coverage --",
"test:watch": "run test:watch --",
"typecheck": "run typecheck --"
}
}
tip

Running npm install (which triggers presetter bootstrap) regenerates the recommended scripts automatically. Re-bootstrapping after the rename is the easiest way to land on the v9 script set.

Step 5: Re-bootstrap Your Project​

Regenerate the configuration files with the v9 templates:

# clean old configs (they will be regenerated)
rm -f tsconfig.json eslint.config.ts vitest.config.ts

# install + bootstrap
npm install

Step 6: Verify Everything Works​

npm run typecheck
npm run lint
npm run test
npm run build

Preset-Specific Updates​

Storybook (new standalone preset)​

If you used Storybook through @presetter/preset-web, you can now add it independently to any framework via @presetter/preset-storybook:

presetter.config.ts
import next from '@presetter/preset-next';
import storybook from '@presetter/preset-storybook';
import { preset } from 'presetter';

export default preset('app', {
extends: [next, storybook],
});

It generates .storybook/main.ts, Storybook scripts, ESLint story rules, and a Vitest browser project. See the Storybook preset page for details.

Node and Bun​

New runtime presets let you target a specific runtime explicitly:

presetter.config.ts (Node)
import { preset } from '@presetter/types';
import node from '@presetter/preset-node';
import esm from '@presetter/preset-esm';

export default preset('my-node-service', {
extends: [node, esm],
});
presetter.config.ts (Bun)
// standalone — no essentials required
export { default } from '@presetter/preset-bun';

Monorepo​

@presetter/preset-monorepo now supports sub-level Vitest configuration, so individual packages can refine their test setup while inheriting workspace defaults. Re-bootstrap each package to pick up the new configs.

Bootstrapping by package name​

presetter bootstrap --projects now accepts package names in addition to globs:

# v9 — bootstrap specific packages by name
presetter bootstrap --projects @myorg/web-app @myorg/shared-utils

Troubleshooting​

Issue: Module not found for presetter-preset-*​

Problem: Imports or dependencies still reference the old unscoped names.

Solution: Re-run the rename from Step 1 and check both package.json and every *.config.ts / source import.

Issue: New TypeScript errors after upgrading​

Problem: noUncheckedIndexedAccess or TypeScript 6 strict defaults surface new type errors.

Solution: Add guards/assertions for indexed access, or temporarily override noUncheckedIndexedAccess: false in tsconfig.json (see Step 3).

Issue: npm run coverage / npm run watch no longer exists​

Problem: Scripts were renamed in v9.

Solution: Use npm run test:coverage and npm run test:watch, or re-bootstrap to regenerate the script set (see Step 4).

Issue: Changelog generation behaves differently​

Problem: Releases used Standard Version in v8.

Solution: v9 uses git-cliff. Run run release to bump, generate the changelog, commit, and tag. Pass a prerelease with PRERELEASE=alpha run release.

Rollback Plan​

If you need to revert:

# restore v8 dependencies
npm install --save-dev presetter@^8.3.0
npm install --save-dev presetter-preset-essentials@^8.3.0
# ... other presets

# restore v8 configurations
git checkout HEAD~1 -- tsconfig.json eslint.config.ts vitest.config.ts package.json

# re-bootstrap with v8
npm install

Post-Migration Checklist​

  • ✅ All preset packages renamed to @presetter/*
  • ✅ All Presetter dependencies bumped to ^9.0.0
  • ✅ TypeScript upgraded to ^6.0.0
  • ✅ noUncheckedIndexedAccess errors resolved (or overridden)
  • ✅ npm scripts updated to v9 names (test:coverage, test:watch, …)
  • ✅ Bootstrap completed successfully
  • ✅ Typecheck, lint, test, and build all pass
  • ✅ New presets adopted where relevant (node / bun / storybook)
  • ✅ CI/CD updated for renamed packages and scripts
  • ✅ Runtime matrix moved to active Node LTS lines where applicable

Getting Help​


Migration Time: 30-60 minutes for most projects Difficulty: Moderate Breaking Changes: Namespace rename, TypeScript 6, script renames, and Node support policy