create-pracht 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -1
- package/package.json +3 -2
- package/skills/add-auth/SKILL.md +346 -0
- package/skills/add-db/SKILL.md +316 -0
- package/skills/add-i18n/SKILL.md +239 -0
- package/skills/add-observability/SKILL.md +270 -0
- package/skills/audit-a11y/SKILL.md +170 -0
- package/skills/audit-auth/SKILL.md +144 -0
- package/skills/audit-bundles/SKILL.md +167 -0
- package/skills/audit-csrf/SKILL.md +179 -0
- package/skills/audit-deps/SKILL.md +138 -0
- package/skills/audit-headers/SKILL.md +186 -0
- package/skills/audit-islands/SKILL.md +144 -0
- package/skills/audit-loaders/SKILL.md +135 -0
- package/skills/audit-redirects/SKILL.md +146 -0
- package/skills/audit-secrets/SKILL.md +150 -0
- package/skills/audit-seo/SKILL.md +164 -0
- package/skills/audit-shells/SKILL.md +142 -0
- package/skills/configure-isg/SKILL.md +172 -0
- package/skills/migrate-nextjs/SKILL.md +519 -0
- package/skills/pracht-debug/SKILL.md +146 -0
- package/skills/pracht-deploy/SKILL.md +208 -0
- package/skills/pracht-scaffold/SKILL.md +191 -0
- package/skills/pracht-test-api/SKILL.md +165 -0
- package/skills/pre-deploy/SKILL.md +164 -0
- package/skills/scaffold-e2e/SKILL.md +189 -0
- package/skills/scaffold-tests/SKILL.md +283 -0
- package/skills/tune-render-mode/SKILL.md +168 -0
- package/skills/typed-routes/SKILL.md +203 -0
- package/skills/upgrade-pracht/SKILL.md +151 -0
- package/src/index.js +146 -8
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tune-render-mode
|
|
3
|
+
version: 1.1.0
|
|
4
|
+
description: |
|
|
5
|
+
Recommend the right pracht render mode (ssg, isg, ssr, spa) for each route
|
|
6
|
+
based on what its loader actually does. Most apps pick a mode once and never
|
|
7
|
+
revisit; this skill surfaces routes that are mis-tuned.
|
|
8
|
+
Use when asked to "tune render modes", "make my site faster", "should this
|
|
9
|
+
route be SSG", "audit render modes", or "review SSG/ISG/SSR choices".
|
|
10
|
+
allowed-tools:
|
|
11
|
+
- Bash
|
|
12
|
+
- Read
|
|
13
|
+
- Edit
|
|
14
|
+
- Grep
|
|
15
|
+
- Glob
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
# Pracht Tune Render Mode
|
|
19
|
+
|
|
20
|
+
Walk every route, read its loader, and recommend the cheapest render mode that
|
|
21
|
+
still satisfies the route's data dependencies.
|
|
22
|
+
|
|
23
|
+
This is a **tune** skill, not a report-only audit: it ends by applying edits.
|
|
24
|
+
The contract is propose-then-apply — produce the recommendation table and the
|
|
25
|
+
exact diffs first, then apply them **only after the user explicitly confirms**
|
|
26
|
+
(per route or as a batch). Never edit before that confirmation.
|
|
27
|
+
|
|
28
|
+
## Decision Tree
|
|
29
|
+
|
|
30
|
+
For each route:
|
|
31
|
+
|
|
32
|
+
1. **No `loader`, no `getStaticPaths`, no per-request data** → **`ssg`**
|
|
33
|
+
- Pure UI. Build once, serve from CDN. Highest performance.
|
|
34
|
+
|
|
35
|
+
2. **Loader reads only build-time-stable data** (filesystem, static config,
|
|
36
|
+
typed CMS export, no `request`/`params`/`context.env` use) → **`ssg`** or
|
|
37
|
+
**`isg`**
|
|
38
|
+
- Pick `isg` with `timeRevalidate(seconds)` if the source can change between
|
|
39
|
+
deploys (CMS, pricing pages, public catalog).
|
|
40
|
+
- Pick `ssg` if the source only changes when you redeploy.
|
|
41
|
+
|
|
42
|
+
3. **Loader reads `params` to fetch data, but not `request`/cookies** →
|
|
43
|
+
**`ssg`** with `getStaticPaths`, or **`isg`** if the universe of params is
|
|
44
|
+
open-ended (millions of slugs).
|
|
45
|
+
|
|
46
|
+
4. **Loader reads `request`, but the data is shareable** (`request-static`:
|
|
47
|
+
request used only for cache keys like `Accept-Language`, never for
|
|
48
|
+
identity) → **`ssr`** by default; **`isg`** is possible on adapters whose
|
|
49
|
+
cache can key on the varying dimension (e.g. a normalized cache key at a
|
|
50
|
+
Cloudflare gateway). If the variant fan-out is unbounded or the adapter
|
|
51
|
+
cache can't express the Vary, stay on `ssr`.
|
|
52
|
+
|
|
53
|
+
5. **Loader reads cookies, auth headers, `context.env` per-request, or
|
|
54
|
+
anything personalized** → **`ssr`**
|
|
55
|
+
- Auth dashboards, anything user-specific, anything that varies by user
|
|
56
|
+
identity at request time.
|
|
57
|
+
|
|
58
|
+
6. **Heavy client interactivity, no SEO need, auth-gated** → **`spa`**
|
|
59
|
+
- Internal admin tools, post-login dashboards where the first paint can be a
|
|
60
|
+
skeleton.
|
|
61
|
+
|
|
62
|
+
## Step 1: Enumerate
|
|
63
|
+
|
|
64
|
+
If the pracht MCP server is registered (see docs/MCP.md), prefer its tools
|
|
65
|
+
(`inspect_routes`, `inspect_api`, `inspect_build`, `doctor`, `verify`) over
|
|
66
|
+
shelling out.
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
pracht inspect routes --json
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Prerequisite: `pracht inspect` needs a vite config with the pracht plugin
|
|
73
|
+
wired up.
|
|
74
|
+
|
|
75
|
+
Capture: `path`, `file`, current `render`, `revalidate`, `middleware`, and
|
|
76
|
+
the top-level `mode` field (manifest vs pages router — Step 4 depends on it).
|
|
77
|
+
|
|
78
|
+
## Step 2: Read each loader
|
|
79
|
+
|
|
80
|
+
For each route, open the file and look at the `loader`/`getStaticPaths`
|
|
81
|
+
exports. Tag the loader with one of:
|
|
82
|
+
|
|
83
|
+
- `none` — no loader at all
|
|
84
|
+
- `static` — only reads imports / pure data
|
|
85
|
+
- `param-static` — reads `params` only
|
|
86
|
+
- `request-static` — reads `request` for cache keys but data is shareable
|
|
87
|
+
(e.g. `Accept-Language`) — decision-tree branch 4
|
|
88
|
+
- `request-personalized` — reads cookies, auth headers, user-specific
|
|
89
|
+
`context.env` lookups — decision-tree branch 5
|
|
90
|
+
|
|
91
|
+
## Step 3: Recommend
|
|
92
|
+
|
|
93
|
+
Produce a table:
|
|
94
|
+
|
|
95
|
+
| Route | Current | Recommended | Severity | Reason |
|
|
96
|
+
| ----- | ------- | ----------- | -------- | ------ |
|
|
97
|
+
|
|
98
|
+
Severity: `error` (broken today, e.g. `ssg` with a loader that reads
|
|
99
|
+
`request` — cannot be prerendered correctly), `warn` (works but mis-tuned,
|
|
100
|
+
e.g. `ssr` with no loader), `info` (optional improvement, e.g. hydration
|
|
101
|
+
tuning).
|
|
102
|
+
|
|
103
|
+
Examples of recommendations:
|
|
104
|
+
- `ssr` → `ssg` when loader is empty: "no loader; no per-request data — make it
|
|
105
|
+
static." (`warn`)
|
|
106
|
+
- `ssr` → `isg(3600)` when loader fetches a public CMS: "shared data, freshness
|
|
107
|
+
acceptable at 1 hour." (`warn`)
|
|
108
|
+
- `ssg` → `ssr` when loader reads `request.headers.get('cookie')`: "reads
|
|
109
|
+
request — cannot be prerendered." (`error`)
|
|
110
|
+
- `spa` → `ssr` when route has SEO-relevant `head()` and unauthenticated
|
|
111
|
+
visitors should see content. (`warn`)
|
|
112
|
+
|
|
113
|
+
## Step 3b: Consider the hydration mode too
|
|
114
|
+
|
|
115
|
+
Render mode controls when HTML is generated; **hydration mode** controls how
|
|
116
|
+
much JavaScript ships afterwards (`hydration: "full" | "islands" | "none"`,
|
|
117
|
+
default `"full"` — see `docs/ISLANDS.md`). A current `@pracht/cli` emits the
|
|
118
|
+
resolved `hydration` per route in the inspect JSON; if your CLI predates the
|
|
119
|
+
field (absent from the JSON), grep the manifest for `hydration:` (pages apps:
|
|
120
|
+
`HYDRATION` exports) instead. While tuning, also flag:
|
|
121
|
+
|
|
122
|
+
- Routes with **no interactivity at all** (no event handlers, no hooks) →
|
|
123
|
+
`hydration: "none"` — zero JS shipped.
|
|
124
|
+
- Content-heavy routes with **one or two isolated widgets** (counter, search
|
|
125
|
+
box, newsletter form) → `hydration: "islands"` with the widgets moved to
|
|
126
|
+
`src/islands/`.
|
|
127
|
+
- Caveats: islands routes use MPA-style full-document navigation (no client
|
|
128
|
+
router), island props must be JSON-serializable, and `render: "spa"` cannot
|
|
129
|
+
combine with `"islands"`/`"none"`.
|
|
130
|
+
|
|
131
|
+
## Step 4: Propose diffs, then apply on confirmation
|
|
132
|
+
|
|
133
|
+
Present the exact edits and wait for approval. Where the edit lands depends
|
|
134
|
+
on the router `mode` from Step 1:
|
|
135
|
+
|
|
136
|
+
- **Manifest apps**: edit `src/routes.ts` to update the `render` field. For
|
|
137
|
+
ISG, add `revalidate: timeRevalidate(N)` and import `timeRevalidate` from
|
|
138
|
+
`@pracht/core`. Hydration changes update the `hydration` field the same
|
|
139
|
+
way.
|
|
140
|
+
- **Pages apps**: render mode is a per-file constant —
|
|
141
|
+
`export const RENDER_MODE = "ssg"` in the page module (valid values
|
|
142
|
+
`"ssr" | "ssg" | "isg" | "spa"`; the default is `"ssr"`, overridable
|
|
143
|
+
globally via `pracht({ pagesDefaultRender: "..." })` in vite config).
|
|
144
|
+
Hydration is `export const HYDRATION = "..."` in the same file. If most
|
|
145
|
+
pages want the same mode, prefer changing `pagesDefaultRender` over adding
|
|
146
|
+
a constant to every file.
|
|
147
|
+
|
|
148
|
+
Apply the edits only after the user confirms.
|
|
149
|
+
|
|
150
|
+
## Rules
|
|
151
|
+
|
|
152
|
+
1. Never silently change render modes. Always present the recommendation and
|
|
153
|
+
the exact diff first; apply only after explicit user approval.
|
|
154
|
+
2. If a route uses `auth` middleware, default to `ssr` — auth implies cookies.
|
|
155
|
+
3. All three adapters support ISG — the mechanisms differ. Confirm which
|
|
156
|
+
adapter is in play, then use this capability table:
|
|
157
|
+
|
|
158
|
+
| Adapter | ISG mechanism (default) | Notes |
|
|
159
|
+
| ---------- | -------------------------------------------------------------- | ----- |
|
|
160
|
+
| Node | Filesystem: `isg-manifest.json` + file-mtime revalidation | Serves stale immediately, refreshes in place. |
|
|
161
|
+
| Cloudflare | Worker-managed Workers Cache API, **per colo** — works without any extra config | `cloudflareAdapter({ cache: true })` + `"cache": { "enabled": true }` in wrangler config is an **optional upgrade** that moves time-revalidated routes to an edge-tier cache in front of the Worker; webhook-only routes stay worker-managed. Webhook invalidation on the default path is per-colo, not a global purge. |
|
|
162
|
+
| Vercel | Native ISR: Build Output API prerender functions with `expiration` from the time policy; `PRACHT_REVALIDATE_TOKEN` becomes the `bypassToken` (must be set at build time) | See docs/ADAPTERS.md. |
|
|
163
|
+
|
|
164
|
+
4. For dynamic SSG/ISG routes, ensure `getStaticPaths` exists. Flag if missing.
|
|
165
|
+
5. Use `pracht inspect routes --json` rather than reading `src/routes.ts`
|
|
166
|
+
manually — the resolved graph already accounts for groups and inheritance.
|
|
167
|
+
|
|
168
|
+
$ARGUMENTS
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: typed-routes
|
|
3
|
+
version: 1.1.0
|
|
4
|
+
description: |
|
|
5
|
+
Add or maintain pracht typed routes, typed links, route-object navigation,
|
|
6
|
+
and generated href helpers. Use when asked to "add typed routes", "fix typed
|
|
7
|
+
links", "replace hard-coded hrefs", "run typegen", or make navigation route-id
|
|
8
|
+
based instead of string based.
|
|
9
|
+
allowed-tools:
|
|
10
|
+
- Bash
|
|
11
|
+
- Read
|
|
12
|
+
- Write
|
|
13
|
+
- Edit
|
|
14
|
+
- Grep
|
|
15
|
+
- Glob
|
|
16
|
+
- AskUserQuestion
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
# Pracht Typed Routes
|
|
20
|
+
|
|
21
|
+
Use this workflow to keep route ids, params, links, and navigation type-safe.
|
|
22
|
+
|
|
23
|
+
## Step 1: Inspect the resolved graph
|
|
24
|
+
|
|
25
|
+
The resolved app graph is the source of truth — not a manual glob of `src/`.
|
|
26
|
+
|
|
27
|
+
If the pracht MCP server is registered (see docs/MCP.md), prefer its tools
|
|
28
|
+
(`inspect_routes`, `inspect_api`, `inspect_build`, `doctor`, `verify`,
|
|
29
|
+
`generate_*`) over shelling out. Prerequisite: `pracht inspect` needs a vite
|
|
30
|
+
config with the pracht plugin registered.
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pracht inspect routes --json
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Check every route has a stable id. Explicit `id` fields are preferred for routes
|
|
37
|
+
that app code links to, because fallback ids change when paths change.
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
route("/products/:id", () => import("./routes/products/[id].tsx"), {
|
|
41
|
+
id: "product",
|
|
42
|
+
render: "ssr",
|
|
43
|
+
});
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Any route without an explicit `id` — manifest apps included, not just
|
|
47
|
+
pages-router apps — gets a fallback id derived from the route path (`/` →
|
|
48
|
+
`index`, `/blog/:slug` → `blog-slug`, `/*` → `splat`).
|
|
49
|
+
|
|
50
|
+
## Step 2: Generate route types and helpers
|
|
51
|
+
|
|
52
|
+
Run:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
pracht typegen
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
This writes:
|
|
59
|
+
|
|
60
|
+
- `src/pracht.d.ts` — module augmentation for route ids, params, loader data
|
|
61
|
+
types, and API route request/response types (consumed by `apiFetch()`).
|
|
62
|
+
- `src/pracht-routes.ts` — runtime `href()` helper backed by the same route map.
|
|
63
|
+
|
|
64
|
+
Earlier versions wrote the declaration to `src/pracht-routes.d.ts`; typegen
|
|
65
|
+
removes that stale file automatically (TypeScript silently ignored it next to
|
|
66
|
+
the same-named `.ts` helper).
|
|
67
|
+
|
|
68
|
+
Do not hand-edit generated files. If they are stale, update the route graph and
|
|
69
|
+
run typegen again — or rely on `pracht dev`, which refreshes them when route
|
|
70
|
+
files are added, removed, or renamed and when the route manifest or an imported
|
|
71
|
+
definition module changes. The dev banner prompts for the initial typegen run
|
|
72
|
+
when `src/pracht.d.ts` does not exist. In CI, prefer:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
pracht typegen --check
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Step 3: Replace string navigation where it matters
|
|
79
|
+
|
|
80
|
+
### Components
|
|
81
|
+
|
|
82
|
+
```tsx
|
|
83
|
+
import { Link, useNavigate } from "@pracht/core";
|
|
84
|
+
|
|
85
|
+
export function ProductLink({ id }: { id: string }) {
|
|
86
|
+
const navigate = useNavigate();
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<>
|
|
90
|
+
<Link route="product" params={{ id }} search={{ ref: "home" }}>
|
|
91
|
+
View product
|
|
92
|
+
</Link>
|
|
93
|
+
<button onClick={() => void navigate({ route: "product", params: { id } })}>
|
|
94
|
+
Open product
|
|
95
|
+
</button>
|
|
96
|
+
</>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
`<Link>` renders a normal `<a>` and the client router intercepts it like any
|
|
102
|
+
same-origin anchor. It also accepts navigation-behavior props:
|
|
103
|
+
`prefetch="none" | "hover" | "intent" | "viewport" | "render"` (per-link
|
|
104
|
+
prefetch strategy, default `"intent"`), `preserveScroll` (keep the scroll
|
|
105
|
+
position), and `viewTransition` (animate the navigation with the View Transitions API
|
|
106
|
+
where supported). There is also an imperative `prefetch()` export and a
|
|
107
|
+
`useNavigation()` hook for pending navigation/submission state.
|
|
108
|
+
|
|
109
|
+
### Outside components
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
import { href } from "./pracht-routes";
|
|
113
|
+
|
|
114
|
+
const productUrl = href("product", {
|
|
115
|
+
params: { id: "123" },
|
|
116
|
+
search: { tab: "details" },
|
|
117
|
+
});
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Use `href()` in loaders that return URLs, sitemap helpers, menu config, test
|
|
121
|
+
fixtures, and other non-component code.
|
|
122
|
+
|
|
123
|
+
### Loader data
|
|
124
|
+
|
|
125
|
+
After typegen, `useRouteData(routeId)` returns that route's loader data with
|
|
126
|
+
no generic — route ids autocomplete and the type follows the route's loader
|
|
127
|
+
(or its separate loader file from the manifest):
|
|
128
|
+
|
|
129
|
+
```tsx
|
|
130
|
+
import { useRouteData } from "@pracht/core";
|
|
131
|
+
|
|
132
|
+
export function Component() {
|
|
133
|
+
const data = useRouteData("product");
|
|
134
|
+
return <h1>{data.product.name}</h1>;
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Prefer this over `useRouteData<typeof loader>()` when typegen runs; keep the
|
|
139
|
+
generic form for projects that do not generate route types. Routes without a
|
|
140
|
+
loader type their data as `undefined`. The id must be the active route — dev
|
|
141
|
+
mode warns on mismatches.
|
|
142
|
+
|
|
143
|
+
### API routes
|
|
144
|
+
|
|
145
|
+
After typegen, `apiFetch()` type-checks API calls end to end — paths,
|
|
146
|
+
methods, params, bodies and queries (for `defineApi()` routes), and response
|
|
147
|
+
types:
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
import { apiFetch } from "@pracht/core";
|
|
151
|
+
|
|
152
|
+
const item = await apiFetch("/api/items/:id", { params: { id: "42" } });
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
See docs/API_VALIDATION.md for `defineApi()` and validation error handling.
|
|
156
|
+
Typegen discovers API route files without importing them, so it is safe for
|
|
157
|
+
route modules that initialize runtime-only services at module scope.
|
|
158
|
+
|
|
159
|
+
Query and params values cross the wire as strings — write schemas that accept
|
|
160
|
+
string input (`z.coerce.number()`, not `z.number()`); `apiFetch()` rejects
|
|
161
|
+
query and params keys without a string representation at compile time when the
|
|
162
|
+
schema exposes a concrete input type. Handlers that need a custom status keep
|
|
163
|
+
typed payloads with `json(value, { status })`.
|
|
164
|
+
|
|
165
|
+
## Step 4: Param and search rules
|
|
166
|
+
|
|
167
|
+
Generated param types accept `RouteParamInput = string | number | boolean`
|
|
168
|
+
(values are stringified into the path), so:
|
|
169
|
+
|
|
170
|
+
- `:id` requires `params: { id: RouteParamInput }` — a `string` is typical,
|
|
171
|
+
but `number`/`boolean` also typecheck.
|
|
172
|
+
- `*` requires `params: { "*": RouteParamInput }`.
|
|
173
|
+
- `:path*` requires `params: { path: RouteParamInput }`.
|
|
174
|
+
- Routes with no dynamic segments should omit `params`.
|
|
175
|
+
- Missing and extra params should fail at typecheck time.
|
|
176
|
+
- `search` currently accepts `string`, `URLSearchParams`, or an object of
|
|
177
|
+
primitive values/arrays; route-specific search schemas can be added later.
|
|
178
|
+
|
|
179
|
+
## Step 5: Verify
|
|
180
|
+
|
|
181
|
+
Run at least:
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
pracht typegen --check
|
|
185
|
+
pnpm typecheck
|
|
186
|
+
pracht verify --json
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
If navigation changed, add or update Playwright coverage for both the rendered
|
|
190
|
+
anchor `href` and client-side navigation without a full page reload.
|
|
191
|
+
|
|
192
|
+
## Rules
|
|
193
|
+
|
|
194
|
+
1. Always start from `pracht inspect routes --json` or `pracht typegen`; do not
|
|
195
|
+
infer the full route map from files by hand.
|
|
196
|
+
2. Prefer adding explicit ids before converting links for important routes.
|
|
197
|
+
3. Never edit `src/pracht.d.ts` or `src/pracht-routes.ts` manually.
|
|
198
|
+
4. Keep plain `<a href="...">` where a URL is genuinely external, opaque, or
|
|
199
|
+
user-provided.
|
|
200
|
+
5. After adding/removing/renaming routes, run `pracht typegen` and include the
|
|
201
|
+
generated file changes in the same commit.
|
|
202
|
+
|
|
203
|
+
$ARGUMENTS
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: upgrade-pracht
|
|
3
|
+
version: 1.0.0
|
|
4
|
+
description: |
|
|
5
|
+
Upgrade the @pracht/* packages in an app safely: inventory installed
|
|
6
|
+
versions, read the changelogs between installed and target, map breaking
|
|
7
|
+
changes to actual usage in the codebase, apply the upgrade, and walk the
|
|
8
|
+
verification ladder (doctor, typegen, verify, build, tests).
|
|
9
|
+
Use when asked to "upgrade pracht", "update @pracht packages", "bump the
|
|
10
|
+
framework", "what changed in the new pracht version", or "is this pracht
|
|
11
|
+
upgrade safe".
|
|
12
|
+
allowed-tools:
|
|
13
|
+
- Bash
|
|
14
|
+
- Read
|
|
15
|
+
- Edit
|
|
16
|
+
- Grep
|
|
17
|
+
- Glob
|
|
18
|
+
- AskUserQuestion
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
# Pracht Upgrade
|
|
22
|
+
|
|
23
|
+
Upgrade `@pracht/*` dependencies with the changelog read *before* the install,
|
|
24
|
+
not after the build breaks.
|
|
25
|
+
|
|
26
|
+
## Step 1: Inventory
|
|
27
|
+
|
|
28
|
+
List every installed pracht package and its resolved version:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pnpm list --depth 1 --json | grep -A2 '@pracht/' # or read package.json + lockfile
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The family: `@pracht/core`, `@pracht/cli`, `@pracht/vite-plugin`,
|
|
35
|
+
`@pracht/adapter-node`, `@pracht/adapter-cloudflare`, `@pracht/adapter-vercel`,
|
|
36
|
+
`@pracht/preact-ssr-precompile`. Get the latest published versions with
|
|
37
|
+
`npm view <pkg> version`.
|
|
38
|
+
|
|
39
|
+
## Step 2: Understand the versioning model
|
|
40
|
+
|
|
41
|
+
Pracht packages are **independently versioned** (the repo's changesets config
|
|
42
|
+
has empty `fixed`/`linked` groups) — `@pracht/core` can be at 0.9.x while
|
|
43
|
+
`@pracht/cli` is at 1.6.x. There is no "one framework version". Two
|
|
44
|
+
consequences:
|
|
45
|
+
|
|
46
|
+
1. **Internal dependencies are pinned exact.** Published packages depend on
|
|
47
|
+
their siblings at exact versions (e.g. `@pracht/vite-plugin@0.5.0` depends
|
|
48
|
+
on `@pracht/core@0.9.0`, not a range). Upgrade the whole family in one
|
|
49
|
+
move; upgrading only one package can drag in a second copy of
|
|
50
|
+
`@pracht/core` and split the runtime.
|
|
51
|
+
2. **Most packages are 0.x**, so under semver a *minor* bump may be breaking —
|
|
52
|
+
treat `### Minor Changes` entries on 0.x packages with the same care as
|
|
53
|
+
majors.
|
|
54
|
+
|
|
55
|
+
After any upgrade, confirm a single core resolution:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
pnpm why @pracht/core # exactly one version may appear
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Step 3: Read the changelogs between installed and target
|
|
62
|
+
|
|
63
|
+
Only `@pracht/cli` ships `CHANGELOG.md` in its npm tarball
|
|
64
|
+
(`node_modules/@pracht/cli/CHANGELOG.md`); the other packages publish `dist/`
|
|
65
|
+
only. Fetch their changelogs from the repo instead:
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
https://raw.githubusercontent.com/JoviDeCroock/pracht/main/packages/<dir>/CHANGELOG.md
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
| Package | Repo directory |
|
|
72
|
+
| ------- | -------------- |
|
|
73
|
+
| `@pracht/core` | `packages/framework` |
|
|
74
|
+
| `@pracht/cli` | `packages/cli` |
|
|
75
|
+
| `@pracht/vite-plugin` | `packages/vite-plugin` |
|
|
76
|
+
| `@pracht/adapter-node` / `-cloudflare` / `-vercel` | `packages/adapter-*` |
|
|
77
|
+
| `@pracht/preact-ssr-precompile` | `packages/preact-ssr-precompile` |
|
|
78
|
+
|
|
79
|
+
Changelogs are changesets-generated: `## X.Y.Z` sections containing
|
|
80
|
+
`### Major Changes` / `### Minor Changes` / `### Patch Changes`. Read every
|
|
81
|
+
section between the installed and target version of every installed package.
|
|
82
|
+
|
|
83
|
+
## Step 4: Map changes onto this app
|
|
84
|
+
|
|
85
|
+
Classify each entry as **breaking** / **feature** / **fix**. For each breaking
|
|
86
|
+
(or 0.x minor) entry, grep the app for the APIs, exports, config options, and
|
|
87
|
+
generated-file shapes it names, and record: affected files, the migration the
|
|
88
|
+
changelog prescribes, and whether it can be applied mechanically. Also
|
|
89
|
+
re-check peer ranges after a major target bump — `@pracht/vite-plugin`
|
|
90
|
+
requires `vite` (^8), `@pracht/adapter-cloudflare` requires `vite` and
|
|
91
|
+
`wrangler` (^4.81), `@pracht/core` requires `preact` (^10) and
|
|
92
|
+
`preact-render-to-string` (^6).
|
|
93
|
+
|
|
94
|
+
Present the plan as a table:
|
|
95
|
+
|
|
96
|
+
| Package | Installed → Target | Breaking entries | App impact | Migration |
|
|
97
|
+
| ------- | ------------------ | ---------------- | ---------- | --------- |
|
|
98
|
+
|
|
99
|
+
## Step 5: Confirm, then apply
|
|
100
|
+
|
|
101
|
+
Use `AskUserQuestion` before touching anything when breaking migrations are
|
|
102
|
+
required: confirm the target versions and which migrations to apply. Then:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
pnpm up '@pracht/core@<v>' '@pracht/cli@<v>' '@pracht/vite-plugin@<v>' <adapters...>
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Upgrade every installed `@pracht/*` package in the same command. Apply the
|
|
109
|
+
agreed code migrations with minimal diffs, one changelog entry at a time.
|
|
110
|
+
|
|
111
|
+
## Step 6: Verification ladder
|
|
112
|
+
|
|
113
|
+
Run in order; stop and fix at the first failure:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
pracht doctor --json # wiring still valid
|
|
117
|
+
pracht typegen --check # generated route types up to date?
|
|
118
|
+
pracht typegen # regenerate if --check failed or routes changed
|
|
119
|
+
pracht verify --json # framework-aware checks
|
|
120
|
+
pracht build # full production build (budgets included)
|
|
121
|
+
pnpm test # the app's own suite
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
`pracht doctor`, `verify`, and `typegen --check` exit non-zero on failure, so
|
|
125
|
+
they gate CI cleanly.
|
|
126
|
+
|
|
127
|
+
## Step 7: Rollback note
|
|
128
|
+
|
|
129
|
+
If the ladder cannot be made green, roll back rather than shipping a
|
|
130
|
+
half-upgrade:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
git restore package.json pnpm-lock.yaml && pnpm install
|
|
134
|
+
git checkout -- <migrated files> # or revert the upgrade commit
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Because internal deps are exact-pinned, a *partial* rollback (one package
|
|
138
|
+
back, the rest forward) recreates the duplicate-core problem from Step 2 —
|
|
139
|
+
roll the whole family back together.
|
|
140
|
+
|
|
141
|
+
## Rules
|
|
142
|
+
|
|
143
|
+
1. Never mix `@pracht/*` versions from different release waves — upgrade and
|
|
144
|
+
roll back the family as a unit, and verify with `pnpm why @pracht/core`.
|
|
145
|
+
2. Read changelogs before installing, not after something breaks.
|
|
146
|
+
3. Never apply a breaking-change migration without explicit user confirmation
|
|
147
|
+
via `AskUserQuestion`.
|
|
148
|
+
4. Treat 0.x minor bumps as potentially breaking.
|
|
149
|
+
5. Do not hand-edit lockfiles; let the package manager resolve.
|
|
150
|
+
|
|
151
|
+
$ARGUMENTS
|