@nativele/ts-dom 0.5.3 → 0.5.5
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 +46 -0
- package/README.md +3 -3
- package/README.zh-TW.md +3 -3
- package/dist/{a11y-Bw-8ZIAK.mjs → a11y-CiEJj_e6.mjs} +16 -14
- package/dist/cli.mjs +163 -25
- package/dist/{lint-qEPgmmMa.mjs → lint-Did4w-NL.mjs} +2 -2
- package/dist/oxlint-plugin.mjs +2 -2
- package/dist/vite-plugin.mjs +5 -2
- package/llms.txt +3 -1
- package/package.json +1 -1
- package/skills/README.md +33 -13
- package/skills/nativele-angular/SKILL.md +2 -4
- package/skills/nativele-architecture/SKILL.md +19 -9
- package/skills/nativele-core/SKILL.md +2 -2
- package/skills/nativele-integration/SKILL.md +7 -9
- package/skills/nativele-react/SKILL.md +11 -15
- package/skills/nativele-state-management/SKILL.md +4 -4
- package/skills/nativele-svelte/SKILL.md +9 -13
- package/skills/nativele-tailwind/SKILL.md +1 -1
- package/skills/nativele-tanstack-query/SKILL.md +1 -1
- package/skills/nativele-testing/SKILL.md +7 -7
- package/skills/nativele-vite/SKILL.md +6 -6
- package/skills/nativele-vue/SKILL.md +5 -5
- package/skills/nativele-zod/SKILL.md +1 -1
package/skills/README.md
CHANGED
|
@@ -19,10 +19,10 @@ skills/
|
|
|
19
19
|
│ └── SKILL.md
|
|
20
20
|
├── nativele-react/
|
|
21
21
|
│ ├── SKILL.md
|
|
22
|
-
│ └── example/ #
|
|
22
|
+
│ └── example/ # optional: a reference project from the agent trial
|
|
23
23
|
│ ├── package.json # exact versions of every tool the Skill names
|
|
24
24
|
│ ├── ... # the minimal app the SKILL.md example comes from
|
|
25
|
-
│ └── verify.mjs #
|
|
25
|
+
│ └── verify.mjs # browser checks run by `npm run verify` (or the project's own tests, see below)
|
|
26
26
|
└── ...
|
|
27
27
|
```
|
|
28
28
|
|
|
@@ -38,7 +38,7 @@ name: nativele-react
|
|
|
38
38
|
description: "Use Nativele web components inside a React 19 app: props, events, children and slots, lifecycle, and TSX types. Use when a project has react and @nativele/ts-dom."
|
|
39
39
|
metadata:
|
|
40
40
|
tier: official # official | community | experimental
|
|
41
|
-
nativele: ">=0.5.0 <0.6.0" # the library versions the Skill was verified with
|
|
41
|
+
nativele: ">=0.5.0 <0.6.0" # the library versions the Skill supports; the lower bound is the first one it was verified with
|
|
42
42
|
verified: "2026-11-05" # date of the last verification
|
|
43
43
|
tools: "react@19.3.1 react-dom@19.3.1 vite@8.3.5 @vitejs/plugin-react@6.2.1 typescript@7.0.2"
|
|
44
44
|
author: "Nativele maintainers" # for community Skills: the contributor
|
|
@@ -51,7 +51,7 @@ Quote the `description` and any value that contains `: `, so the frontmatter sta
|
|
|
51
51
|
Required sections, in this order:
|
|
52
52
|
|
|
53
53
|
1. **When to use**: the situation and the signals (dependencies, files) that call for this Skill, and when not to use it.
|
|
54
|
-
2. **Versions**: a table of every tool and the exact version it was verified with, and the date. If the project uses a different major version of a tool, the Skill says so and stops treating its steps as verified.
|
|
54
|
+
2. **Versions**: a table of every tool and the exact version it was verified with, and the date. The `@nativele/ts-dom` row names the version of the Skill's last agent trial. If the project uses a different major version of a tool, the Skill says so and stops treating its steps as verified.
|
|
55
55
|
3. **Set-up**: numbered steps with the exact commands and file contents, starting from a working project of the tool (for an integration Skill) and covering only what Nativele adds or changes.
|
|
56
56
|
4. **Example**: a complete, minimal working example (every file it needs, short).
|
|
57
57
|
5. **Known limitations**: what does not work or needs care, each with what to do instead. An agent must never have to guess these.
|
|
@@ -70,12 +70,12 @@ Rules for the content:
|
|
|
70
70
|
|
|
71
71
|
| Tier | Who maintains it | Requirements | Shown by `ts-dom skills` |
|
|
72
72
|
|---|---|---|---|
|
|
73
|
-
| Official | the Nativele maintainers | accepted through an agent trial
|
|
73
|
+
| Official | the Nativele maintainers | accepted through an agent trial and maintained by the maintainers; verified within the last 6 months | as official |
|
|
74
74
|
| Community | its author | the full SKILL.md format, `author` and `verified` filled in, accepted through a maintainer's agent trial | as community, with the author |
|
|
75
75
|
| Experimental | its author | the SKILL.md format; verification not complete | as experimental, with a warning |
|
|
76
76
|
|
|
77
|
-
- A new contribution starts as Experimental. It becomes Community when it passes a maintainer's agent trial (see "How a Skill pull request is accepted"), and Official when the maintainers adopt
|
|
78
|
-
- A Skill whose `verified` date is older than 6 months, or whose `nativele` range does not include the installed version, is shown as stale. A stale Official Skill
|
|
77
|
+
- A new contribution starts as Experimental. It becomes Community when it passes a maintainer's agent trial (see "How a Skill pull request is accepted"), and Official when the maintainers adopt and maintain it.
|
|
78
|
+
- A Skill whose `verified` date is older than 6 months, or whose `nativele` range does not include the installed version, is shown as stale. A stale Official Skill is verified again with a new agent trial, or moved to Experimental until it is.
|
|
79
79
|
|
|
80
80
|
## Delivering Skills to projects
|
|
81
81
|
|
|
@@ -88,12 +88,11 @@ The library already lives in the user's `node_modules`, so its Skills travel wit
|
|
|
88
88
|
|
|
89
89
|
None of this adds a runtime dependency: listing and copying read the Markdown files with Node's file API, and the frontmatter is parsed with a small reader for the fields above.
|
|
90
90
|
|
|
91
|
-
##
|
|
91
|
+
## Examples
|
|
92
92
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
- The monthly run opens nothing by itself; a failure is reported in the workflow run, and the Skill is fixed or moved to Experimental.
|
|
93
|
+
A Skill may keep the project from its agent trial in `skills/<name>/example/` as a reference: the files the Skill's example comes from, with exact versions and `"@nativele/ts-dom": "file:../../.."`. An example is not run in CI; the Skill is verified by its agent trials. The npm package does not ship the `example/` folders.
|
|
94
|
+
|
|
95
|
+
The workflow `.github/workflows/skills.yml` has one required check for `main`, "SKILL.md format": it checks every `SKILL.md` against this specification, and the links in the READMEs, `llms.txt` and the guides, whenever any of them changes.
|
|
97
96
|
|
|
98
97
|
## Contributing a Skill
|
|
99
98
|
|
|
@@ -117,7 +116,28 @@ The Skill is accepted when:
|
|
|
117
116
|
- the agent followed the Skill's steps as written, and no step was wrong for the stated versions;
|
|
118
117
|
- every guess, outside search or unexplained error in the report is already covered by the Skill; a guess about the tool itself (how to configure the framework, the build tool or the test runner beyond what Nativele needs) is not a gap in the Skill.
|
|
119
118
|
|
|
120
|
-
Otherwise the maintainers send the gaps back to the contributor as review comments (a missing step, a limitation to list, a Troubleshooting row to add), and the trial runs again, with a new agent, on the updated Skill.
|
|
119
|
+
Otherwise the maintainers send the gaps back to the contributor as review comments (a missing step, a limitation to list, a Troubleshooting row to add), and the trial runs again, with a new agent, on the updated Skill. Each trial is recorded on the PR as a review comment in this form, so a reader can see which agent passed which task, what it ran into, and whether a later change to the Skill needs a new trial:
|
|
120
|
+
|
|
121
|
+
```yaml
|
|
122
|
+
trial:
|
|
123
|
+
skill: nativele-vue
|
|
124
|
+
round: 2 # 1 for the first trial; a new agent each round
|
|
125
|
+
date: 2026-11-05
|
|
126
|
+
nativele: 0.5.4 # the packed version the agent installed
|
|
127
|
+
tools: vue@3.5.44 vite@8.3.6 vue-tsc@3.3.14 typescript@6.0.3
|
|
128
|
+
agent: Claude Code # or Codex, Cursor, ...
|
|
129
|
+
model: <model id>
|
|
130
|
+
start: create-vite vue-ts, built once before Nativele was added
|
|
131
|
+
task: |
|
|
132
|
+
An inventory screen: an item component in a keyed list Vue sorts, a two-way bound input,
|
|
133
|
+
Light DOM and shadow slots with Vue-managed children, a store bound under <KeepAlive>.
|
|
134
|
+
verify: # the Skill's Verify commands, as the maintainer re-ran them
|
|
135
|
+
"npx ts-dom check src/nv": "ts-dom check: no issues"
|
|
136
|
+
"npm run build": "built"
|
|
137
|
+
browser: 22 checks passed, no console errors or Vue warnings
|
|
138
|
+
gaps: [] # each guess or error the Skill did not cover; empty to accept
|
|
139
|
+
result: accepted # or: gaps sent back
|
|
140
|
+
```
|
|
121
141
|
|
|
122
142
|
## Skills
|
|
123
143
|
|
|
@@ -25,7 +25,7 @@ Nativele components are native custom elements. Angular renders their tags like
|
|
|
25
25
|
|
|
26
26
|
| Tool | Verified version |
|
|
27
27
|
|---|---|
|
|
28
|
-
| @nativele/ts-dom | 0.5.1 |
|
|
28
|
+
| @nativele/ts-dom | 0.5.1 (the last agent trial) |
|
|
29
29
|
| @angular/core, @angular/common, @angular/compiler, @angular/compiler-cli, @angular/platform-browser, @angular/router | 22.2.4 |
|
|
30
30
|
| @angular/cli, @angular/build | 22.2.6 |
|
|
31
31
|
| typescript | 6.0.3 |
|
|
@@ -274,9 +274,7 @@ export class App {
|
|
|
274
274
|
1. **`CUSTOM_ELEMENTS_SCHEMA` turns off binding checks for unknown elements.** With it, Angular's compiler accepts any property on any hyphenated tag: `[item]="42"`, `[count]="'x'"` and `[nope]="1"` on `<info-card>` all build without an error. Angular has no mechanism to type-check bindings on custom elements, so keep the values typed in the Angular class (`signal<Item>(...)`) and check the Nativele side with `ts-dom check src/nv`.
|
|
275
275
|
2. **`$event` is typed `Event`.** Angular's template type checker types `$event` of a DOM event it does not know as `Event`, so `select($event.detail)` or a handler parameter typed `CustomEvent<...>` fails with `TS2339: Property 'detail' does not exist on type 'Event'` or `TS2345: Argument of type 'Event' is not assignable to parameter of type 'CustomEvent<...>'`. Take `Event` and cast it with `EmitEvent<Component, 'event-name'>` from `@nativele/ts-dom`; a wrong event name there becomes `never` and fails with `Property 'detail' does not exist on type 'never'`. Nothing checks that the cast names the element the listener is on.
|
|
276
276
|
3. **Bind properties, not attributes, for objects.** `[attr.item]="item()"` writes the attribute `item="[object Object]"` and the prop stays `null`. `[attr.count]` works for a number prop (the attribute text is converted), but `[count]` is simpler.
|
|
277
|
-
4. **Light DOM slots and Angular-managed children do not mix
|
|
278
|
-
- `shadow: true` on the component; `@if` and `@for` children can then change freely;
|
|
279
|
-
- one stable element per slot that Angular never removes, with `@if`/`@for` inside it (the `light-card` above). A stable but empty wrapper still fills its slot, so the slot's fallback content does not show; when the fallback matters, use `shadow: true`.
|
|
277
|
+
4. **Light DOM slots and Angular-managed children do not mix** (https://github.com/Pixmi/nativele/blob/main/docs/components.md#slots-and-children-managed-by-a-framework). When `@if` or `@for` sit directly inside a Light DOM host with slots, Angular still adds and removes nodes, but a node it re-inserts lands in the wrong place: a `<span slot="title">` brought back by `@if` ends up in the default slot while the title shows its fallback, and `assignedNodes()` stops matching the page. Angular reports no error. Use `shadow: true`, or one stable element per slot that Angular never removes, with `@if`/`@for` inside it (the `light-card` above).
|
|
280
278
|
5. **Zoneless rendering is scheduled.** In the CLI's default zoneless app, Angular updates the DOM after a click in a later task, so code that calls `button.click()` and reads the DOM right away sees the old values; with `zone.js` the DOM is already updated when `click()` returns. A test that reads the DOM right after dispatching an event must wait (Playwright: wait for the expected text; TestBed: `await fixture.whenStable()`). The Nativele component itself renders synchronously as soon as Angular sets the property.
|
|
281
279
|
6. **State changed outside Angular's listeners.** In a zoneless app, a plain field set from a Nativele store subscription (`session.subscribe(...)` in an Angular class) or a `setTimeout` does not re-render the Angular view; put the value in a signal. Nativele components that `bind` the store update themselves either way.
|
|
282
280
|
7. **`ng serve` started before the modules were generated stays broken.** It fails with `Could not resolve "./info-card.html.js"` and does not rebuild when `ts-dom gen` creates the files later. Use `npm start` (step 4 runs `gen` first), or restart `ng serve`, or save one of the component `.ts` files.
|
|
@@ -26,7 +26,7 @@ metadata:
|
|
|
26
26
|
|
|
27
27
|
| Tool | Verified version |
|
|
28
28
|
|---|---|
|
|
29
|
-
| @nativele/ts-dom | 0.5.3 |
|
|
29
|
+
| @nativele/ts-dom | 0.5.3 (the last agent trial) |
|
|
30
30
|
| vite | 8.3.6 |
|
|
31
31
|
| typescript | 7.0.2 |
|
|
32
32
|
| create-vite (`vanilla-ts` template, the starting point) | 9.3.4 |
|
|
@@ -35,9 +35,9 @@ metadata:
|
|
|
35
35
|
| @vitest/browser-playwright | 5.1.0 |
|
|
36
36
|
| playwright | 1.64.1 (drives the installed Chrome; no browser is downloaded) |
|
|
37
37
|
| @playwright/test | 1.64.1 |
|
|
38
|
-
| universal-router | 10.0.3 (checked by hand with the
|
|
38
|
+
| universal-router | 10.0.3 (checked by hand with the same tests as the Navigation API router, see step 5) |
|
|
39
39
|
|
|
40
|
-
Verified on 2026-11-05 with Node 24 and Google Chrome 148, with the example project in the Nativele repository (https://github.com/Pixmi/nativele/tree/main/skills/nativele-architecture/example; it is not in the npm package),
|
|
40
|
+
Verified on 2026-11-05 with Node 24 and Google Chrome 148, with the example project in the Nativele repository (https://github.com/Pixmi/nativele/tree/main/skills/nativele-architecture/example; it is not in the npm package), and by an agent trial: a fresh agent did real work with only the Skill, and the result was checked independently. The example is a reference; it is not run in CI. The router relies on the Navigation API and `URLPattern`, verified in Chrome only. With another major version of any tool above, treat every step as unverified.
|
|
41
41
|
|
|
42
42
|
## Set-up
|
|
43
43
|
|
|
@@ -139,7 +139,7 @@ Verified on 2026-11-05 with Node 24 and Google Chrome 148, with the example proj
|
|
|
139
139
|
}
|
|
140
140
|
```
|
|
141
141
|
|
|
142
|
-
`router.resolve()` returns `undefined` when an action returns nothing, so test the match with `!= null`. With this router, the
|
|
142
|
+
`router.resolve()` returns `undefined` when an action returns nothing, so test the match with `!= null`. With this router, the same unit tests and end-to-end tests pass except the one for a browser without the Navigation API (links are always handled by the click handler) and the one that calls `navigation.navigate()`, which passes when it clicks a link instead; its browser router tests use the Navigation API and do not apply.
|
|
143
143
|
|
|
144
144
|
6. **Write each page as a component** in its feature folder: route params arrive as string props (`props: { projectId: "" }`), the page shows what an unknown id means, and it binds its stores in `connected()`. Everything a page must keep after the user leaves goes into a store: leaving removes the page, which ends its `bind` subscriptions, and coming back creates a new element.
|
|
145
145
|
|
|
@@ -167,11 +167,11 @@ Verified on 2026-11-05 with Node 24 and Google Chrome 148, with the example proj
|
|
|
167
167
|
npm pkg set scripts.verify="npm run build && ts-dom doctor src && npm test && npm run test:browser && npm run test:e2e"
|
|
168
168
|
```
|
|
169
169
|
|
|
170
|
-
Add `test-results` and `playwright-report` to `.gitignore`, next to `*.html.ts` and `ts-dom-tags.ts`.
|
|
170
|
+
The build script is for a project whose type check is plain `tsc`: `ts-dom check` generates the files and type-checks. In a framework project, keep the framework's type check (`tsc -b`, `vue-tsc -b`, `svelte-check`) and put `ts-dom gen src &&` in front of it instead (the framework's Skill shows the script). Add `test-results` and `playwright-report` to `.gitignore`, next to `*.html.ts` and `ts-dom-tags.ts`.
|
|
171
171
|
|
|
172
172
|
## Example
|
|
173
173
|
|
|
174
|
-
|
|
174
|
+
A project tracker: a projects list (`/`), a project page with its tasks (`/projects/:id`) and a settings page (`/settings`). Everything below is enough to build the same layout; the full project from the agent trial is in the repository only as a reference.
|
|
175
175
|
|
|
176
176
|
```
|
|
177
177
|
index.html package.json tsconfig.json vite.config.ts playwright.config.ts
|
|
@@ -183,6 +183,16 @@ src/features/settings/settings-page.* settings-page.test.ts
|
|
|
183
183
|
e2e/app.spec.ts
|
|
184
184
|
```
|
|
185
185
|
|
|
186
|
+
The small components the code below uses, by their interface (write them like any component, with the core Skill):
|
|
187
|
+
|
|
188
|
+
| Component | Folder | Props | Events |
|
|
189
|
+
|---|---|---|---|
|
|
190
|
+
| `text-entry` | `src/shared/` | `label`, `action` (strings) | `entry-submit` with `{ value: string }` |
|
|
191
|
+
| `progress-meter` | `src/shared/` | `done`, `total` (numbers) | none |
|
|
192
|
+
| `task-row` | `src/features/projects/` | `task` (a `Task`) | `task-toggle`, no data |
|
|
193
|
+
| `route-message` | `src/app/` | `heading`, `detail` (strings) | none |
|
|
194
|
+
| `app-nav` | `src/app/` | none; it binds `currentPath` and `preferences` to mark the current link and show the user name | none |
|
|
195
|
+
|
|
186
196
|
The router (Navigation API and `URLPattern`, no dependency):
|
|
187
197
|
|
|
188
198
|
```ts
|
|
@@ -264,7 +274,7 @@ export const currentPath = createStore(location.pathname);
|
|
|
264
274
|
A feature store and a page of that feature:
|
|
265
275
|
|
|
266
276
|
```ts
|
|
267
|
-
// src/features/projects/projects-store.ts
|
|
277
|
+
// src/features/projects/projects-store.ts
|
|
268
278
|
import { createStore } from "@nativele/ts-dom";
|
|
269
279
|
|
|
270
280
|
export interface Project { id: string; name: string }
|
|
@@ -476,7 +486,7 @@ test("each page module is loaded only when its route is first shown", async ({ p
|
|
|
476
486
|
|
|
477
487
|
1. **Scan one folder.** Several folders work too (`dirs: ["src/shared", "src/features"]`): a template imports a child defined in any scanned folder, and a tag defined in two of them is `duplicate-define`. But every folder then needs to be named in `tsDom({ dirs })`, `ts-dom check`, `ts-dom doctor` and the tsconfig `include` alike, and a folder left out of one of them is silently not generated or not checked there. One folder, `src`, avoids that. A component outside every scanned folder is not imported automatically: import its module where it is used.
|
|
478
488
|
2. **Browser support.** The router needs `URLPattern`: without it the app stops at start-up with `URLPattern is not defined`. Without the Navigation API, `startRouter` attaches nothing, every link loads a new document, and the router renders the route at start-up (verified in Chrome with `navigation` removed). Only Chrome 148 was verified; check your target browsers, or use a History API router (step 5).
|
|
479
|
-
3. **Keep the abort check.** Without `if (signal?.aborted) return;` after the import, a page module that loads slowly mounts over the page of a later navigation, and the address bar and the page disagree.
|
|
489
|
+
3. **Keep the abort check.** Without `if (signal?.aborted) return;` after the import, a page module that loads slowly mounts over the page of a later navigation, and the address bar and the page disagree. To prove the check in a test, delay one page's chunk and navigate twice: the second page must stay.
|
|
480
490
|
4. **Every app path must return `index.html`.** Deep links and reloads (and every link, without the Navigation API) ask the server for the page's path. `vite dev` and `vite preview` answer with `index.html`; a production server must be set up the same way (not verified here).
|
|
481
491
|
5. **A page is a new element on every navigation.** Its `state` starts over, also on back and forward, and also between `/projects/p1` and `/projects/p2`. Keep what must survive in a store.
|
|
482
492
|
6. **Feature stores live as long as the document.** A store created in a lazily loaded module is created on the first visit to the feature and kept until a reload; reset it in tests with `store.set(seed())` in `beforeEach`.
|
|
@@ -495,7 +505,7 @@ npm run test:browser # expect: every src/**/*.browser.test.ts passes in he
|
|
|
495
505
|
npm run test:e2e # expect: every e2e spec passes against vite preview on the built app
|
|
496
506
|
```
|
|
497
507
|
|
|
498
|
-
|
|
508
|
+
From a clean checkout with nothing generated, the output of these commands for the project tracker (the `info` lines left out):
|
|
499
509
|
|
|
500
510
|
```
|
|
501
511
|
ts-dom check: no issues
|
|
@@ -23,10 +23,10 @@ A component is two files. The HTML holds structure and names (`{{ name }}`, `dat
|
|
|
23
23
|
|
|
24
24
|
| Tool | Verified version |
|
|
25
25
|
|---|---|
|
|
26
|
-
| @nativele/ts-dom | 0.5.0 |
|
|
26
|
+
| @nativele/ts-dom | 0.5.0 (the last agent trial) |
|
|
27
27
|
| typescript | 7.0.2 |
|
|
28
28
|
|
|
29
|
-
Verified on 2026-11-05 by the library's own CI
|
|
29
|
+
Verified on 2026-11-05 by agent trials: fresh agents built real components with only this Skill, and the results were checked independently. The library's own CI type-checks, lints and runs the examples in the repository in real Chrome.
|
|
30
30
|
|
|
31
31
|
## Set-up
|
|
32
32
|
|
|
@@ -16,7 +16,7 @@ metadata:
|
|
|
16
16
|
|
|
17
17
|
- The app is built with a UI framework and wants to use components written with Nativele (`@nativele/ts-dom`), and no Skill exists for that framework yet. React, Vue, Svelte and Angular have their own (`nativele-react`, `nativele-vue`, `nativele-svelte`, `nativele-angular`); `npx ts-dom skills` lists every Skill.
|
|
18
18
|
- It is a procedure for finding out how the framework treats custom elements and connecting it correctly. It does not cover setting the framework itself up: start from the app as it is, with its own type checker and build.
|
|
19
|
-
- A framework with its own file format type-checks it with its own tool (Vue: `vue-tsc`). `ts-dom check` runs `vue-tsc` instead of `tsc` when the project's `package.json` lists it; for other formats see Troubleshooting. A framework that uses TSX (Preact, Solid) is checked by plain `tsc`, and `ts-dom check` reports its errors too.
|
|
19
|
+
- A framework with its own file format type-checks it with its own tool (Vue: `vue-tsc`). `ts-dom check` runs `vue-tsc` instead of `tsc` when the project's `package.json` lists it and it resolves from the project folder; for other formats see Troubleshooting. A framework that uses TSX (Preact, Solid) is checked by plain `tsc`, and `ts-dom check` reports its errors too.
|
|
20
20
|
- Not for writing the Nativele components themselves: use `nativele-core` (`npx ts-dom skills add nativele-core`).
|
|
21
21
|
|
|
22
22
|
Nativele components are native custom elements. The framework renders their tags like any other tag; nothing wraps them. Every rule below follows from that.
|
|
@@ -25,13 +25,13 @@ Nativele components are native custom elements. The framework renders their tags
|
|
|
25
25
|
|
|
26
26
|
| Tool | Verified version |
|
|
27
27
|
|---|---|
|
|
28
|
-
| @nativele/ts-dom | 0.5.1 |
|
|
28
|
+
| @nativele/ts-dom | 0.5.1 (the last agent trial) |
|
|
29
29
|
| preact | 11.1.1 |
|
|
30
30
|
| vite | 8.3.6 |
|
|
31
31
|
| @preact/preset-vite | 2.10.6 |
|
|
32
32
|
| typescript | 7.0.2 |
|
|
33
33
|
|
|
34
|
-
Verified on 2026-11-05 in Google Chrome 148, starting from `create-vite` 9.3.4 with the `preact-ts` template, with the example project in the Nativele repository (`skills/nativele-integration/example`),
|
|
34
|
+
Verified on 2026-11-05 in Google Chrome 148, starting from `create-vite` 9.3.4 with the `preact-ts` template, with the example project in the Nativele repository (`skills/nativele-integration/example`), and by an agent trial: a fresh agent did real work with only the Skill, and the result was checked independently. The example is a reference; it is not run in CI. The procedure was also followed earlier in a Vue app made with the `vue-ts` template. The rules come from the custom element standard and hold for any framework, but each framework's own behavior (the answers to step 3) is verified only for Preact 11 here and for the frameworks that have their own Skill. With Preact 10, treat the Preact answers below as unverified.
|
|
35
35
|
|
|
36
36
|
## Set-up
|
|
37
37
|
|
|
@@ -56,8 +56,8 @@ Verified on 2026-11-05 in Google Chrome 148, starting from `create-vite` 9.3.4 w
|
|
|
56
56
|
|
|
57
57
|
Many frameworks decide between property and attribute by checking whether the element already has the property, which is only true after `define()` has run.
|
|
58
58
|
|
|
59
|
-
5. Give the framework's template type checker the Nativele tags. `ts-dom gen` writes `ts-dom-tags.ts`, which adds every exported component to `HTMLElementTagNameMap`;
|
|
60
|
-
6. The generated files are not committed, so on a clean checkout a build script that runs the type checker before the bundler (the `preact-ts` template's `"build": "tsc -b && vite build"`) fails with `TS2307: Cannot find module './x.html.js'`. Put `ts-dom gen src/nv &&` in front: `"build": "ts-dom gen src/nv && tsc -b && vite build"`. The dev server needs no change: the Vite plugin generates the files when it starts.
|
|
59
|
+
5. Give the framework's template type checker the Nativele tags. `ts-dom gen` writes `ts-dom-tags.ts`, which adds every exported component to `HTMLElementTagNameMap`; the element type members a declaration reads (`props` and `"~emits"`) are in https://github.com/Pixmi/nativele/blob/main/docs/components.md#element-types-for-framework-declarations. Build the framework's tag declaration from these, so new components need no edit; "In Preact (verified)" below has the one for Preact's JSX, `nativele-react` the one for React and `nativele-vue` the Vue one. Check how the framework names an event listener in its types: Preact and React type `item-select` as the prop `onitem-select`, Vue types `@item-select` as `onItemSelect`. If the framework has no typed templates, skip this step.
|
|
60
|
+
6. The generated files are not committed, so on a clean checkout a build script that runs the type checker before the bundler (the `preact-ts` template's `"build": "tsc -b && vite build"`) fails with `TS2307: Cannot find module './x.html.js'`. Put `ts-dom gen src/nv &&` in front: `"build": "ts-dom gen src/nv && tsc -b && vite build"`. Do the same in front of whatever type check the framework's template runs (`vue-tsc -b`, `svelte-check`). Only a project whose type check is plain `tsc` replaces it with `ts-dom check`, which generates and type-checks: `"build": "ts-dom check src/nv && vite build"`, as `nativele-vite` sets it up. The dev server needs no change: the Vite plugin generates the files when it starts.
|
|
61
61
|
|
|
62
62
|
## Example
|
|
63
63
|
|
|
@@ -138,9 +138,7 @@ A template ref is typed from the same map: `useRef<HTMLElementTagNameMap["nv-inf
|
|
|
138
138
|
|
|
139
139
|
1. **Define before render.** Import the component modules before the framework mounts (step 4). A framework that sees an undefined element may write an attribute instead of a property: in Preact, `item={item}` becomes the attribute `item="[object Object]"`, the component gets `null` after `define()`, and the attribute stays even after a later new object is set as a property. A property set before `define()` is kept: Nativele takes it over when the element is upgraded. So when a module really is loaded later, set object props through the framework's property syntax, or, where it has none (Preact), from a ref: `ref={(el) => { if (el) el.item = item; }}`, and leave `item={item}` out of that tag, or Preact also writes the attribute.
|
|
140
140
|
2. **Props.** A prop whose default is a string, number or boolean can also come from an attribute, with camelCase names in kebab-case (`maxLength` from `max-length`, a boolean `true` when the attribute is present). Arrays and objects can only be set as properties. Pass a new object when data changes: props are compared by reference.
|
|
141
|
-
3. **Light DOM slots and framework-managed children do not mix
|
|
142
|
-
- `shadow: true` on the component, so the browser assigns slots natively and the children can change freely;
|
|
143
|
-
- one stable element per slot that the framework never removes, with the changing content inside it (`<div slot="title">...</div>`, `<div>...</div>` for the default slot). A stable but empty wrapper still fills its slot, so the slot's fallback content does not show; when the fallback matters, use `shadow: true`.
|
|
141
|
+
3. **Light DOM slots and framework-managed children do not mix** (https://github.com/Pixmi/nativele/blob/main/docs/components.md#slots-and-children-managed-by-a-framework): a framework that inserts, removes or reorders the children of a Light DOM host with slots can fail or leave nodes in the wrong slot. In Preact, toggling or reordering such children throws `NotFoundError: Failed to execute 'moveBefore' on 'Element'` (or `'insertBefore' on 'Node'`) and the slot loses its children. Use `shadow: true`, or one stable element per slot that the framework never removes, with the changing content inside it.
|
|
144
142
|
4. **Events do not bubble.** `emit` dispatches a `CustomEvent` on the component that does not bubble, so the listener must be on that element, not on an ancestor. The event data is `event.detail`.
|
|
145
143
|
5. **Lifecycle.** `connected()` runs each time the element is inserted and `disconnected()` each time it is removed, and `bind` subscriptions end on removal and start again on insertion. A framework that moves an element (reordering a keyed list) with `insertBefore` runs both; one that moves it with `moveBefore` keeps it connected, so neither runs and its subscriptions continue. Preact 11 uses `moveBefore` where the browser has it (Chrome does) and `insertBefore` elsewhere. Either way the moved element keeps its props and the children in its Light DOM slots. So subscribe in `connected()`, and do not rely on a move running it.
|
|
146
144
|
6. **The framework's templates are checked by the framework's type checker**, with the declaration from step 5, not by the Nativele rules. How much it checks depends on the framework: with the Preact declaration, `tsc` reports a wrong prop type, a wrong object shape, a wrong use of `e.detail`, an unknown prop name, an event prop in the wrong case (`onItemSelect`) and an unknown tag, but not a hyphenated attribute name (`selected-item`), which TypeScript never checks in JSX; `vue-tsc` reports a wrong prop type or event detail, but not an unknown prop name.
|
|
@@ -170,7 +168,7 @@ In the browser, render a component with an object prop and an event listener, an
|
|
|
170
168
|
- the listener runs once per event;
|
|
171
169
|
- children that the framework toggles, reorders, adds and removes stay in the right slot, with no errors or framework warnings in the console (run this check once against the dev server too: production builds drop framework warnings). Read a slot with `el.assignedNodes(name)` (`""` is the default slot), which works in both modes; a Light DOM component has no `<slot>` elements once rendered. Frameworks leave comment and empty text nodes as anchors among the children, so compare elements, not all nodes.
|
|
172
170
|
|
|
173
|
-
|
|
171
|
+
A reference implementation of these checks is the [`verify.mjs`](https://github.com/Pixmi/nativele/blob/main/skills/nativele-integration/example/verify.mjs) of the trial's example project, which does them for Preact with Playwright, plus a component defined after render, keyed reordering of hosts with and without `moveBefore`, and the end of `bind` on removal.
|
|
174
172
|
|
|
175
173
|
## Troubleshooting
|
|
176
174
|
|
|
@@ -24,14 +24,14 @@ Nativele components are native custom elements. React renders them like any othe
|
|
|
24
24
|
|
|
25
25
|
| Tool | Verified version |
|
|
26
26
|
|---|---|
|
|
27
|
-
| @nativele/ts-dom | 0.5.1 |
|
|
27
|
+
| @nativele/ts-dom | 0.5.1 (the last agent trial) |
|
|
28
28
|
| react, react-dom | 19.3.1 |
|
|
29
29
|
| @types/react, @types/react-dom | 19.3.0 |
|
|
30
30
|
| vite | 8.3.5 |
|
|
31
31
|
| @vitejs/plugin-react | 6.2.1 |
|
|
32
32
|
| typescript | 7.0.2 |
|
|
33
33
|
|
|
34
|
-
Verified on 2026-11-05 in Google Chrome 148 with the example project in the Nativele repository (`skills/nativele-react/example`),
|
|
34
|
+
Verified on 2026-11-05 in Google Chrome 148 with the example project in the Nativele repository (`skills/nativele-react/example`), and by an agent trial: a fresh agent did real work with only the Skill, and the result was checked independently. The example is a reference; it is not run in CI. With a different major version of React, treat every step as unverified.
|
|
35
35
|
|
|
36
36
|
## Set-up
|
|
37
37
|
|
|
@@ -162,18 +162,14 @@ define("shadow-card", ShadowCard);
|
|
|
162
162
|
|
|
163
163
|
1. **Define before render.** Import every Nativele module before React first renders its tag (step 6). Otherwise props become attributes and object props are lost.
|
|
164
164
|
2. **Event prop names.** Only `on` plus the exact event name works (`onitem-select`); `onItemSelect` and `onItem-select` never fire. Nativele events do not bubble by default, so put the handler on the element that emits. A ref with `addEventListener` in `useEffect` also works (remove it in the cleanup).
|
|
165
|
-
3. **Light DOM slots and React children do not mix
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
<
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
</light-card>
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
A stable but empty wrapper still fills its slot, so the slot's fallback content does not show; when the fallback matters, use `shadow: true`.
|
|
165
|
+
3. **Light DOM slots and React children do not mix** (https://github.com/Pixmi/nativele/blob/main/docs/components.md#slots-and-children-managed-by-a-framework). When React inserts, removes or reorders the children of a Light DOM host with slots, it fails with `insertBefore`/`removeChild` errors that unmount the root, or leaves nodes outside the slot. Changing text in place and appending at the end happen to work, but do not rely on that. Use `shadow: true`, or one stable React element per slot and let React change only what is inside it:
|
|
166
|
+
|
|
167
|
+
```tsx
|
|
168
|
+
<light-card>
|
|
169
|
+
<div slot="title">{showTitle && <span>{title}</span>}</div>
|
|
170
|
+
<div>{rows.map((r) => <p key={r.id}>{r.text}</p>)}</div>
|
|
171
|
+
</light-card>
|
|
172
|
+
```
|
|
177
173
|
4. **New objects for changed props.** A Nativele component compares props by reference; pass a new object when the data changes (React state updates usually do).
|
|
178
174
|
5. **TSX types need the declaration** in step 7; without it, every Nativele tag is `TS2339: Property 'info-card' does not exist on type 'JSX.IntrinsicElements'`. Check it with a `.tsx` that has a wrong prop under `// @ts-expect-error`, because `skipLibCheck` can hide mistakes inside the declaration itself. Keep these cases in a `.tsx` file of their own that the app never imports (the example uses `src/types-check.tsx`).
|
|
179
175
|
6. Not verified: server-side rendering, form controls inside Nativele components submitting with a React form, and React versions other than 19.3.
|
|
@@ -193,7 +189,7 @@ npm run build # expect: ✓ built (it runs ts-dom gen, tsc -b and
|
|
|
193
189
|
|
|
194
190
|
Pass `tsc` the tsconfig from step 3. A root `tsconfig.json` that only lists `references` (as in Vite's template) checks nothing itself, so `tsc --noEmit` without `-p` passes even with type errors.
|
|
195
191
|
|
|
196
|
-
In the browser, render a component with an object prop and an event handler, and check that the element has no `item` attribute (`el.getAttributeNames()`), that `el.item` is the object, and that the handler fires once per click.
|
|
192
|
+
In the browser, render a component with an object prop and an event handler, and check that the element has no `item` attribute (`el.getAttributeNames()`), that `el.item` is the object, and that the handler fires once per click. A reference implementation of these checks is the [`verify.mjs`](https://github.com/Pixmi/nativele/blob/main/skills/nativele-react/example/verify.mjs) of the trial's example project, which does them with Playwright, plus children that React reorders, adds and removes inside a `shadow: true` component and inside stable wrappers in a Light DOM component. A Light DOM component has no `<slot>` elements once rendered; check where the children ended up with `el.assignedNodes(name)`, where `""` is the default slot.
|
|
197
193
|
|
|
198
194
|
## Troubleshooting
|
|
199
195
|
|
|
@@ -24,16 +24,16 @@ metadata:
|
|
|
24
24
|
|
|
25
25
|
| Tool | Verified version |
|
|
26
26
|
|---|---|
|
|
27
|
-
| @nativele/ts-dom | 0.5.2 |
|
|
27
|
+
| @nativele/ts-dom | 0.5.2 (the last agent trial) |
|
|
28
28
|
| zustand (`zustand/vanilla`) | 5.0.16 |
|
|
29
29
|
| @reduxjs/toolkit | 2.14.0 |
|
|
30
|
-
| redux | 5.0.1 (installed by `@reduxjs/toolkit`; a `legacy_createStore` store with the adapter was checked by hand with `tsc` and Node
|
|
30
|
+
| redux | 5.0.1 (installed by `@reduxjs/toolkit`; a `legacy_createStore` store with the adapter was checked by hand with `tsc` and Node) |
|
|
31
31
|
| nanostores | 1.5.5 (`atom` only) |
|
|
32
32
|
| vite | 8.3.6 |
|
|
33
33
|
| typescript | 7.0.2 |
|
|
34
34
|
| create-vite (`vanilla-ts` template, the starting point) | 9.3.4 |
|
|
35
35
|
|
|
36
|
-
Verified on 2026-11-05 in Google Chrome 148 with the example project in the Nativele repository (`skills/nativele-state-management/example`),
|
|
36
|
+
Verified on 2026-11-05 in Google Chrome 148 with the example project in the Nativele repository (`skills/nativele-state-management/example`), and by an agent trial: a fresh agent did real work with only the Skill, and the result was checked independently. The example is a reference; it is not run in CI. With a different major version of a state library, treat the adapter and every limitation about that library as unverified.
|
|
37
37
|
|
|
38
38
|
## Set-up
|
|
39
39
|
|
|
@@ -294,7 +294,7 @@ for (const name of ["set", "render"]) {
|
|
|
294
294
|
}
|
|
295
295
|
```
|
|
296
296
|
|
|
297
|
-
Then typing a note must log nothing for `cart-badge`, and adding an item must log `set` and `render` once each.
|
|
297
|
+
Then typing a note must log nothing for `cart-badge`, and adding an item must log `set` and `render` once each. A reference implementation of these checks is the [`verify.mjs`](https://github.com/Pixmi/nativele/blob/main/skills/nativele-state-management/example/verify.mjs) of the trial's example project, which does them with Playwright for Zustand, Redux, nanostores and `createStore`, plus the `bind` contract itself (an immediate call, no call for an `Object.is`-equal set, nothing after removal), the nanostores double call, selectors that return a new object or a kept slice, and no console errors.
|
|
298
298
|
|
|
299
299
|
## Troubleshooting
|
|
300
300
|
|
|
@@ -25,7 +25,7 @@ Nativele components are native custom elements. Svelte renders them like any oth
|
|
|
25
25
|
|
|
26
26
|
| Tool | Verified version |
|
|
27
27
|
|---|---|
|
|
28
|
-
| @nativele/ts-dom | 0.5.1 |
|
|
28
|
+
| @nativele/ts-dom | 0.5.1 (the last agent trial) |
|
|
29
29
|
| svelte | 5.57.3 |
|
|
30
30
|
| @sveltejs/vite-plugin-svelte | 7.3.1 |
|
|
31
31
|
| svelte-check | 4.7.8 |
|
|
@@ -196,18 +196,14 @@ Used from Svelte:
|
|
|
196
196
|
|
|
197
197
|
1. **Define before mount.** Import the component modules before `mount` (step 6). Svelte 5 decides per value: when the element is defined, it sets a property the element has; when it is not defined yet (for example a module loaded later with `import()`), it sets objects and arrays as properties and writes every other value as an attribute string. Nativele reads both when the element is upgraded, which was verified for string, number and boolean props with one-word names (`count={0}` becomes `count="0"`, `false` becomes `open="false"`, both arrive with the right type). Other cases were not verified, so define first.
|
|
198
198
|
2. **Event attribute names.** Only `on` plus the exact event name works (`onitem-select`). `onItemSelect` listens to an event named `ItemSelect` and never fires; with the declaration it is also a `svelte-check` error. The legacy directive `on:item-select` fires too, but only use the event attributes: in a runes component `on:` is deprecated (compiler warning `event_directive_deprecated`), a component cannot mix it with event attributes (compile error `mixed_event_handler_syntaxes`), and the declaration in step 7 does not type it. Nativele events do not bubble, so put the handler on the element that emits.
|
|
199
|
-
3. **Light DOM slots and Svelte-managed children do not mix
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
<
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
</light-card>
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
A stable but empty wrapper still fills its slot, so the slot's fallback content does not show; when the fallback matters, use `shadow: true`.
|
|
199
|
+
3. **Light DOM slots and Svelte-managed children do not mix** (https://github.com/Pixmi/nativele/blob/main/docs/components.md#slots-and-children-managed-by-a-framework). Nativele moves the comment nodes Svelte uses as anchors into the slot positions together with the children. Svelte does not throw, but it goes wrong silently: an `{#if}` that adds `<span slot="title">` back inserts it next to its anchor, which now sits in the default slot, so the title slot shows its fallback; and `assignedNodes()` stops matching what is on the page. Keyed `{#each}` reordering happened to look right, but do not rely on it. Use `shadow: true`, or one stable element per slot and let Svelte change only what is inside it:
|
|
200
|
+
|
|
201
|
+
```svelte
|
|
202
|
+
<light-card>
|
|
203
|
+
<div slot="title">{#if showTitle}<span>Title</span>{/if}</div>
|
|
204
|
+
<div>{#each rows as r (r)}<p>{r}</p>{/each}</div>
|
|
205
|
+
</light-card>
|
|
206
|
+
```
|
|
211
207
|
4. **New objects for changed props.** A Nativele component compares props by reference. Changing a `$state` object in place (`item.name = "kiwi"`) does not re-render it; assign a new object (`item = { ...item, name: "kiwi" }`).
|
|
212
208
|
5. **Write closing tags.** `<info-card ... />` works, but the Svelte compiler warns `element_invalid_self_closing_tag` (shown in the dev server's terminal); write `<info-card ...></info-card>`.
|
|
213
209
|
6. **Template types need the declaration** in step 7. Without it, Svelte accepts any attribute on a hyphenated tag, and an inline handler's parameter is `any` (`Parameter 'e' implicitly has an 'any' type` under `strict`). With it, `svelte-check` reports a wrong prop type, an unknown prop name, a wrong `e.detail` field and a camelCase handler name. `<!-- @ts-expect-error -->` in markup does not silence an error, so keep the expected-error cases in a `.ts` file (step 8). To type an element reference, use the tag map: `let card: HTMLElementTagNameMap["info-card"] | undefined = $state()` with `bind:this={card}`.
|
|
@@ -25,7 +25,7 @@ The recommended path is Vitest: component tests in happy-dom, and the few behavi
|
|
|
25
25
|
|
|
26
26
|
| Tool | Verified version |
|
|
27
27
|
|---|---|
|
|
28
|
-
| @nativele/ts-dom | 0.5.2 |
|
|
28
|
+
| @nativele/ts-dom | 0.5.2 (the last agent trial) |
|
|
29
29
|
| vitest | 5.1.0 |
|
|
30
30
|
| happy-dom | 21.0.4 |
|
|
31
31
|
| jsdom | 30.1.3 (the same component tests pass; not the recommended environment) |
|
|
@@ -35,7 +35,7 @@ The recommended path is Vitest: component tests in happy-dom, and the few behavi
|
|
|
35
35
|
| vite | 8.3.6 |
|
|
36
36
|
| typescript | 7.0.2 |
|
|
37
37
|
|
|
38
|
-
Verified on 2026-11-05 with Node 24 and Google Chrome 148, starting from `create-vite` 9.3.4 with the `vanilla-ts` template, with the example project in the Nativele repository (`skills/nativele-testing/example`),
|
|
38
|
+
Verified on 2026-11-05 with Node 24 and Google Chrome 148, starting from `create-vite` 9.3.4 with the `vanilla-ts` template, with the example project in the Nativele repository (`skills/nativele-testing/example`), and by an agent trial: a fresh agent did real work with only the Skill, and the result was checked independently. The example is a reference; it is not run in CI. Vitest 5 changed its configuration (`test.api`, `test.projects`, providers as separate packages); with Vitest 4 or older, or another major version of any tool above, treat every step as unverified.
|
|
39
39
|
|
|
40
40
|
## Set-up
|
|
41
41
|
|
|
@@ -69,7 +69,7 @@ Verified on 2026-11-05 with Node 24 and Google Chrome 148, starting from `create
|
|
|
69
69
|
npm pkg set scripts.test="vitest run --project unit"
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
`ts-dom check src/nv` is what reports type errors in the tests and the templates. In a project whose type check is plain `tsc`, the build script `ts-dom check src/nv && vite build` (as `nativele-vite` sets it up) runs it; in a framework project, whose build puts `ts-dom gen src/nv &&` in front of its own type check (`tsc -b`, `vue-tsc -b`, `svelte-check`), run `ts-dom check src/nv` before or after the tests.
|
|
73
73
|
|
|
74
74
|
5. For the real-browser tests, install the Vitest provider and Playwright, and add a second project. It drives the Chrome already installed on the machine (`CHROME_BIN`, or `/usr/bin/google-chrome`, which GitHub's `ubuntu-latest` runners have), so nothing is downloaded and `npx playwright install` is not needed:
|
|
75
75
|
|
|
@@ -129,7 +129,7 @@ Verified on 2026-11-05 with Node 24 and Google Chrome 148, starting from `create
|
|
|
129
129
|
|
|
130
130
|
## Example
|
|
131
131
|
|
|
132
|
-
The component under test (
|
|
132
|
+
The component under test (`src/nv/counter-card.*`):
|
|
133
133
|
|
|
134
134
|
```html
|
|
135
135
|
<!-- src/nv/counter-card.html -->
|
|
@@ -224,7 +224,7 @@ The steps every test follows:
|
|
|
224
224
|
4. Assert at once. Rendering is synchronous: after `append`, a prop assignment, `setAttribute`, `el.click()` or `store.set()`, the DOM is already updated, and `emit` listeners have already run. For a form, set the input's `value`, then call `click()` on its submit button or `form.requestSubmit()`: both run the template's `data-on-submit` handler, in happy-dom too. The only waits are for slot changes made after connection (one microtask, below).
|
|
225
225
|
5. Empty the document in `afterEach` (`document.body.replaceChildren()`): removing the elements runs `disconnected()` and ends their `bind`, `on` and `onCleanup` registrations.
|
|
226
226
|
|
|
227
|
-
Other parts of the API
|
|
227
|
+
Other parts of the API:
|
|
228
228
|
|
|
229
229
|
```ts
|
|
230
230
|
// lists (test/todo-list.test.ts): items as components; an item's event reaches the parent with hit.key
|
|
@@ -286,7 +286,7 @@ test("Chrome moves a list item with moveBefore: it stays connected and keeps foc
|
|
|
286
286
|
});
|
|
287
287
|
```
|
|
288
288
|
|
|
289
|
-
Under happy-dom this test fails: there is no `moveBefore`, both callbacks run and the input loses focus.
|
|
289
|
+
Under happy-dom this test fails: there is no `moveBefore`, both callbacks run and the input loses focus. A unit test can assert the happy-dom behavior, so that a later happy-dom release that adds `moveBefore` shows up as a failing test. The Vitest API in browser mode is the same: happy-dom tests also pass unchanged when run in the browser project, so moving a test there is a matter of renaming it.
|
|
290
290
|
|
|
291
291
|
A page test with Playwright (the alternative):
|
|
292
292
|
|
|
@@ -336,7 +336,7 @@ npx vitest run --project browser # expect: the *.browser.test.ts files pass in
|
|
|
336
336
|
npx playwright test # if you use the alternative: builds, serves on port 4432, passes
|
|
337
337
|
```
|
|
338
338
|
|
|
339
|
-
The
|
|
339
|
+
The same unit tests also pass under jsdom, as a third Vitest project with `"environment": "jsdom"`. Each limitation above was proved by a test in the agent trial: the module import, per-file isolation, `define()` twice, `vi.resetModules()`, slot timing in both modes, and the move that happy-dom and Chrome handle differently.
|
|
340
340
|
|
|
341
341
|
## Troubleshooting
|
|
342
342
|
|
|
@@ -17,20 +17,20 @@ metadata:
|
|
|
17
17
|
- A Vite app with no UI framework (for example one started with `npm create vite@latest -- --template vanilla-ts`) that adds components written with Nativele (`@nativele/ts-dom`).
|
|
18
18
|
- Any Vite project with Nativele whose set-up goes beyond the defaults: `root` in a sub-folder, several HTML pages, Vite+, or questions about what `vite dev` does when a template changes.
|
|
19
19
|
- Signals: `vite` or `vite-plus` and `@nativele/ts-dom` in `package.json`, a `vite.config.ts`.
|
|
20
|
-
- With
|
|
20
|
+
- With a UI framework, follow its Skill: `nativele-react`, `nativele-vue`, `nativele-svelte`, `nativele-angular`, or `nativele-integration` for any other. The parts about `dirs` and `root` apply there too; the build script in step 5 does not: it is for a project whose type check is plain `tsc`, while a framework project keeps its own type check (`tsc -b`, `vue-tsc -b`, `svelte-check`) and puts `ts-dom gen <folder> &&` in front of that script. The hot-update behavior below was verified without a framework.
|
|
21
21
|
- Not for writing the components themselves (`npx ts-dom skills add nativele-core`), and not for setting Vite up.
|
|
22
22
|
|
|
23
23
|
## Versions
|
|
24
24
|
|
|
25
25
|
| Tool | Verified version |
|
|
26
26
|
|---|---|
|
|
27
|
-
| @nativele/ts-dom | 0.5.1 |
|
|
27
|
+
| @nativele/ts-dom | 0.5.1 (the last agent trial) |
|
|
28
28
|
| vite | 8.3.6 |
|
|
29
29
|
| typescript | 7.0.2 |
|
|
30
30
|
| create-vite (`vanilla-ts` template, the starting point) | 9.3.4 |
|
|
31
|
-
| vite-plus | 1.5.0 (checked by hand with `vp dev`, `vp build` and `vp lint
|
|
31
|
+
| vite-plus | 1.5.0 (checked by hand with `vp dev`, `vp build` and `vp lint`) |
|
|
32
32
|
|
|
33
|
-
Verified on 2026-11-05 in Google Chrome 148 with the example project in the Nativele repository (`skills/nativele-vite/example`),
|
|
33
|
+
Verified on 2026-11-05 in Google Chrome 148 with the example project in the Nativele repository (`skills/nativele-vite/example`), and by an agent trial: a fresh agent did real work with only the Skill, and the result was checked independently. The example is a reference; it is not run in CI. With a different major version of Vite, treat every step as unverified.
|
|
34
34
|
|
|
35
35
|
## Set-up
|
|
36
36
|
|
|
@@ -75,7 +75,7 @@ Starting from the `vanilla-ts` template, which already has `"type": "module"`, a
|
|
|
75
75
|
|
|
76
76
|
## Example
|
|
77
77
|
|
|
78
|
-
A two-page app with `root: "web"
|
|
78
|
+
A two-page app with `root: "web"`:
|
|
79
79
|
|
|
80
80
|
```
|
|
81
81
|
package.json tsconfig.json vite.config.ts
|
|
@@ -173,7 +173,7 @@ npm run build # expect: ts-dom check: no issues, then ✓ built,
|
|
|
173
173
|
npx ts-dom doctor src/nv # expect: ts-dom doctor: the set-up looks right
|
|
174
174
|
```
|
|
175
175
|
|
|
176
|
-
Then run `npm run dev`, open every page and check that its components render and respond. Edit a template and check that the page reloads once and shows the change.
|
|
176
|
+
Then run `npm run dev`, open every page and check that its components render and respond. Edit a template and check that the page reloads once and shows the change. A reference implementation is the [`verify.mjs`](https://github.com/Pixmi/nativele/blob/main/skills/nativele-vite/example/verify.mjs) of the trial's example project, which checks with Playwright that `vite build` alone writes the generated files, that both built pages work under `vite preview`, and every row of the table in Known limitations, restoring the files it edits.
|
|
177
177
|
|
|
178
178
|
## Troubleshooting
|
|
179
179
|
|