create-koppajs 1.2.1 → 1.2.2

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/CHANGELOG.md CHANGED
@@ -16,6 +16,24 @@ _No unreleased changes yet._
16
16
 
17
17
  ---
18
18
 
19
+ ## [1.2.2] — Node & Validation Baseline Alignment
20
+
21
+ **2026-03-27**
22
+
23
+ ### Changed
24
+
25
+ - raised the repository and bundled starter minimum Node.js version to `>=22`
26
+ and expanded CI coverage to Node 24
27
+ - aligned CI and release validation around `pnpm validate`, added a packed-CLI
28
+ smoke test, and switched release automation to the maintainer default from
29
+ `.nvmrc`
30
+ - refreshed bundled starter dependencies to the current `@koppajs/koppajs-core`,
31
+ `@koppajs/koppajs-vite-plugin`, and `@koppajs/koppajs-router` baselines
32
+ - removed the obsolete starter-local `.kpa` export wrapper now that the Vite
33
+ plugin emits valid ES modules itself
34
+
35
+ ---
36
+
19
37
  ## [1.2.1] — Starter Source Of Truth Cleanup
20
38
 
21
39
  **2026-03-26**
package/README.md CHANGED
@@ -1,9 +1,63 @@
1
- # create-koppajs
2
-
3
- `create-koppajs` is the official KoppaJS CLI scaffolder. It creates a new
4
- project by copying the versioned base starter in `template/`, optionally
5
- applying a supported starter overlay from `template-overlays/`, and patching a
6
- small, explicit set of identity files.
1
+ <a id="readme-top"></a>
2
+
3
+ <div align="center">
4
+ <img src="https://public-assets-1b57ca06-687a-4142-a525-0635f7649a5c.s3.eu-central-1.amazonaws.com/koppajs/koppajs-logo-text-900x226.png" width="500" alt="KoppaJS Logo">
5
+ </div>
6
+
7
+ <br>
8
+
9
+ <div align="center">
10
+ <a href="https://www.npmjs.com/package/create-koppajs"><img src="https://img.shields.io/npm/v/create-koppajs?style=flat-square" alt="npm version"></a>
11
+ <a href="https://github.com/koppajs/create-koppajs/actions"><img src="https://img.shields.io/github/actions/workflow/status/koppajs/create-koppajs/ci.yml?branch=main&style=flat-square" alt="CI Status"></a>
12
+ <a href="./LICENSE"><img src="https://img.shields.io/badge/license-Apache--2.0-blue?style=flat-square" alt="License"></a>
13
+ </div>
14
+
15
+ <br>
16
+
17
+ <div align="center">
18
+ <h1 align="center">create-koppajs</h1>
19
+ <h3 align="center">Official project scaffolder for KoppaJS</h3>
20
+ <p align="center">
21
+ <i>Generate a ready-to-run KoppaJS starter with the current quality baseline in one command.</i>
22
+ </p>
23
+ </div>
24
+
25
+ <br>
26
+
27
+ <div align="center">
28
+ <p align="center">
29
+ <a href="https://github.com/koppajs/koppajs-documentation">Documentation</a>
30
+ &middot;
31
+ <a href="https://github.com/koppajs/koppajs-core">KoppaJS Core</a>
32
+ &middot;
33
+ <a href="https://github.com/koppajs/koppajs-vite-plugin">Vite Plugin</a>
34
+ &middot;
35
+ <a href="https://github.com/koppajs/koppajs-router">Router</a>
36
+ &middot;
37
+ <a href="https://github.com/koppajs/create-koppajs/issues">Issues</a>
38
+ </p>
39
+ </div>
40
+
41
+ <br>
42
+
43
+ <details>
44
+ <summary>Table of Contents</summary>
45
+ <ol>
46
+ <li><a href="#purpose">Purpose</a></li>
47
+ <li><a href="#repository-classification">Repository Classification</a></li>
48
+ <li><a href="#ownership-boundaries">Ownership Boundaries</a></li>
49
+ <li><a href="#public-contract">Public Contract</a></li>
50
+ <li><a href="#usage">Usage</a></li>
51
+ <li><a href="#requirements">Requirements</a></li>
52
+ <li><a href="#generated-starters">Generated Starters</a></li>
53
+ <li><a href="#ecosystem-fit">Ecosystem Fit</a></li>
54
+ <li><a href="#architecture-governance">Architecture & Governance</a></li>
55
+ <li><a href="#community-contribution">Community & Contribution</a></li>
56
+ <li><a href="#license">License</a></li>
57
+ </ol>
58
+ </details>
59
+
60
+ ---
7
61
 
8
62
  ## Purpose
9
63
 
@@ -17,6 +71,8 @@ This repository exists to do one job well:
17
71
  It is not a runtime package and it does not own application behavior after
18
72
  generation.
19
73
 
74
+ ---
75
+
20
76
  ## Repository Classification
21
77
 
22
78
  - Repo type: CLI scaffolding package with a bundled starter family
@@ -27,6 +83,8 @@ generation.
27
83
  - UI surface: none at the repository root; the generated starter owns the UI
28
84
  - Maturity level: stable, contract-governed, maintenance-first
29
85
 
86
+ ---
87
+
30
88
  ## Ownership Boundaries
31
89
 
32
90
  - `bin/create-koppajs.js` owns argument parsing, prompting, validation, starter
@@ -45,6 +103,8 @@ The root package must not take on runtime concerns that belong in generated
45
103
  applications, and generated applications must not depend on unpublished root
46
104
  files after scaffold completion.
47
105
 
106
+ ---
107
+
48
108
  ## Public Contract
49
109
 
50
110
  The stable public contract of this repository is:
@@ -71,6 +131,8 @@ The governing specs for that contract are:
71
131
  - [docs/specs/cli-scaffolding.md](./docs/specs/cli-scaffolding.md)
72
132
  - [docs/specs/template-starter-contract.md](./docs/specs/template-starter-contract.md)
73
133
 
134
+ ---
135
+
74
136
  ## Usage
75
137
 
76
138
  Default starter:
@@ -107,11 +169,14 @@ pnpm install
107
169
  pnpm dev
108
170
  ```
109
171
 
172
+ ---
173
+
110
174
  ## Requirements
111
175
 
112
- - for `create-koppajs`: Node.js `>=20`
113
- - for generated starter projects: pnpm `>=10` and a starter-supported Node.js
114
- line, currently `20.19+`, `22.13+`, or `24+`
176
+ - for `create-koppajs`: Node.js `>=22`
177
+ - for generated starter projects: Node.js `>=22` and pnpm `>=10`
178
+
179
+ ---
115
180
 
116
181
  ## Generated Starters
117
182
 
@@ -133,6 +198,8 @@ The root repository treats those starters as versioned product surface, not
133
198
  test data. `template/` plus the supported overlays are the only source of truth
134
199
  for starter behavior.
135
200
 
201
+ ---
202
+
136
203
  ## Ecosystem Fit
137
204
 
138
205
  `create-koppajs` is the canonical entry point for starting a new KoppaJS
@@ -147,9 +214,11 @@ application. It complements:
147
214
  The repository stays intentionally narrow so the CLI, starter contract, and
148
215
  governance baseline can evolve together without hidden behavior.
149
216
 
150
- ## Governance
217
+ ---
218
+
219
+ ## Architecture & Governance
151
220
 
152
- The root meta layer defines how this repository changes:
221
+ Project intent, contributor rules, and documentation contracts live in the local repo meta layer:
153
222
 
154
223
  - [AI_CONSTITUTION.md](./AI_CONSTITUTION.md)
155
224
  - [ARCHITECTURE.md](./ARCHITECTURE.md)
@@ -158,12 +227,36 @@ The root meta layer defines how this repository changes:
158
227
  - [TESTING_STRATEGY.md](./TESTING_STRATEGY.md)
159
228
  - [RELEASE.md](./RELEASE.md)
160
229
  - [ROADMAP.md](./ROADMAP.md)
161
- - [docs/meta/README.md](./docs/meta/README.md)
230
+ - [CHANGELOG.md](./CHANGELOG.md)
231
+ - [CONTRIBUTING.md](./CONTRIBUTING.md)
232
+ - [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md)
233
+ - [docs/specs/README.md](./docs/specs/README.md)
234
+ - [docs/specs/repository-documentation-contract.md](./docs/specs/repository-documentation-contract.md)
162
235
  - [docs/architecture/README.md](./docs/architecture/README.md)
236
+ - [docs/meta/README.md](./docs/meta/README.md)
163
237
  - [docs/quality/README.md](./docs/quality/README.md)
164
238
 
165
- Tagged releases are documented in [CHANGELOG.md](./CHANGELOG.md). Contributor
166
- workflow rules live in [CONTRIBUTING.md](./CONTRIBUTING.md).
239
+ The file-shape contract for `README.md`, `CHANGELOG.md`, `CODE_OF_CONDUCT.md`, and `CONTRIBUTING.md` is defined in [docs/specs/repository-documentation-contract.md](./docs/specs/repository-documentation-contract.md).
240
+
241
+ Run the local document guard before committing:
242
+
243
+ ```bash
244
+ pnpm run check:docs
245
+ ```
246
+
247
+ ---
248
+
249
+ ## Community & Contribution
250
+
251
+ Issues and pull requests are welcome:
252
+
253
+ https://github.com/koppajs/create-koppajs/issues
254
+
255
+ Contributor workflow details live in [CONTRIBUTING.md](./CONTRIBUTING.md).
256
+
257
+ Community expectations live in [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md).
258
+
259
+ ---
167
260
 
168
261
  ## License
169
262
 
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync, copyFileSync, statSync } from "node:fs";
3
+ import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync, copyFileSync, statSync, realpathSync } from "node:fs";
4
4
  import { basename, join, dirname, resolve } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import { createInterface } from "node:readline";
@@ -335,7 +335,15 @@ export async function runCli(
335
335
  }
336
336
 
337
337
  function isDirectExecution() {
338
- return Boolean(process.argv[1]) && resolve(process.argv[1]) === __filename;
338
+ if (!process.argv[1]) {
339
+ return false;
340
+ }
341
+
342
+ try {
343
+ return realpathSync(process.argv[1]) === realpathSync(__filename);
344
+ } catch {
345
+ return resolve(process.argv[1]) === __filename;
346
+ }
339
347
  }
340
348
 
341
349
  if (isDirectExecution()) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-koppajs",
3
- "version": "1.2.1",
3
+ "version": "1.2.2",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Scaffold a new KoppaJS project in seconds.",
@@ -17,7 +17,7 @@
17
17
  ],
18
18
  "packageManager": "pnpm@10.12.1",
19
19
  "engines": {
20
- "node": ">=20",
20
+ "node": ">=22",
21
21
  "pnpm": ">=10"
22
22
  },
23
23
  "scripts": {
@@ -29,12 +29,15 @@
29
29
  "test:watch": "node scripts/run-unit-tests.mjs --watch",
30
30
  "test:smoke": "node scripts/smoke-test.mjs",
31
31
  "test:template-build": "node scripts/template-build-test.mjs",
32
+ "test:package": "node scripts/test-package-smoke.mjs",
32
33
  "test": "npm run test:unit && npm run test:smoke",
33
34
  "pack:dry-run": "npm pack --dry-run",
34
- "check": "npm run check:meta && npm run lint && npm run format:check && npm run check:cli && npm test && npm run pack:dry-run",
35
- "release:check": "npm run check && npm run test:template-build",
35
+ "check": "pnpm run check:docs && npm run check:meta && npm run lint && npm run format:check && npm run check:cli && npm test && npm run pack:dry-run",
36
+ "validate": "pnpm run check && pnpm run test:template-build && pnpm run test:package",
37
+ "release:check": "pnpm run validate",
36
38
  "prepare": "husky",
37
- "clean": "node scripts/clean.mjs"
39
+ "clean": "node scripts/clean.mjs",
40
+ "check:docs": "node scripts/check-doc-contract.mjs"
38
41
  },
39
42
  "author": "Bastian Bensch",
40
43
  "license": "Apache-2.0",
@@ -10,14 +10,13 @@ This repository is a minimal KoppaJS application starter. It intentionally demon
10
10
  - `src/counter-component.kpa` is the example stateful leaf component. It owns its own `count` state and button event handlers.
11
11
  - `src/style.css` contains the global reset only.
12
12
  - `public/` contains static assets referenced by the HTML shell or components.
13
- - `vite.config.mjs` wires the KoppaJS Vite plugin into the build and applies a small repo-local compatibility transform so `.kpa` files are emitted as valid ES modules.
14
- - `vitest.config.mjs` reuses the Vite config so unit and integration tests exercise the same `.kpa` loading path as the application.
13
+ - `vite.config.mjs` wires the KoppaJS Vite plugin into the build without local compatibility layers.
14
+ - `vitest.config.mjs` reuses the Vite config so integration tests exercise the same `.kpa` loading path as the application.
15
15
  - `playwright.config.ts` runs a Chromium smoke test against `vite preview`, which keeps the minimal UI starter covered by one real browser path.
16
16
  - `CHANGELOG.md` records official tagged milestones for the repository.
17
17
  - `RELEASE.md` documents the maintainer release path across `develop`, `release/*`, and `main`.
18
18
  - `commitlint.config.mjs` defines the repository's commit message convention.
19
19
  - `.github/workflows/release.yml` reruns validation for `vX.Y.Z` tags, checks version alignment, and creates GitHub Releases.
20
- - `tests/unit/` covers isolated logic such as the `.kpa` module export normalization helper.
21
20
  - `tests/integration/` covers application bootstrap wiring without requiring a full browser run.
22
21
  - `tests/e2e/` covers the observable starter flow in a real browser.
23
22
  - `tsconfig.json` enforces strict TypeScript rules for source, tests, and TypeScript config files. `.kpa` compilation is still handled by the KoppaJS Vite plugin.
@@ -46,24 +45,23 @@ More detailed boundaries live in [docs/architecture/module-boundaries.md](./docs
46
45
 
47
46
  ## Module responsibilities
48
47
 
49
- | Module | Responsibility | Must not do |
50
- | ------------------------------- | ------------------------------------------------------------------------------------------ | --------------------------------------------------------------------- |
51
- | `index.html` | Declare root tag and static assets | Hold business logic or feature wiring |
52
- | `CHANGELOG.md` | Record official tagged release notes | Become a scratchpad for speculative work or duplicate repository docs |
53
- | `RELEASE.md` | Define the maintainer release procedure | Drift away from actual branch, tag, or workflow behavior |
54
- | `commitlint.config.mjs` | Define commit message validation rules | Expand into unrelated repository lint policy |
55
- | `src/main.ts` | Bootstrap and component registration | Accumulate feature logic, state, or DOM manipulation |
56
- | `src/app-view.kpa` | Root layout and composition | Own unrelated business workflows or register components |
57
- | `src/counter-component.kpa` | Local interactive example state | Reach into global DOM or mutate app shell concerns |
58
- | `src/style.css` | Global reset/base rules | Contain feature-specific visuals that belong to components |
59
- | `public/` | Static assets | Store generated build output or source code |
60
- | `tests/unit/` | Isolated tests for helper or tooling logic | Production runtime side effects or browser-only expectations |
61
- | `tests/integration/` | Wiring tests across bootstrap boundaries | Full browser assertions already covered by Playwright |
62
- | `tests/e2e/` | Real browser smoke coverage of the starter UI | Deep implementation-detail assertions or brittle CSS-driven flows |
63
- | `vite.config.mjs` | Build and dev server integration, including the `.kpa` module export compatibility wrapper | Application runtime logic |
64
- | `vitest.config.mjs` | Unit and integration test runner configuration | Runtime feature code |
65
- | `playwright.config.ts` | Browser smoke-test orchestration against preview output | Application runtime logic |
66
- | `.github/workflows/release.yml` | Tagged release validation and GitHub Release creation | Publish to npm while the repository remains private |
48
+ | Module | Responsibility | Must not do |
49
+ | ------------------------------- | ------------------------------------------------------------------------- | --------------------------------------------------------------------- |
50
+ | `index.html` | Declare root tag and static assets | Hold business logic or feature wiring |
51
+ | `CHANGELOG.md` | Record official tagged release notes | Become a scratchpad for speculative work or duplicate repository docs |
52
+ | `RELEASE.md` | Define the maintainer release procedure | Drift away from actual branch, tag, or workflow behavior |
53
+ | `commitlint.config.mjs` | Define commit message validation rules | Expand into unrelated repository lint policy |
54
+ | `src/main.ts` | Bootstrap and component registration | Accumulate feature logic, state, or DOM manipulation |
55
+ | `src/app-view.kpa` | Root layout and composition | Own unrelated business workflows or register components |
56
+ | `src/counter-component.kpa` | Local interactive example state | Reach into global DOM or mutate app shell concerns |
57
+ | `src/style.css` | Global reset/base rules | Contain feature-specific visuals that belong to components |
58
+ | `public/` | Static assets | Store generated build output or source code |
59
+ | `tests/integration/` | Wiring tests across bootstrap boundaries | Full browser assertions already covered by Playwright |
60
+ | `tests/e2e/` | Real browser smoke coverage of the starter UI | Deep implementation-detail assertions or brittle CSS-driven flows |
61
+ | `vite.config.mjs` | Build and dev server integration through the upstream KoppaJS Vite plugin | Application runtime logic |
62
+ | `vitest.config.mjs` | Unit and integration test runner configuration | Runtime feature code |
63
+ | `playwright.config.ts` | Browser smoke-test orchestration against preview output | Application runtime logic |
64
+ | `.github/workflows/release.yml` | Tagged release validation and GitHub Release creation | Publish to npm while the repository remains private |
67
65
 
68
66
  ## Invariants
69
67
 
@@ -9,13 +9,14 @@ Only tagged versions represent official releases.
9
9
 
10
10
  ## [Unreleased]
11
11
 
12
- This section is intentionally empty.
13
-
14
- Changes will only appear here when they:
15
-
16
- - alter the project's observable behavior,
17
- - change contributor or maintainer workflow,
18
- - or modify documented repository guarantees.
12
+ ### Changed
13
+
14
+ - raised the minimum Node.js version to `>=22` and expanded CI coverage to
15
+ Node 24
16
+ - refreshed the starter dependency baseline to the current `@koppajs/koppajs-core`
17
+ and `@koppajs/koppajs-vite-plugin` releases
18
+ - removed the obsolete local `.kpa` export wrapper because the upstream Vite
19
+ plugin already emits valid ES modules
19
20
 
20
21
  ---
21
22
 
@@ -4,12 +4,9 @@
4
4
 
5
5
  Requirements:
6
6
 
7
- - Node.js 20.19+, 22.13+, or 24+
7
+ - Node.js >= 22
8
8
  - pnpm 10 or newer
9
9
 
10
- Node 23 is intentionally not part of the supported range because the current
11
- upstream frontend tooling excludes it.
12
-
13
10
  Install and run:
14
11
 
15
12
  ```bash
@@ -81,12 +81,9 @@ Use it as a starting point for new KoppaJS projects or as a reference for how co
81
81
 
82
82
  ## Requirements
83
83
 
84
- - Node.js 20.19+, 22.13+, or 24+
84
+ - Node.js >= 22
85
85
  - pnpm >= 10
86
86
 
87
- Node 23 is intentionally not treated as supported here because the current
88
- upstream frontend toolchain excludes it.
89
-
90
87
  ---
91
88
 
92
89
  ## Getting Started
@@ -66,12 +66,9 @@ Before cutting a release, ensure all of the following are true:
66
66
 
67
67
  Tooling expectations for local verification:
68
68
 
69
- - Node.js 20.19+, 22.13+, or 24+
69
+ - Node.js 22 or newer
70
70
  - pnpm 10 or newer
71
71
 
72
- Node 23 is intentionally not treated as supported here because the current
73
- upstream frontend toolchain excludes it.
74
-
75
72
  This repository also enforces `engine-strict=true` in `.npmrc`, so incompatible
76
73
  Node.js or pnpm versions should be treated as a release blocker.
77
74
 
@@ -26,7 +26,10 @@ This repository now has a small automated testing stack aligned with the actual
26
26
 
27
27
  ### Unit tests
28
28
 
29
- Use unit tests for isolated logic that can fail independently of the browser runtime. Today that includes the repo-local `.kpa` export normalization helper in `vite.config.mjs`.
29
+ Use unit tests for isolated logic that can fail independently of the browser runtime.
30
+
31
+ Today the starter does not ship dedicated extracted helper modules, so unit-only
32
+ coverage is optional until new isolated logic actually exists.
30
33
 
31
34
  Typical triggers:
32
35
 
@@ -12,6 +12,9 @@ on:
12
12
  jobs:
13
13
  validate:
14
14
  runs-on: ubuntu-latest
15
+ strategy:
16
+ matrix:
17
+ node: [22, 24]
15
18
 
16
19
  steps:
17
20
  - name: Check out repository
@@ -25,7 +28,7 @@ jobs:
25
28
  - name: Set up Node.js
26
29
  uses: actions/setup-node@v4
27
30
  with:
28
- node-version: 20
31
+ node-version: ${{ matrix.node }}
29
32
  cache: pnpm
30
33
 
31
34
  - name: Install dependencies
@@ -26,7 +26,7 @@ jobs:
26
26
  - name: Set up Node.js
27
27
  uses: actions/setup-node@v4
28
28
  with:
29
- node-version: 20
29
+ node-version: 22
30
30
  cache: pnpm
31
31
 
32
32
  - name: Install dependencies
@@ -0,0 +1,32 @@
1
+ # ADR 0003: Rely on upstream KPA ES module output
2
+
3
+ ## Context
4
+
5
+ The starter depends on `@koppajs/koppajs-vite-plugin` to transform `.kpa`
6
+ files for both development and production builds.
7
+
8
+ Earlier starter revisions carried a repo-local compatibility wrapper in
9
+ `vite.config.mjs` because the plugin emitted raw object literals instead of ES
10
+ modules.
11
+
12
+ The current upstream plugin now emits `export default ...` modules directly, so
13
+ keeping the local wrapper would only duplicate behavior and create drift
14
+ between the starter and the official plugin contract.
15
+
16
+ ## Decision
17
+
18
+ Use the upstream KoppaJS Vite plugin output directly and keep `vite.config.mjs`
19
+ limited to plugin wiring.
20
+
21
+ ## Consequences
22
+
23
+ - The starter stays aligned with the official Vite plugin behavior instead of
24
+ carrying a stale local workaround.
25
+ - Build configuration stays smaller and easier to reason about.
26
+ - Future maintainers must update the starter and the plugin together if the
27
+ `.kpa` module contract changes again.
28
+
29
+ ## Alternatives considered
30
+
31
+ - Keeping a repo-local post-transform in `vite.config.mjs`
32
+ - Forking the plugin behavior inside the starter
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Context
4
4
 
5
- The repository already had a living meta layer and a real interactive UI, but its practical quality gates were still limited to `pnpm typecheck`, `pnpm build`, and manual smoke testing. That left the repo-local `.kpa` build workaround, bootstrap wiring, and browser-visible starter behavior exposed to easy regression.
5
+ The repository already had a living meta layer and a real interactive UI, but its practical quality gates were still limited to `pnpm typecheck`, `pnpm build`, and manual smoke testing. That left bootstrap wiring and browser-visible starter behavior exposed to easy regression.
6
6
 
7
7
  ## Decision
8
8
 
@@ -31,7 +31,7 @@ Use [`TEMPLATE.md`](./TEMPLATE.md). Every ADR must contain:
31
31
 
32
32
  - [`0001-keep-the-starter-minimal.md`](./0001-keep-the-starter-minimal.md)
33
33
  - [`0002-adopt-a-living-meta-layer.md`](./0002-adopt-a-living-meta-layer.md)
34
- - [`0003-normalize-kpa-plugin-output.md`](./0003-normalize-kpa-plugin-output.md)
34
+ - [`0003-rely-on-upstream-kpa-es-module-output.md`](./0003-rely-on-upstream-kpa-es-module-output.md)
35
35
  - [`0004-adopt-an-automated-quality-baseline.md`](./0004-adopt-an-automated-quality-baseline.md)
36
36
  - [`0005-adopt-a-tag-driven-release-baseline.md`](./0005-adopt-a-tag-driven-release-baseline.md)
37
37
  - [`0006-adopt-commit-message-conventions.md`](./0006-adopt-commit-message-conventions.md)
@@ -4,25 +4,24 @@
4
4
 
5
5
  This repository is intentionally shallow. Its boundaries are designed to keep the starter easy to understand.
6
6
 
7
- | Path | Responsibility | Allowed dependencies | Must not depend on |
8
- | ------------------------------- | ------------------------------------------------------------------------------------------ | -------------------------------------------------- | ----------------------------------------------------------------- |
9
- | `index.html` | Document shell, root element, asset references | Static assets, `src/main.ts`, `src/style.css` | Feature logic, extra scripts, framework-specific behavior |
10
- | `CHANGELOG.md` | Tagged release history | Release notes, versioned milestones | Unreleased planning notes or unrelated contributor guidance |
11
- | `RELEASE.md` | Maintainer release procedure | `package.json`, `CHANGELOG.md`, GitHub workflows | Runtime code or undocumented branch conventions |
12
- | `commitlint.config.mjs` | Commit message rules | Conventional commit policy | Source-code linting or release branching rules |
13
- | `src/main.ts` | Application bootstrap and component registration | `@koppajs/koppajs-core`, local `.kpa` modules | UI business logic, ad hoc DOM manipulation, network code |
14
- | `src/app-view.kpa` | Root page layout and composition | Local markup, local CSS, child components | Global bootstrap concerns, unrelated feature orchestration |
15
- | `src/counter-component.kpa` | Example local state and interaction | Local markup, local methods, local CSS | Shared infrastructure, global state, direct root DOM coordination |
16
- | `src/style.css` | Global reset and truly global base rules | Standard CSS | Component-specific visual rules |
17
- | `public/` | Static assets served as-is | Static files only | Source code or generated artifacts |
18
- | `tests/unit/` | Isolated tests for helper and tooling logic | Vitest, local modules | Full browser expectations or long integration chains |
19
- | `tests/integration/` | Bootstrap and boundary-level integration tests | Vitest, local modules, selective mocks | Browser-only smoke assertions |
20
- | `tests/e2e/` | Real-browser smoke coverage | Playwright, preview server | Implementation-detail assertions |
21
- | `vite.config.mjs` | Build and dev server integration, including the `.kpa` module export compatibility wrapper | Vite and the KoppaJS Vite plugin | Application runtime logic |
22
- | `vitest.config.mjs` | Vitest orchestration merged with the Vite config | Vitest and Vite config | Application runtime logic |
23
- | `playwright.config.ts` | Playwright orchestration against preview output | Playwright and preview server script | Application runtime logic |
24
- | `.github/workflows/release.yml` | Tag-triggered validation and GitHub Release creation | GitHub Actions, `package.json`, validation scripts | npm publishing while `package.json` is `private` |
25
- | `tsconfig.json` | TypeScript compiler behavior for source, tests, and TS config files | TypeScript compiler options | Application runtime logic |
7
+ | Path | Responsibility | Allowed dependencies | Must not depend on |
8
+ | ------------------------------- | ------------------------------------------------------------------------- | -------------------------------------------------- | ----------------------------------------------------------------- |
9
+ | `index.html` | Document shell, root element, asset references | Static assets, `src/main.ts`, `src/style.css` | Feature logic, extra scripts, framework-specific behavior |
10
+ | `CHANGELOG.md` | Tagged release history | Release notes, versioned milestones | Unreleased planning notes or unrelated contributor guidance |
11
+ | `RELEASE.md` | Maintainer release procedure | `package.json`, `CHANGELOG.md`, GitHub workflows | Runtime code or undocumented branch conventions |
12
+ | `commitlint.config.mjs` | Commit message rules | Conventional commit policy | Source-code linting or release branching rules |
13
+ | `src/main.ts` | Application bootstrap and component registration | `@koppajs/koppajs-core`, local `.kpa` modules | UI business logic, ad hoc DOM manipulation, network code |
14
+ | `src/app-view.kpa` | Root page layout and composition | Local markup, local CSS, child components | Global bootstrap concerns, unrelated feature orchestration |
15
+ | `src/counter-component.kpa` | Example local state and interaction | Local markup, local methods, local CSS | Shared infrastructure, global state, direct root DOM coordination |
16
+ | `src/style.css` | Global reset and truly global base rules | Standard CSS | Component-specific visual rules |
17
+ | `public/` | Static assets served as-is | Static files only | Source code or generated artifacts |
18
+ | `tests/integration/` | Bootstrap and boundary-level integration tests | Vitest, local modules, selective mocks | Browser-only smoke assertions |
19
+ | `tests/e2e/` | Real-browser smoke coverage | Playwright, preview server | Implementation-detail assertions |
20
+ | `vite.config.mjs` | Build and dev server integration through the upstream KoppaJS Vite plugin | Vite and the KoppaJS Vite plugin | Application runtime logic |
21
+ | `vitest.config.mjs` | Vitest orchestration merged with the Vite config | Vitest and Vite config | Application runtime logic |
22
+ | `playwright.config.ts` | Playwright orchestration against preview output | Playwright and preview server script | Application runtime logic |
23
+ | `.github/workflows/release.yml` | Tag-triggered validation and GitHub Release creation | GitHub Actions, `package.json`, validation scripts | npm publishing while `package.json` is `private` |
24
+ | `tsconfig.json` | TypeScript compiler behavior for source, tests, and TS config files | TypeScript compiler options | Application runtime logic |
26
25
 
27
26
  ## Boundary rules
28
27
 
@@ -33,7 +33,7 @@ Keep the starter's tooling, tests, and contributor workflow consistent enough th
33
33
  - Consistent formatting on supported file types
34
34
  - Conventional commit history for maintainable review and release preparation
35
35
  - Type-safe source and TypeScript tooling files
36
- - Automated confidence for the `.kpa` export workaround, bootstrap wiring, and the starter UI smoke path
36
+ - Automated confidence for bootstrap wiring and the starter UI smoke path
37
37
  - A guarded GitHub release path that fails when the pushed tag does not match `package.json`
38
38
  - Fast feedback before commit and in CI
39
39
 
@@ -16,7 +16,7 @@
16
16
  ],
17
17
  "packageManager": "pnpm@10.12.1",
18
18
  "engines": {
19
- "node": "^20.19.0 || ^22.13.0 || >=24.0.0",
19
+ "node": ">=22",
20
20
  "pnpm": ">=10"
21
21
  },
22
22
  "scripts": {
@@ -42,13 +42,13 @@
42
42
  "prepare": "husky"
43
43
  },
44
44
  "dependencies": {
45
- "@koppajs/koppajs-core": "^3.0.3"
45
+ "@koppajs/koppajs-core": "^3.0.7"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@commitlint/cli": "^20.1.0",
49
49
  "@commitlint/config-conventional": "^20.0.0",
50
50
  "@eslint/js": "^10.0.1",
51
- "@koppajs/koppajs-vite-plugin": "^1.0.1",
51
+ "@koppajs/koppajs-vite-plugin": "^1.0.4",
52
52
  "@playwright/test": "^1.58.2",
53
53
  "@types/node": "^25.4.0",
54
54
  "@vitest/coverage-v8": "^4.0.18",
@@ -9,8 +9,8 @@ importers:
9
9
  .:
10
10
  dependencies:
11
11
  '@koppajs/koppajs-core':
12
- specifier: ^3.0.3
13
- version: 3.0.3
12
+ specifier: ^3.0.7
13
+ version: 3.0.7
14
14
  devDependencies:
15
15
  '@commitlint/cli':
16
16
  specifier: ^20.1.0
@@ -22,8 +22,8 @@ importers:
22
22
  specifier: ^10.0.1
23
23
  version: 10.0.1(eslint@10.0.3(jiti@2.6.1))
24
24
  '@koppajs/koppajs-vite-plugin':
25
- specifier: ^1.0.1
26
- version: 1.0.1(typescript@5.9.3)(vite@7.2.6(@types/node@25.4.0)(jiti@2.6.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))
25
+ specifier: ^1.0.4
26
+ version: 1.0.4(typescript@5.9.3)(vite@7.2.6(@types/node@25.4.0)(jiti@2.6.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))
27
27
  '@playwright/test':
28
28
  specifier: ^1.58.2
29
29
  version: 1.58.2
@@ -605,13 +605,13 @@ packages:
605
605
  '@jridgewell/trace-mapping@0.3.31':
606
606
  resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
607
607
 
608
- '@koppajs/koppajs-core@3.0.3':
609
- resolution: {integrity: sha512-l6e+M8hEugVW/4crOzddeImLM/0j+rqSJ5ZkFyBZPdv57Bp45WWdbmoK7HNKzPd+L70SehCaH7CcgRKM8ltbCQ==}
610
- engines: {node: '>=20', pnpm: '>=10.24.0'}
608
+ '@koppajs/koppajs-core@3.0.7':
609
+ resolution: {integrity: sha512-ZVaeU93skwDJZU+lWQaIlbExLFXJRC4+lg1Stoi0QUNg3iHUNEm1k8ESfTeF+GIJordpYdRKr1uytAHsT+EXLQ==}
610
+ engines: {node: '>=22', pnpm: '>=10.24.0'}
611
611
 
612
- '@koppajs/koppajs-vite-plugin@1.0.1':
613
- resolution: {integrity: sha512-idjAD9zYQsK68yLxAI9So2bZScH7R4akDPsznHHivVCksMHSvkvQVtq328oeAg8rCi9AYN4z1Q0SAUB90aMDbg==}
614
- engines: {node: '>=20', pnpm: '>=10.24.0'}
612
+ '@koppajs/koppajs-vite-plugin@1.0.4':
613
+ resolution: {integrity: sha512-lBME5lq80t6Pk1lNDJeI4lOAfvfHR4AVqwpMLHUH5rqP9nbGh6t4ud/0llH4MiLNZAZK2HzNykzqO3+Rb+7tsw==}
614
+ engines: {node: '>=22', pnpm: '>=10.24.0'}
615
615
  peerDependencies:
616
616
  typescript: '>=5.5 <6'
617
617
  vite: ^7.0.0
@@ -2406,9 +2406,9 @@ snapshots:
2406
2406
  '@jridgewell/resolve-uri': 3.1.2
2407
2407
  '@jridgewell/sourcemap-codec': 1.5.5
2408
2408
 
2409
- '@koppajs/koppajs-core@3.0.3': {}
2409
+ '@koppajs/koppajs-core@3.0.7': {}
2410
2410
 
2411
- '@koppajs/koppajs-vite-plugin@1.0.1(typescript@5.9.3)(vite@7.2.6(@types/node@25.4.0)(jiti@2.6.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))':
2411
+ '@koppajs/koppajs-vite-plugin@1.0.4(typescript@5.9.3)(vite@7.2.6(@types/node@25.4.0)(jiti@2.6.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))':
2412
2412
  dependencies:
2413
2413
  acorn: 8.16.0
2414
2414
  autoprefixer: 10.4.23(postcss@8.5.6)
@@ -1,6 +1,4 @@
1
- import type { Plugin, UserConfig } from "vite";
2
-
3
- export declare function normalizeKpaModuleExport(): Plugin;
1
+ import type { UserConfig } from "vite";
4
2
 
5
3
  declare const config: UserConfig;
6
4
 
@@ -1,45 +1,10 @@
1
1
  import koppaPlugin from "@koppajs/koppajs-vite-plugin";
2
2
  import { defineConfig } from "vite";
3
3
 
4
- /**
5
- * Wrap raw `.kpa` object output in a valid ES module export.
6
- *
7
- * @returns {import("vite").Plugin}
8
- */
9
- export function normalizeKpaModuleExport() {
10
- return {
11
- name: "normalize-kpa-module-export",
12
- enforce: "post",
13
- /**
14
- * @param {string} code
15
- * @param {string} id
16
- */
17
- transform(code, id) {
18
- const cleanId = id.split("?")[0];
19
-
20
- if (!cleanId.endsWith(".kpa")) {
21
- return null;
22
- }
23
-
24
- const trimmed = code.trim();
25
-
26
- if (!trimmed.startsWith("{") || trimmed.startsWith("export default")) {
27
- return null;
28
- }
29
-
30
- return {
31
- code: `export default ${trimmed};`,
32
- map: null,
33
- };
34
- },
35
- };
36
- }
37
-
38
4
  export default defineConfig({
39
5
  plugins: [
40
6
  koppaPlugin({
41
7
  tsconfigFile: "./tsconfig.json",
42
8
  }),
43
- normalizeKpaModuleExport(),
44
9
  ],
45
10
  });
@@ -12,7 +12,13 @@ not every internal refactor.
12
12
 
13
13
  ## [Unreleased]
14
14
 
15
- _No unreleased changes yet._
15
+ ### Changed
16
+
17
+ - raised the minimum Node.js version to `>=22` and expanded CI coverage to
18
+ Node 24
19
+ - refreshed the router starter dependency baseline to the current
20
+ `@koppajs/koppajs-core`, `@koppajs/koppajs-vite-plugin`, and
21
+ `@koppajs/koppajs-router` releases
16
22
 
17
23
  ---
18
24
 
@@ -85,12 +85,9 @@ together.
85
85
 
86
86
  ## Requirements
87
87
 
88
- - Node.js 20.19+, 22.13+, or 24+
88
+ - Node.js >= 22
89
89
  - pnpm >= 10
90
90
 
91
- Node 23 is intentionally not treated as supported here because the current
92
- upstream frontend toolchain excludes it.
93
-
94
91
  ---
95
92
 
96
93
  ## Getting Started
@@ -17,7 +17,7 @@
17
17
  ],
18
18
  "packageManager": "pnpm@10.12.1",
19
19
  "engines": {
20
- "node": "^20.19.0 || ^22.13.0 || >=24.0.0",
20
+ "node": ">=22",
21
21
  "pnpm": ">=10"
22
22
  },
23
23
  "scripts": {
@@ -43,14 +43,14 @@
43
43
  "prepare": "husky"
44
44
  },
45
45
  "dependencies": {
46
- "@koppajs/koppajs-core": "^3.0.3",
47
- "@koppajs/koppajs-router": "^0.1.0"
46
+ "@koppajs/koppajs-core": "^3.0.7",
47
+ "@koppajs/koppajs-router": "^0.1.2"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@commitlint/cli": "^20.1.0",
51
51
  "@commitlint/config-conventional": "^20.0.0",
52
52
  "@eslint/js": "^10.0.1",
53
- "@koppajs/koppajs-vite-plugin": "^1.0.1",
53
+ "@koppajs/koppajs-vite-plugin": "^1.0.4",
54
54
  "@playwright/test": "^1.58.2",
55
55
  "@types/node": "^25.4.0",
56
56
  "@vitest/coverage-v8": "^4.0.18",
@@ -9,11 +9,11 @@ importers:
9
9
  .:
10
10
  dependencies:
11
11
  '@koppajs/koppajs-core':
12
- specifier: ^3.0.3
13
- version: 3.0.3
12
+ specifier: ^3.0.7
13
+ version: 3.0.7
14
14
  '@koppajs/koppajs-router':
15
- specifier: ^0.1.0
16
- version: 0.1.0
15
+ specifier: ^0.1.2
16
+ version: 0.1.2
17
17
  devDependencies:
18
18
  '@commitlint/cli':
19
19
  specifier: ^20.1.0
@@ -25,8 +25,8 @@ importers:
25
25
  specifier: ^10.0.1
26
26
  version: 10.0.1(eslint@10.0.3(jiti@2.6.1))
27
27
  '@koppajs/koppajs-vite-plugin':
28
- specifier: ^1.0.1
29
- version: 1.0.1(typescript@5.9.3)(vite@7.2.6(@types/node@25.4.0)(jiti@2.6.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))
28
+ specifier: ^1.0.4
29
+ version: 1.0.4(typescript@5.9.3)(vite@7.2.6(@types/node@25.4.0)(jiti@2.6.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))
30
30
  '@playwright/test':
31
31
  specifier: ^1.58.2
32
32
  version: 1.58.2
@@ -608,17 +608,17 @@ packages:
608
608
  '@jridgewell/trace-mapping@0.3.31':
609
609
  resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
610
610
 
611
- '@koppajs/koppajs-core@3.0.3':
612
- resolution: {integrity: sha512-l6e+M8hEugVW/4crOzddeImLM/0j+rqSJ5ZkFyBZPdv57Bp45WWdbmoK7HNKzPd+L70SehCaH7CcgRKM8ltbCQ==}
613
- engines: {node: '>=20', pnpm: '>=10.24.0'}
611
+ '@koppajs/koppajs-core@3.0.7':
612
+ resolution: {integrity: sha512-ZVaeU93skwDJZU+lWQaIlbExLFXJRC4+lg1Stoi0QUNg3iHUNEm1k8ESfTeF+GIJordpYdRKr1uytAHsT+EXLQ==}
613
+ engines: {node: '>=22', pnpm: '>=10.24.0'}
614
614
 
615
- '@koppajs/koppajs-router@0.1.0':
616
- resolution: {integrity: sha512-XJclUeB2Nwc/tDviuyFDSupFeOdI0NrkBKAoxwGdXE3vI6TN7AIoV6bfuiFnE8u4vLtVKhJ2Ds0IId7wlpF73Q==}
617
- engines: {node: '>=20', pnpm: '>=10.17.1'}
615
+ '@koppajs/koppajs-router@0.1.2':
616
+ resolution: {integrity: sha512-HJCvG4RDDlTG50UPiyodhtDRO/P2FDZ7c+U7FWyA/l5GtiYiJ02JjuY0ARjC6qCdiNwswNc6oHQGlmlYx3p7fQ==}
617
+ engines: {node: '>=22', pnpm: '>=10.17.1'}
618
618
 
619
- '@koppajs/koppajs-vite-plugin@1.0.1':
620
- resolution: {integrity: sha512-idjAD9zYQsK68yLxAI9So2bZScH7R4akDPsznHHivVCksMHSvkvQVtq328oeAg8rCi9AYN4z1Q0SAUB90aMDbg==}
621
- engines: {node: '>=20', pnpm: '>=10.24.0'}
619
+ '@koppajs/koppajs-vite-plugin@1.0.4':
620
+ resolution: {integrity: sha512-lBME5lq80t6Pk1lNDJeI4lOAfvfHR4AVqwpMLHUH5rqP9nbGh6t4ud/0llH4MiLNZAZK2HzNykzqO3+Rb+7tsw==}
621
+ engines: {node: '>=22', pnpm: '>=10.24.0'}
622
622
  peerDependencies:
623
623
  typescript: '>=5.5 <6'
624
624
  vite: ^7.0.0
@@ -2413,11 +2413,11 @@ snapshots:
2413
2413
  '@jridgewell/resolve-uri': 3.1.2
2414
2414
  '@jridgewell/sourcemap-codec': 1.5.5
2415
2415
 
2416
- '@koppajs/koppajs-core@3.0.3': {}
2416
+ '@koppajs/koppajs-core@3.0.7': {}
2417
2417
 
2418
- '@koppajs/koppajs-router@0.1.0': {}
2418
+ '@koppajs/koppajs-router@0.1.2': {}
2419
2419
 
2420
- '@koppajs/koppajs-vite-plugin@1.0.1(typescript@5.9.3)(vite@7.2.6(@types/node@25.4.0)(jiti@2.6.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))':
2420
+ '@koppajs/koppajs-vite-plugin@1.0.4(typescript@5.9.3)(vite@7.2.6(@types/node@25.4.0)(jiti@2.6.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))':
2421
2421
  dependencies:
2422
2422
  acorn: 8.16.0
2423
2423
  autoprefixer: 10.4.23(postcss@8.5.6)
@@ -1,24 +0,0 @@
1
- # ADR 0003: Normalize KPA plugin output
2
-
3
- ## Context
4
-
5
- The installed `@koppajs/koppajs-vite-plugin` currently transforms `.kpa` files into raw object literals during Vite loading. Rollup expects valid ES module syntax, so production builds fail unless the transformed output is wrapped in an export.
6
-
7
- ## Decision
8
-
9
- Keep using the upstream KoppaJS Vite plugin, but add a small repo-local post-transform in `vite.config.mjs` that converts raw `.kpa` object literals into `export default ...` modules.
10
-
11
- This workaround remains in place until the upstream plugin emits valid module syntax on its own.
12
-
13
- ## Consequences
14
-
15
- - The repository builds successfully without changing application source files.
16
- - The workaround is isolated to build configuration.
17
- - Future maintainers must remove or revise the wrapper if the upstream plugin behavior changes.
18
- - Architecture documentation must reflect that the build stack includes this compatibility layer.
19
-
20
- ## Alternatives considered
21
-
22
- - Patching `node_modules` directly
23
- - Rewriting application imports around the plugin defect
24
- - Leaving the repository in a state where `pnpm build` fails
@@ -1,46 +0,0 @@
1
- import type { Plugin } from "vite";
2
- import { describe, expect, it } from "vitest";
3
-
4
- import { normalizeKpaModuleExport } from "../../vite.config.mjs";
5
-
6
- async function transformWith(plugin: Plugin, code: string, id: string) {
7
- const transform = plugin.transform;
8
-
9
- if (!transform) {
10
- return null;
11
- }
12
-
13
- if (typeof transform === "function") {
14
- return transform.call({} as never, code, id);
15
- }
16
-
17
- return transform.handler.call({} as never, code, id);
18
- }
19
-
20
- describe("normalizeKpaModuleExport", () => {
21
- it("wraps raw KPA output in an ES module export", async () => {
22
- const plugin = normalizeKpaModuleExport();
23
-
24
- await expect(
25
- transformWith(plugin, '{ template: "<div></div>" }', "/src/app-view.kpa"),
26
- ).resolves.toEqual({
27
- code: 'export default { template: "<div></div>" };',
28
- map: null,
29
- });
30
- });
31
-
32
- it("ignores non-KPA files and already exported modules", async () => {
33
- const plugin = normalizeKpaModuleExport();
34
-
35
- await expect(
36
- transformWith(plugin, "const count = 0;", "/src/main.ts"),
37
- ).resolves.toBeNull();
38
- await expect(
39
- transformWith(
40
- plugin,
41
- "export default { template: '<div></div>' };",
42
- "/src/app-view.kpa",
43
- ),
44
- ).resolves.toBeNull();
45
- });
46
- });