Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
33 changes: 33 additions & 0 deletions .claude/skills/author-migration/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ For each changed dependency:
- Exception: if a minor bump introduces known breaking behavior, use `scope: project_wide` and explain in `notes`
- Set `reference` to the library's changelog or releases page
- Write a concise `notes` describing the impact
- For **breaking changes that swap one library for another** (e.g., i18n stack replacement), the `notes` field MUST also document:
- Known incompatibilities between old and new library behavior (e.g., key format differences, implicit assumptions)
- Test infrastructure implications (mock patterns, setup files that will need updating)

#### 3b. `infrastructure` — Config/build tool changes

Expand Down Expand Up @@ -120,6 +123,22 @@ From the `--diff-filter=D` output, create an entry for each deleted file. Explai

All remaining modified files that were not already classified above go here. Group related files into a single entry when they are part of the same PR or feature. Describe what changed and why.

**Test infrastructure check:** When a change involves replacing a foundational library (i18n stack, state management, CSS framework, test runner), explicitly check for test implications and create a dedicated entry if any of these apply:
- Global test setup files (`setupTests.ts`, `vitest.setup.ts`) need mock updates
- Test mock patterns change (e.g., `vi.mock('react-i18next')` → `vi.mock('next-intl')`)
- Test utility/helper functions change their API

Set `downstream_only: true` on the entry if the upstream `packages/app/` has no such file but downstream projects are expected to. Use the `notes` field to describe the exact mock/setup pattern change with before/after examples.

#### 3h. `downstream_warnings` — Pre-migration compatibility checks

After completing all 7 change type classifications, review every `project_wide` dependency change for known incompatibilities that downstream projects must fix *before* running the migration. Create a `downstream_warnings` entry for each incompatibility that:
- Would cause runtime errors or crashes (not just deprecation warnings)
- Cannot be detected or fixed automatically by the migration plugin
- Requires manual inspection of downstream code or config files
Comment thread
PhilKsr marked this conversation as resolved.

Common triggers: key format changes in locale/config files, behavioral differences between the old and new library, implicit assumptions that differ between libraries.

### Step 4: Generate the manifest YAML

Save the manifest to `packages/app/manifests/<version>.yaml`.
Expand Down Expand Up @@ -148,6 +167,7 @@ changes: # Array of change entries
# ── 2. infrastructure — config/build tool changes ────────────────────
- type: infrastructure
description: "what changed"
interactive: true # optional — if true, plugin confirms with user before applying (default: false)
files:
- path: "relative/to/app/root"
action: modified | added
Expand All @@ -157,10 +177,13 @@ changes: # Array of change entries
# ── 3. file_tracking — modified existing files ───────────────────────
- type: file_tracking
description: "what changed and why"
batch_hint: codemod # optional — identical mechanical change across all listed files
downstream_only: true # optional — upstream has no such file, but downstream projects do
files:
- path: "relative/to/app/root"
action: modified
pr: "https://github.com/Frachtwerk/essencium-frontend/pull/NNN" # optional
notes: "additional context, downstream implications" # optional

# ── 4. new_file — newly added files ─────────────────────────────────
- type: new_file
Expand Down Expand Up @@ -189,6 +212,12 @@ changes: # Array of change entries
- name: "VARIABLE_NAME"
required: true | false
default: "optional-default-value" # optional — if present, the migration plugin uses this as the initial value

# ── downstream_warnings — pre-migration compatibility checks (optional) ──
downstream_warnings:
- check: "Human-readable description of what to check"
reason: "Why this is a problem — what breaks and how"
action: "What the user must do before starting the migration"
```

#### Key rules for the manifest
Expand All @@ -199,6 +228,10 @@ changes: # Array of change entries
- **Group related changes** — if multiple files were changed in the same PR/feature, put them in a single `file_tracking` entry with multiple files.
- **Order changes** by type: infrastructure first, then dependency_migration (major/project_wide before minor/package), then new_file, file_removal, file_tracking, translation, env_variable.
- **Use comments** as section separators in the YAML (see existing manifests for style).
- **`batch_hint: codemod`** on `file_tracking` entries signals that the change is purely mechanical and identical across all listed files (e.g., a find-and-replace). The migration plugin may use this to apply the change via a single codemod script instead of individual file edits.
Comment thread
PhilKsr marked this conversation as resolved.
Outdated
- **`interactive: true`** on `infrastructure` entries tells the migration plugin to pause and confirm with the user before applying. Use for changes where downstream projects may have intentionally different values (e.g., Node.js version pinning, Docker base images, CI environment constraints).
- **`downstream_only: true`** on `file_tracking` entries marks changes that apply to downstream projects but not to the upstream `packages/app/` itself (e.g., a `setupTests.ts` that upstream does not have).
- **`downstream_warnings`** is an optional top-level array (sibling to `changes`). Use it for incompatibilities that downstream projects must fix *before* starting the migration — things that would cause crashes and cannot be auto-detected by the plugin. Each entry needs `check` (what to look for), `reason` (what breaks), and `action` (how to fix it).

### Step 5: Validate the YAML

Expand Down
28 changes: 28 additions & 0 deletions packages/app/MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,34 @@ Changed files:
- `src/app/[locale]/(public)/login/page.tsx`
- `src/app/[locale]/(public)/set-password/page.tsx`

### Test infrastructure (downstream projects)

If your project has a `setupTests.ts` or `vitest.setup.ts` with i18next/react-i18next mocks, update them for next-intl.

Before:

```ts
vi.mock('react-i18next', () => ({
useTranslation: () => ({
t: (key: string) => key,
i18n: { language: 'en', changeLanguage: vi.fn() },
}),
}))
```

After:

```ts
vi.mock('next-intl', () => ({
useTranslations: () => (key: string) => key,
useLocale: () => 'en',
}))
```

Update all test files that have local `vi.mock('react-i18next')` calls with the same pattern.

> **Note:** next-intl uses dots as nesting separators in translation keys. If your locale JSON files contain keys with literal dots (e.g., `"14.1"`, `"29.3"`), rename them before migrating (e.g., `"14_1"`, `"29_3"`) — otherwise next-intl will crash at startup with an `INVALID_KEY` error. Update any code that references these keys accordingly.

### Raise Node.js minimum version to 24

#### `packages/app/package.json`
Expand Down
23 changes: 23 additions & 0 deletions packages/app/manifests/10.0.0.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@ version: "10.0.0"
from: "9.5.0"
date: "2026-04-16"

downstream_warnings:
- check: "Locale JSON files must not contain dots in translation keys"
reason: "next-intl interprets dots as nesting separators (e.g., the key '14.1' is treated as { '14': { '1': ... } }), unlike i18next which treated them as literal key names. This causes a runtime crash (INVALID_KEY error) when the dev server starts."
action: "Search all locale JSON files for keys containing dots (e.g., '14.1', '29.3') and rename them using underscores or another separator (e.g., '14_1', '29_3') before running the migration. Update any code that references these keys accordingly."
- check: "Test files mocking react-i18next need updating to next-intl"
reason: "After the i18n stack migration, vi.mock('react-i18next') calls and any i18next-related test setup will fail because react-i18next is no longer installed."
action: "Update setupTests.ts (if present) and all test files: replace vi.mock('react-i18next', () => ({ useTranslation: () => ({ t: (key) => key }) })) with vi.mock('next-intl', () => ({ useTranslations: () => (key) => key, useLocale: () => 'en' }))."

changes:
# ── BREAKING: i18n stack replaced (i18next → next-intl) ────────────
- type: dependency_migration
Expand Down Expand Up @@ -146,9 +154,11 @@ changes:

- type: infrastructure
description: "BREAKING: Raise minimum Node.js version from >=20 to >=24. Update volta.node from 20.15.0 to 24.0.0."
interactive: true
files:
- path: "package.json"
action: modified
notes: "Downstream projects may intentionally pin a different Node.js version. Verify your CI/CD pipelines, Docker base images, and deployment environment support Node 24 before applying."

# ── New files: i18n infrastructure (next-intl) ────────────────────
- type: new_file
Expand Down Expand Up @@ -293,6 +303,7 @@ changes:

- type: file_tracking
description: "Replace useTranslation() with useTranslations() from next-intl in all view components."
batch_hint: codemod
files:
- path: "src/app/[locale]/(main)/admin/rights/RightsView.tsx"
action: modified
Expand Down Expand Up @@ -321,6 +332,7 @@ changes:
# ── File tracking: next-intl migration — page.tsx generateMetadata ─
- type: file_tracking
description: "Simplify generateMetadata in all page.tsx files: replace initTranslations(locale) with getTranslations() from next-intl/server. Remove Props/params/ResolvingMetadata types. Page components no longer receive params prop."
batch_hint: codemod
files:
- path: "src/app/[locale]/(main)/admin/rights/page.tsx"
action: modified
Expand All @@ -346,6 +358,17 @@ changes:
action: modified
pr: "https://github.com/Frachtwerk/essencium-frontend/pull/967"

# ── File tracking: next-intl migration — test infrastructure (downstream) ──
- type: file_tracking
description: "Update test infrastructure for i18n stack migration. Downstream projects with test suites must update: (1) setupTests.ts/vitest.setup.ts — replace react-i18next global mock with next-intl mock, (2) all test files using vi.mock('react-i18next') — change to vi.mock('next-intl') with updated mock shape."
downstream_only: true
batch_hint: codemod
files:
- path: "setupTests.ts"
action: modified
notes: "The upstream app has no setupTests.ts, but downstream projects cloned from this boilerplate typically have one. Before: vi.mock('react-i18next', () => ({ useTranslation: () => ({ t: (key) => key, i18n: { language: 'en' } }) })). After: vi.mock('next-intl', () => ({ useTranslations: () => (key) => key, useLocale: () => 'en' }))."
pr: "https://github.com/Frachtwerk/essencium-frontend/pull/967"

# ── File tracking: next-intl migration — TranslationsView (complex) ─
- type: file_tracking
description: "Complex refactor: replace getI18n()/i18n.language with useLocale() from next-intl. Accept staticMessages prop (preloaded by page.tsx). Replace i18n.addResourceBundle() with staticMessages[locale] lookup. Replace window.location.reload() with router.refresh(). Use routing.locales. Fix component name typo (TranlsationView → TranslationsView)."
Expand Down
Loading