@nativele/ts-dom 0.5.5 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/README.md +5 -0
  3. package/README.zh-TW.md +5 -0
  4. package/dist/{a11y-CiEJj_e6.mjs → a11y-KXbo-8o3.mjs} +119 -27
  5. package/dist/cli.mjs +273 -50
  6. package/dist/{lint-Did4w-NL.mjs → lint-DUsdDKZ-.mjs} +33 -8
  7. package/dist/oxlint-plugin.mjs +3 -3
  8. package/dist/vite-plugin.mjs +1 -1
  9. package/docs/architecture.md +188 -0
  10. package/docs/components.md +367 -0
  11. package/docs/contributing.md +80 -0
  12. package/docs/design.md +55 -0
  13. package/docs/lists.md +150 -0
  14. package/docs/performance.md +24 -0
  15. package/docs/recipes.md +189 -0
  16. package/docs/templates.md +73 -0
  17. package/docs/tooling.md +251 -0
  18. package/llms.txt +5 -5
  19. package/package.json +2 -1
  20. package/skills/README.md +19 -2
  21. package/skills/nativele-angular/SKILL.md +15 -11
  22. package/skills/nativele-architecture/SKILL.md +14 -11
  23. package/skills/nativele-ci/SKILL.md +148 -0
  24. package/skills/nativele-core/SKILL.md +16 -13
  25. package/skills/nativele-formatting/SKILL.md +167 -0
  26. package/skills/nativele-integration/SKILL.md +11 -8
  27. package/skills/nativele-linting/SKILL.md +174 -0
  28. package/skills/nativele-monorepo/SKILL.md +298 -0
  29. package/skills/nativele-react/SKILL.md +12 -9
  30. package/skills/nativele-state-management/SKILL.md +4 -4
  31. package/skills/nativele-svelte/SKILL.md +10 -8
  32. package/skills/nativele-tailwind/SKILL.md +8 -7
  33. package/skills/nativele-tanstack-query/SKILL.md +5 -5
  34. package/skills/nativele-testing/SKILL.md +19 -13
  35. package/skills/nativele-typescript/SKILL.md +186 -0
  36. package/skills/nativele-vite/SKILL.md +18 -14
  37. package/skills/nativele-vue/SKILL.md +9 -7
  38. package/skills/nativele-zod/SKILL.md +6 -6
@@ -3,8 +3,8 @@ name: nativele-svelte
3
3
  description: "Use Nativele web components inside an existing Svelte 5 app: passing props, listening to events, children and slots, lifecycle, template types with svelte-check, and ts-dom check. Use when a project depends on svelte and @nativele/ts-dom."
4
4
  metadata:
5
5
  tier: experimental
6
- nativele: ">=0.5.1 <0.6.0"
7
- verified: "2026-11-05"
6
+ nativele: ">=0.6.0 <0.7.0"
7
+ verified: "2026-11-06"
8
8
  tools: "svelte@5.57.3 vite@8.3.6 @sveltejs/vite-plugin-svelte@7.3.1 svelte-check@4.7.8 @tsconfig/svelte@5.0.8 typescript@6.0.3"
9
9
  author: "Nativele maintainers"
10
10
  detects: "svelte"
@@ -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 (the last agent trial) |
28
+ | @nativele/ts-dom | 0.6.0 (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 |
@@ -33,13 +33,13 @@ Nativele components are native custom elements. Svelte renders them like any oth
33
33
  | vite | 8.3.6 |
34
34
  | typescript | 6.0.3 |
35
35
 
36
- Verified on 2026-11-05 in Google Chrome 148, against the Vite dev server (so Svelte's development warnings would show), in a fresh app made with `npm create vite@latest -- --template svelte-ts`. Runes mode (`$state`) is what was verified; legacy mode was checked only for the `on:` directive (limitation 2). With a different major version of Svelte, treat every step as unverified.
36
+ Verified on 2026-11-06 in Google Chrome 148, against the Vite dev server (so Svelte's development warnings would show), in a fresh app made with `npm create vite@latest -- --template svelte-ts`. Runes mode (`$state`) is what was verified; legacy mode was checked only for the `on:` directive (limitation 2). With a different major version of Svelte, treat every step as unverified.
37
37
 
38
38
  ## Set-up
39
39
 
40
40
  1. Keep the Nativele components in their own folder, for example `src/nv/`, each as `x.html` plus `x.ts`. Write them with the core Skill: `npx ts-dom skills add nativele-core`.
41
41
  2. Install: `npm i @nativele/ts-dom`.
42
- 3. Leave the tsconfig as it is: Vite's Svelte template has `tsconfig.app.json` extend `@tsconfig/svelte`, which sets `"strict": true`, and its `include` already covers `src/nv`. If the project turned `strict` off, set it back in that file.
42
+ 3. The tsconfig that covers that folder must meet Nativele's requirements: in Vite's Svelte template, `tsconfig.app.json` extends `@tsconfig/svelte`, which sets `"strict": true`, and its `include` already covers `src/nv`, so nothing changes. In any other case, check and adjust it with the TypeScript Skill (`npx ts-dom skills nativele-typescript`).
43
43
  4. Add the Nativele Vite plugin before the Svelte plugin, pointing at that folder:
44
44
 
45
45
  ```ts
@@ -109,7 +109,7 @@ Verified on 2026-11-05 in Google Chrome 148, against the Vite dev server (so Sve
109
109
  export const badEvent: SvelteHTMLElements["info-card"] = { "onitem-select": (e) => e.detail.name };
110
110
  ```
111
111
 
112
- The generated files are not committed, so on a clean checkout the type check in `npm run check` runs before anything has written them and fails with `TS2307: Cannot find module './x.html.js'`. Generate them first: `"check": "ts-dom gen src/nv && svelte-check --tsconfig ./tsconfig.app.json && tsc -p tsconfig.node.json"` (the template's script with `ts-dom gen src/nv &&` in front). `npm run build` needs no change: the Vite plugin generates the files.
112
+ The generated files are not committed, so on a clean checkout the type check in `npm run check` runs before anything has written them and fails with `TS2307: Cannot find module './x.html.js'` (why: `nativele-typescript`). Generate them first: `"check": "ts-dom gen src/nv && svelte-check --tsconfig ./tsconfig.app.json && tsc -p tsconfig.node.json"` (the template's script with `ts-dom gen src/nv &&` in front). `npm run build` needs no change: the Vite plugin generates the files. CI starts from such a checkout; the rest of what its job needs is in `npx ts-dom skills nativele-ci`.
113
113
 
114
114
  ## Example
115
115
 
@@ -222,7 +222,7 @@ npm run check # svelte-check plus tsc for vite.config.ts; expect:
222
222
  npm run build # expect: built
223
223
  ```
224
224
 
225
- When `npm run check` is not run in a terminal (an agent, CI), `svelte-check` prints `COMPLETED ... 0 ERRORS 0 WARNINGS ...` instead. Delete `src/nativele-svelte.d.ts` once to see the check work: `svelte-check` must then report `Unused '@ts-expect-error' directive` in `src/types-check.ts`.
225
+ When `npm run check` is not run in a terminal (an agent, CI; the CI steps are in `nativele-ci`), `svelte-check` prints `COMPLETED ... 0 ERRORS 0 WARNINGS ...` instead. Delete `src/nativele-svelte.d.ts` once to see the check work: `svelte-check` must then report `Unused '@ts-expect-error' directive` in `src/types-check.ts`.
226
226
 
227
227
  In the browser, against the dev server (production builds drop Svelte's warnings), render a component with an object prop and an event attribute, and check:
228
228
 
@@ -234,7 +234,7 @@ In the browser, against the dev server (production builds drop Svelte's warnings
234
234
 
235
235
  | Symptom | Cause | Fix |
236
236
  |---|---|---|
237
- | `svelte-check` reports `Cannot find module './x.html.js'` on a clean checkout | it runs before the generated files exist | start the `check` script with `ts-dom gen src/nv &&` |
237
+ | `svelte-check` reports `Cannot find module './x.html.js'` on a clean checkout | it runs before the generated files exist | start the `check` script with `ts-dom gen src/nv &&`; for CI, `nativele-ci` |
238
238
  | The event handler never runs | the attribute is camelCased (`onItemSelect`), or it is on an ancestor | use `on` plus the exact event name (`onitem-select`) on the element that emits |
239
239
  | `svelte-check`: `'"on:item-select"' does not exist in type 'NativeleElementProps<...>'` | the legacy `on:` directive, which the declaration does not type | use the event attribute `onitem-select` |
240
240
  | `Mixing old (on:item-select) and new syntaxes for event handling is not allowed` | `on:` and event attributes in the same component | use only event attributes |
@@ -252,4 +252,6 @@ In the browser, against the dev server (production builds drop Svelte's warnings
252
252
  - Slots, Light DOM and Shadow DOM: https://github.com/Pixmi/nativele/blob/main/docs/components.md#light-default-and-shadow
253
253
  - Stores and `bind`: https://github.com/Pixmi/nativele/blob/main/docs/components.md#state-store-when-not-using-state
254
254
  - Setup and checks: https://github.com/Pixmi/nativele/blob/main/docs/tooling.md
255
+ - The tsconfig and `svelte-check`'s TypeScript version: https://github.com/Pixmi/nativele/blob/main/skills/nativele-typescript/SKILL.md
256
+ - CI: https://github.com/Pixmi/nativele/blob/main/skills/nativele-ci/SKILL.md
255
257
  - The framework-neutral rules: `npx ts-dom skills nativele-integration`
@@ -3,8 +3,8 @@ name: nativele-tailwind
3
3
  description: "Style Nativele components with Tailwind CSS v4 in a Vite app that already uses Tailwind: classes in .html templates and in render, which files Tailwind scans, dynamic class names, <style scoped> next to utilities, and bringing the Tailwind stylesheet into shadow: true components. Use when a project depends on tailwindcss and @nativele/ts-dom."
4
4
  metadata:
5
5
  tier: experimental
6
- nativele: ">=0.5.2 <0.6.0"
7
- verified: "2026-11-05"
6
+ nativele: ">=0.6.0 <0.7.0"
7
+ verified: "2026-11-06"
8
8
  tools: "tailwindcss@4.3.4 @tailwindcss/vite@4.3.4 vite@8.3.6 typescript@7.0.2 create-vite@9.3.4"
9
9
  author: "Nativele maintainers"
10
10
  detects: "tailwindcss"
@@ -23,14 +23,14 @@ metadata:
23
23
 
24
24
  | Tool | Verified version |
25
25
  |---|---|
26
- | @nativele/ts-dom | 0.5.2 (the last agent trial) |
26
+ | @nativele/ts-dom | 0.6.0 (the last agent trial) |
27
27
  | tailwindcss | 4.3.4 |
28
28
  | @tailwindcss/vite | 4.3.4 |
29
29
  | vite | 8.3.6 |
30
30
  | typescript | 7.0.2 |
31
31
  | create-vite (`vanilla-ts` template, the starting point) | 9.3.4 |
32
32
 
33
- Verified on 2026-11-05 in Google Chrome 148, by reading computed styles with Playwright both against `vite` (dev) and against `vite build` served by `vite preview`, in an app made with `npm create vite@latest -- --template vanilla-ts`, given Tailwind by Tailwind's Vite guide, then Nativele by `nativele-vite`, in a git repository. With a different major version of Tailwind, treat every step as unverified.
33
+ Verified on 2026-11-06 in Google Chrome 148, by reading computed styles with Playwright both against `vite` (dev) and against `vite build` served by `vite preview`, in an app made with `npm create vite@latest -- --template vanilla-ts`, given Tailwind by Tailwind's Vite guide, then Nativele by `nativele-vite`, in a git repository. With a different major version of Tailwind, treat every step as unverified.
34
34
 
35
35
  ## Set-up
36
36
 
@@ -48,7 +48,7 @@ The starting point: `src/style.css` is `@import "tailwindcss";`, `src/main.ts` i
48
48
 
49
49
  2. Keep `import "./style.css"` in the entry module. For Light DOM components (the default) nothing else is needed: Tailwind's automatic source detection reads the `.html` templates and the component `.ts` files, so a class written only in a template, only on a tag in `index.html`, or only as a full string in TypeScript (`classList.toggle("tracking-[0.3em]", on)` in `render`) is generated, in `vite dev` and in `vite build`. No `@source` is needed while the components are inside the project folder.
50
50
  3. The generated `*.html.ts` files do not need to be scanned. Tailwind skips every file matched by `.gitignore` (also outside a git repository), and with the generated files not ignored the built CSS was byte for byte the same. The Skills that `ts-dom skills add` copies into `.agents/` (or `.claude/`) are scanned, though, and their example classes end up in the build; exclude that folder under the import, relative to the CSS file: `@source not "../.agents";`.
51
- 4. Only when templates live outside the project folder (for example a sibling package of a monorepo), add a `@source` line, with the path relative to the CSS file:
51
+ 4. Only when templates live outside the project folder (for example a sibling package of a workspace), add a `@source` line, with the path relative to the CSS file:
52
52
 
53
53
  ```css
54
54
  /* src/style.css */
@@ -56,7 +56,7 @@ The starting point: `src/style.css` is `@import "tailwindcss";`, `src/main.ts` i
56
56
  @source "../../shared-nv";
57
57
  ```
58
58
 
59
- Nativele needs that folder too: list it in `tsDom({ dirs })`, in the arguments of `ts-dom check` and `ts-dom doctor`, and in the tsconfig `include`; keep each component's `.ts` next to its template there (a template whose importer is in another folder is reported as `no-importer`); and the folder must resolve `@nativele/ts-dom` itself, or `tsc` reports `TS2307` for its generated `.html.ts`.
59
+ For Nativele, a package of the workspace generates and checks its own components and the app imports it, as `npx ts-dom skills nativele-monorepo` sets up. A plain folder outside the project that the app scans itself goes into `tsDom({ dirs })`, the arguments of `ts-dom check` and `ts-dom doctor`, and the tsconfig (`nativele-typescript`); keep each component's `.ts` next to its template there (a template whose importer is in another folder is reported as `no-importer`), and the folder must resolve `@nativele/ts-dom` itself, or `tsc` reports `TS2307` for its generated `.html.ts`.
60
60
 
61
61
  5. Only for `shadow: true` components: add one module that turns the same CSS into a constructable stylesheet, and adopt it in each Shadow component's constructor (limitation 3 explains why and what it costs):
62
62
 
@@ -207,7 +207,7 @@ getComputedStyle(document.querySelector("badge-box").shadowRoot.firstElementChil
207
207
  | A Shadow component flashes unstyled on load | the stylesheet comes from a `<link>` | `adoptedStyleSheets` (step 5) |
208
208
  | A utility on an element does not override the template's `<style scoped>` rule | unlayered template styles beat `@layer utilities` | wrap the template rule in `@layer components { }`, or use the `!` modifier |
209
209
  | `@apply` in a template `<style>` has no effect | template styles are not processed by Tailwind | use theme variables (`var(--color-...)`) or utility classes in the markup |
210
- | Templates in another folder of a monorepo are not styled | they are outside the project folder Tailwind scans | `@source "<path relative to the CSS file>"` |
210
+ | Templates in another folder of a workspace are not styled | they are outside the project folder Tailwind scans | `@source "<path relative to the CSS file>"`; how the app gets the package's components is in `nativele-monorepo` |
211
211
  | The built CSS has classes no component uses, from the Skill files | Tailwind scans `.agents/` (or `.claude/`), where `ts-dom skills add` puts the Skills, unless `.gitignore` matches it | add `@source not "../.agents";` (relative to the CSS file) under `@import "tailwindcss";` |
212
212
  | A check that a class is absent from the build fails, although no component uses it | Tailwind scans every file in the project that `.gitignore` does not match, including test scripts that name the class | keep such scripts outside the project, or build the class name from pieces in them |
213
213
 
@@ -218,3 +218,4 @@ getComputedStyle(document.querySelector("badge-box").shadowRoot.firstElementChil
218
218
  - Common DOM operations in `render` (classes): https://github.com/Pixmi/nativele/blob/main/docs/components.md#common-dom-operations-in-render
219
219
  - The Vite plugin: https://github.com/Pixmi/nativele/blob/main/docs/tooling.md#with-vite-optional
220
220
  - Adding Nativele to Vite: `npx ts-dom skills nativele-vite`
221
+ - Components from another package of a workspace: https://github.com/Pixmi/nativele/blob/main/skills/nativele-monorepo/SKILL.md
@@ -3,8 +3,8 @@ name: nativele-tanstack-query
3
3
  description: "Fetch server data with TanStack Query (@tanstack/query-core) and show it in Nativele components: an adapter from QueryObserver and MutationObserver to bind, loading, error and refetching states with data-if, lists with data-each, query keys from props, unsubscribing on removal, and mutations with invalidation. Use when a project depends on @tanstack/query-core and @nativele/ts-dom."
4
4
  metadata:
5
5
  tier: experimental
6
- nativele: ">=0.5.2 <0.6.0"
7
- verified: "2026-11-05"
6
+ nativele: ">=0.6.0 <0.7.0"
7
+ verified: "2026-11-06"
8
8
  tools: "@tanstack/query-core@5.106.1 vite@8.3.6 typescript@7.0.2 create-vite@9.3.4"
9
9
  author: "Nativele maintainers"
10
10
  detects: "@tanstack/query-core"
@@ -25,17 +25,17 @@ metadata:
25
25
 
26
26
  | Tool | Verified version |
27
27
  |---|---|
28
- | @nativele/ts-dom | 0.5.2 (the last agent trial) |
28
+ | @nativele/ts-dom | 0.6.0 (the last agent trial) |
29
29
  | @tanstack/query-core | 5.106.1 |
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 |
33
33
 
34
- Verified on 2026-11-05 in Google Chrome 148 against the Vite dev server, in an app made with `npm create vite@latest -- --template vanilla-ts` and set up with the `nativele-vite` Skill, with a local mock API and Playwright 1.64 holding, failing and counting requests. With a different major version of TanStack Query, treat every step as unverified.
34
+ Verified on 2026-11-06 in Google Chrome 148 against the Vite dev server, in an app made with `npm create vite@latest -- --template vanilla-ts` and set up with the `nativele-vite` Skill, with a local mock API and Playwright 1.64 holding, failing and counting requests. With a different major version of TanStack Query, treat every step as unverified.
35
35
 
36
36
  ## Set-up
37
37
 
38
- Starting from a Vite app with Nativele already set up (`npx ts-dom skills nativele-vite`: `strict`, the `tsDom({ dirs: ["src/nv"] })` plugin, `"build": "ts-dom check src/nv && vite build"`):
38
+ Starting from a Vite app with Nativele already set up (`npx ts-dom skills nativele-vite`: the `tsDom({ dirs: ["src/nv"] })` plugin, `"build": "ts-dom check src/nv && vite build"`, and a tsconfig that meets `nativele-typescript`):
39
39
 
40
40
  1. `npm i @tanstack/query-core`.
41
41
  2. Create one `QueryClient` for the whole app in its own module, call `mount()` once, and add the adapter next to it:
@@ -3,8 +3,8 @@ name: nativele-testing
3
3
  description: "Test Nativele components in an app: component tests with Vitest and happy-dom (or jsdom), real-browser tests with Vitest browser mode in headless Chrome, and page tests with Playwright. Covers generating the templates before tests, creating elements, props, events, lists, data-if, stores, slots, test isolation, and what a DOM shim cannot show. Use when a project depends on @nativele/ts-dom and on vitest, @playwright/test or playwright."
4
4
  metadata:
5
5
  tier: official
6
- nativele: ">=0.5.2 <0.6.0"
7
- verified: "2026-11-05"
6
+ nativele: ">=0.6.0 <0.7.0"
7
+ verified: "2026-11-06"
8
8
  tools: "vitest@5.1.0 happy-dom@21.0.4 jsdom@30.1.3 @vitest/browser-playwright@5.1.0 playwright@1.64.1 @playwright/test@1.64.1 vite@8.3.6 typescript@7.0.2"
9
9
  author: "Nativele maintainers"
10
10
  detects: "vitest @playwright/test playwright"
@@ -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 (the last agent trial) |
28
+ | @nativele/ts-dom | 0.6.0 (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`), 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.
38
+ Verified on 2026-11-06 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`). 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
 
@@ -62,16 +62,16 @@ Verified on 2026-11-05 with Node 24 and Google Chrome 148, starting from `create
62
62
  });
63
63
  ```
64
64
 
65
- 3. Keep the tests in `test/` and add it to `include` in `tsconfig.json` (`"include": ["src", "test"]`). Vitest does not type-check; `ts-dom check src/nv` runs the project's `tsc`, which then checks the tests too, and `ts-dom-tags.ts` types `document.createElement("counter-card")` as the component class, with its props.
65
+ 3. Keep the tests in `test/`, and make the tsconfig cover that folder too (`"include": ["src", "test"]` in the trial; the TypeScript Skill, `npx ts-dom skills nativele-typescript`, checks and adjusts it). Vitest does not type-check; `ts-dom check src/nv` runs the project's `tsc`, which then checks the tests too, and `ts-dom-tags.ts` types `document.createElement("counter-card")` as the component class, with its props.
66
66
  4. Add the scripts:
67
67
 
68
68
  ```sh
69
69
  npm pkg set scripts.test="vitest run --project unit"
70
70
  ```
71
71
 
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.
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. Where these steps go in CI is covered by `npx ts-dom skills nativele-ci`.
73
73
 
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:
74
+ 5. For the real-browser tests, install the Vitest provider and Playwright, and add a second project. It drives a Chromium-based browser already installed on the machine (`CHROME_BIN`, or `/usr/bin/google-chrome`), so nothing is downloaded and `npx playwright install` is not needed; a CI runner needs such a browser too (`nativele-ci`):
75
75
 
76
76
  ```sh
77
77
  npm i -D @vitest/browser-playwright@5.1.0 playwright@1.64.1
@@ -100,7 +100,7 @@ Verified on 2026-11-05 with Node 24 and Google Chrome 148, starting from `create
100
100
  },
101
101
  ```
102
102
 
103
- `headless: true` keeps the run the same locally and in CI. The launch arguments suit containers, WSL and CI runners. Name only the tests that need a real browser `*.browser.test.ts` (limitation 1); everything else stays in happy-dom, which starts in a fraction of the time.
103
+ `headless: true` keeps the run the same locally and in CI. The launch arguments suit containers, WSL and CI runners; the CI job itself is covered by `nativele-ci`. Name only the tests that need a real browser `*.browser.test.ts` (limitation 1); everything else stays in happy-dom, which starts in a fraction of the time.
104
104
 
105
105
  6. Alternative, for tests of whole pages (the built app, every component on it, navigation): Playwright Test against `vite preview`, with the same Chrome:
106
106
 
@@ -125,7 +125,7 @@ Verified on 2026-11-05 with Node 24 and Google Chrome 148, starting from `create
125
125
  });
126
126
  ```
127
127
 
128
- Add `e2e` to `include` in `tsconfig.json` so `ts-dom check` type-checks the specs, and `test-results` and `playwright-report` to `.gitignore`.
128
+ The tsconfig must cover `e2e` too, so `ts-dom check` type-checks the specs (`nativele-typescript`). Add `test-results` and `playwright-report` to `.gitignore`.
129
129
 
130
130
  ## Example
131
131
 
@@ -224,6 +224,8 @@ 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
+ These tests compare `textContent` exactly. When the project formats its templates, the formatter's line breaks and indentation can become part of `textContent`, depending on the whitespace mode: the formatting Skill (`npx ts-dom skills nativele-formatting`) decides the mode and says how to assert on such text.
228
+
227
229
  Other parts of the API:
228
230
 
229
231
  ```ts
@@ -318,7 +320,7 @@ Playwright locators reach into open shadow roots, so `page.locator("shadow-panel
318
320
  2. **Import the module, not only the type.** `import type { CounterCard }` (or `import { type CounterCard }`) is erased, so `define()` never runs: `document.createElement("counter-card")` returns a plain element that never renders, and `tsc` still types it as `CounterCard` through `ts-dom-tags.ts`. The symptom is `expected undefined to be ...` or `Cannot read properties of null (reading 'click')`. Add `import "../src/nv/counter-card.ts"`. Child components used in a template are imported by its generated module, so importing the parent is enough.
319
321
  3. **One registry per test file.** Vitest runs each test file in a new environment by default (`isolate: true`), in happy-dom and in browser mode: each file starts with an empty document and an empty `customElements`. Inside a file, the document and the registry are shared by all its tests, so empty the document in `afterEach`. With `isolate: false` and the files run one after another in the same worker (`--no-isolate --no-file-parallelism`), they share both: the document keeps the previous file's elements, and a direct `customElements.define()` of a name already defined throws.
320
322
  4. **A tag cannot be redefined.** `define()` with a name that is already defined does nothing and keeps the first class; `customElements.define()` throws an error whose message says the name is already used or registered (in happy-dom, jsdom and Chrome). `vi.resetModules()` followed by a new `import()` creates a new class and new module-level stores, but the tag keeps running the first class, bound to the first store. Reset shared stores with `store.set(...)` in `beforeEach` instead, and give a component defined only for one test file a name no other module uses.
321
- 5. **Do not add a `data-ref` only for a test.** `ts-dom check` reports a ref that the component class never reads as `unused-ref`, and access from a test does not count. Query the rendered DOM in the test (`el.querySelector("button")`, `el.shadowRoot.querySelector(...)`, `el.assignedNodes(name)`). The same applies to the oxlint rule: turn `ts-dom/unused-markup` off for test folders, as the setup guide says.
323
+ 5. **Do not add a `data-ref` only for a test.** `ts-dom check` reports a ref that the component class never reads as `unused-ref`, and access from a test does not count. Query the rendered DOM in the test (`el.querySelector("button")`, `el.shadowRoot.querySelector(...)`, `el.assignedNodes(name)`). The linter's `ts-dom/unused-markup` rule does the same; turning it off for test folders silences only templates that test files import themselves (`npx ts-dom skills nativele-linting`).
322
324
  6. **Text of a slotted heading in a Shadow DOM component.** `textContent` (and Playwright's `toHaveText` on the element inside the shadow root) reads the slot's fallback content, not the slotted nodes: in the example, `shadow-panel h2` has the text `Untitled` while it shows `Shadow`. Read the slot with `el.assignedNodes(name)`, or in Playwright use `getByRole("heading", { name: "Shadow" })`, which follows what is shown. A Light DOM component has the slotted nodes in place, so its text is what is shown.
323
325
  7. **Slot changes after connection are asynchronous.** Children added to a Light DOM component after it is connected are placed by a `MutationObserver`, and `slotchange(name)` runs then; for a Shadow DOM component, the native `slotchange` event fires at the same point. `await Promise.resolve()` once before asserting is enough in happy-dom, jsdom and Chrome. Children present when the element is appended are placed synchronously.
324
326
  8. **Typing custom events.** `addEventListener` types an unknown event name as `Event`, so `e.detail` is a `tsc` error (`TS2339`); cast with `EmitEvent<CounterCard, "count-change">`.
@@ -336,7 +338,7 @@ npx vitest run --project browser # expect: the *.browser.test.ts files pass in
336
338
  npx playwright test # if you use the alternative: builds, serves on port 4432, passes
337
339
  ```
338
340
 
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.
341
+ The same unit tests also pass under jsdom, as a third Vitest project with `"environment": "jsdom"`.
340
342
 
341
343
  ## Troubleshooting
342
344
 
@@ -348,7 +350,7 @@ The same unit tests also pass under jsdom, as a third Vitest project with `"envi
348
350
  | `TS2339: Property 'label' does not exist on type 'HTMLElement'` on `document.createElement("counter-card")` | `tsc` ran without `ts-dom-tags.ts` (nothing generated it), or the component class is not exported | run `ts-dom check src/nv` instead of plain `tsc`; export the class |
349
351
  | `src/nv/x.html:1:1 error [unused-ref] data-ref="..." is never used by the component class` | a ref added for a test | query the DOM in the test and remove the ref (limitation 5) |
350
352
  | A test passes alone and fails with others in the same file, or sees elements from another test | the document is shared within a file | `afterEach(() => document.body.replaceChildren())`; reset module-level stores in `beforeEach` |
351
- | `Error: browserType.launch: Failed to launch chromium because executable doesn't exist at /usr/bin/google-chrome` | no Chrome at that path | set `CHROME_BIN` to the Chrome or Chromium binary |
353
+ | `Error: browserType.launch: Failed to launch chromium because executable doesn't exist at /usr/bin/google-chrome` | no Chrome at that path | set `CHROME_BIN` to a Chrome or Chromium binary; on a CI runner, install one first (`nativele-ci`) |
352
354
  | `Error: browserType.launch: Executable doesn't exist at ~/.cache/ms-playwright/...` and `Looks like Playwright was just installed or updated` | `executablePath` is missing from `launchOptions`, so Playwright looks for its own downloaded browser | set `executablePath` as in step 5 |
353
355
  | DEPRECATED: `test.browser.api` was deprecated in Vitest 5. Use `test.api` instead. | the port is set where Vitest 4 had it | move `api` from `test.browser` to `test` of the browser project |
354
356
  | A test that moves list items passes in happy-dom and fails in Chrome (or the reverse) | happy-dom has no `moveBefore` | assert moves in a `*.browser.test.ts` (limitation 1) |
@@ -360,5 +362,9 @@ The same unit tests also pass under jsdom, as a third Vitest project with `"envi
360
362
  - Light DOM and Shadow DOM slots, `assignedNodes`, `slotchange`: https://github.com/Pixmi/nativele/blob/main/docs/components.md#light-default-and-shadow
361
363
  - Stores and `bind`: https://github.com/Pixmi/nativele/blob/main/docs/components.md#state-store-when-not-using-state
362
364
  - Lists, and what moving an item does with and without `moveBefore`: https://github.com/Pixmi/nativele/blob/main/docs/lists.md#moving-items-connecting-and-subscriptions
363
- - The Vite plugin, `ts-dom check`, and lint in test folders: https://github.com/Pixmi/nativele/blob/main/docs/tooling.md
365
+ - The Vite plugin and `ts-dom check`: https://github.com/Pixmi/nativele/blob/main/docs/tooling.md
366
+ - The tsconfig: https://github.com/Pixmi/nativele/blob/main/skills/nativele-typescript/SKILL.md
367
+ - The ts-dom rules and test folders in the project's linter: https://github.com/Pixmi/nativele/blob/main/skills/nativele-linting/SKILL.md
368
+ - Whitespace in `textContent` with a formatter: https://github.com/Pixmi/nativele/blob/main/skills/nativele-formatting/SKILL.md
369
+ - CI and the browser on the runner: https://github.com/Pixmi/nativele/blob/main/skills/nativele-ci/SKILL.md
364
370
  - Setting up the Vite app first: `npx ts-dom skills nativele-vite`
@@ -0,0 +1,186 @@
1
+ ---
2
+ name: nativele-typescript
3
+ description: "Check and adjust the TypeScript set-up of a project that uses Nativele (@nativele/ts-dom): what the tsconfig covering the components needs and why, which stricter options and layouts work, how the generated files and ts-dom check fit the type check. Use when a tsconfig is added or changed, when ts-dom doctor reports a tsconfig-* issue, or when tsc errors point at the set-up rather than the code."
4
+ metadata:
5
+ tier: official
6
+ nativele: ">=0.6.0 <0.7.0"
7
+ verified: "2026-11-06"
8
+ tools: "typescript@7.0.2 typescript@6.0.3 typescript@5.9.3 vue-tsc@3.3.14 @tsconfig/strictest@2.0.8"
9
+ author: "Nativele maintainers"
10
+ detects: "typescript"
11
+ ---
12
+
13
+ # Nativele + TypeScript
14
+
15
+ ## When to use
16
+
17
+ - The project depends on `@nativele/ts-dom` and `typescript`, and you set up, review or change a `tsconfig*.json`, or `ts-dom doctor` reports `no-tsconfig`, `tsconfig-not-strict`, `tsconfig-no-dom-lib`, `tsconfig-classic-resolution`, `tsconfig-misses-templates` or `tsconfig-misses-tag-map`.
18
+ - `tsc` or `ts-dom check` fails with errors that come from the set-up (module not found, DOM names missing, a component typed as `HTMLElement`) rather than from a component.
19
+ - This Skill owns `tsconfig*.json` as far as Nativele is concerned. It does not write components (`nativele-core`), the build or its scripts (`nativele-vite`, or the framework's Skill), lint configuration (`nativele-linting`), formatter configuration (`nativele-formatting`), CI (`nativele-ci`), or a framework's own type checker (`vue-tsc`, `svelte-check`, `ngc`); it states what those need from TypeScript and leaves the how to them.
20
+
21
+ Nativele's value is that `tsc` checks the HTML against the TypeScript: `ts-dom gen` writes a typed `x.html.ts` next to each template and a `ts-dom-tags.ts` per scanned folder, and the tsconfig decides whether `tsc` sees them. Nativele needs no special TypeScript option: it needs a few ordinary ones to be in place, and works with the common stricter ones.
22
+
23
+ ## Versions
24
+
25
+ | Tool | Verified version |
26
+ |---|---|
27
+ | @nativele/ts-dom | 0.6.0 (the last agent trial) |
28
+ | typescript | 7.0.2, 6.0.3, 5.9.3 (every row below); 5.6.3, 5.4.5, 5.0.4 (the baseline set-up only) |
29
+ | vue-tsc | 3.3.14 (with typescript 6.0.3 and 7.0.2) |
30
+ | @tsconfig/strictest | 2.0.8 |
31
+
32
+ Verified on 2026-11-06 in fresh projects built from the packed library, with a fixture of components that covers state, props, derive, emits with and without detail, keyed lists of components, plain lists, slots, `shadow: true`, `bind`, `on`, `instantiate()` and the tag map, plus probe files whose `// @ts-expect-error` lines must each stay an error. With a TypeScript major version not listed, treat every statement as unverified.
33
+
34
+ ## Set-up
35
+
36
+ Check first, then change only what fails. Never replace a tsconfig, reorder it, or change an option this Skill does not name.
37
+
38
+ 1. **Find the config that type-checks the components.** Read `tsconfig.json` in the folder where `ts-dom` runs (the one with `package.json`). If it has `"files": []` and `references` (a solution-style root, as in Vite's templates), the configs that count are the referenced ones; find the one whose `include` covers the Nativele folder (in Vite's templates, `tsconfig.app.json`). Leave the other configs (for `vite.config.ts`, Node scripts, tests in another runtime) as they are.
39
+ 2. **Read its effective options, not the file.** `npx tsc --showConfig -p <config>` prints the options after every `extends` (a relative path, a package such as `@tsconfig/strictest`, or an array) and the file list. It does not print defaults: an absent `strict` is on in TypeScript 6 and 7 and off in TypeScript 5 (see Known limitations). `npx tsc --listFilesOnly -p <config>` lists every file in the program; after `npx ts-dom gen <dir>`, each `x.html.ts` and the folder's `ts-dom-tags.ts` must be in it.
40
+ 3. **Run `npx ts-dom doctor <dir>`** (the same folders you pass to `ts-dom check`). Each `tsconfig-*` issue names the file and the fix. Doctor reads `tsconfig.json` and its references only, and checks the referenced configs that cover the template folders; for a config that is not `tsconfig.json`, pass `--project=<path>`.
41
+ 4. **Compare with what Nativele needs**, and change only the rows that fail:
42
+
43
+ | Requirement | Why | Without it (exact error) |
44
+ |---|---|---|
45
+ | `lib` includes `DOM` (or `lib` absent: the default includes it) | the library's types and the generated files name DOM types | `node_modules/@nativele/ts-dom/dist/index.d.mts(3,24): error TS2304: Cannot find name 'Element'.` (with `skipLibCheck: true`: `TS2584: Cannot find name 'document'` and `TS2304: Cannot find name 'Event'` in your files); doctor: `tsconfig-no-dom-lib` |
46
+ | A module resolution that finds packages in `node_modules`: `bundler` (with `module` `ESNext` or `Preserve`), `NodeNext`, or `Node16` (`node10` too, on TypeScript 5 only) | the components and the generated files import `@nativele/ts-dom` | with `classic` on TypeScript 5: `TS2792: Cannot find module '@nativele/ts-dom'`, and then every component loses its base class (`TS2339: Property 'refs' does not exist on type 'X'`); TypeScript 6 rejects `classic` and `node10` with `TS5107` (deprecated), TypeScript 7 with `TS5108` (removed) |
47
+ | `include` covers the `.ts` files of every folder with templates | the generated `ts-dom-tags.ts` is imported by nothing, so only `include` brings it in; a component no other file imports is not checked at all | a component typed as plain `HTMLElement` (`TS2339: Property 'todo' does not exist on type 'HTMLElement'`), a keyed list reported as `item-not-in-tag-map`, and contract errors of unimported components (`missing-binding`) not reported; doctor: `tsconfig-misses-templates`, and `tsconfig-misses-tag-map` (an error) when `include` or `exclude` leaves `ts-dom-tags.ts` out |
48
+ | `strict: true` (set explicitly) | null checks and implicit `any` belong to it | see "What `strict` adds" below; doctor: `tsconfig-not-strict` (a warning) for `"strict": false`, and for an absent `strict` on TypeScript 5 |
49
+ | `"type": "module"` in `package.json`, when `module` is `NodeNext` or `Node16` and `tsc` emits the JavaScript | without it the files are CommonJS | `tsc` reports nothing, but emits `Object.defineProperty(exports, "__esModule", ...)`, which a browser cannot run; with `bundler`, or when `tsc` does not emit, it makes no difference to the type check |
50
+
51
+ Add a missing `DOM` to the existing `lib` array; add the missing folder to the existing `include` (as `"src/nv"` or a pattern ending in `.ts`, matching the style already there); change `moduleResolution` only when it is `classic`, absent with `module` `ESNext` on TypeScript 5 (that default is `classic`), or `node10`/`node` on TypeScript 6 or 7 (on 5.9.3 it works; leave it unless the user moves to a newer TypeScript).
52
+ 5. **`strict`.** If it is absent, add `"strict": true`: on TypeScript 6 and 7 that changes nothing but makes it explicit for doctor and for TypeScript 5. If the project sets `"strict": false`, do not turn it on for the whole app on your own: the errors it brings in code outside the Nativele folder are not this task. Turn it on in the config that covers the Nativele folder, or, when that config is the whole app, add a config for the folder and pass it to `ts-dom check --project=` and `ts-dom doctor --project=`:
53
+
54
+ ```json
55
+ // tsconfig.nv.json
56
+ { "extends": "./tsconfig.json", "compilerOptions": { "strict": true }, "include": ["src/nv"] }
57
+ ```
58
+
59
+ Tell the user that `ts-dom doctor` without `--project=` keeps warning `tsconfig-not-strict` on `tsconfig.json` in that case.
60
+ 6. **Keep the user's other options.** The stricter options in the Compatibility table work with the generated files and the library's types; do not remove or relax one to make Nativele pass. The only option that does not work is `isolatedDeclarations` (Known limitations).
61
+ 7. **State the order to the owner of the scripts.** The generated files are not committed, so every type check (`tsc`, `tsc -b`, `vue-tsc`, `svelte-check`, `ngc`, in a script, a hook or CI) must run after `ts-dom gen <dir>`, or be `ts-dom check <dir>`, which generates first. On a clean checkout a type check that runs first fails with `TS2307: Cannot find module './x.html.js' or its corresponding type declarations`, and doctor reports the script as `typecheck-before-gen`. Do not choose the script here: the build Skill (`nativele-vite`) or the framework's Skill owns it, and the CI steps belong to `nativele-ci`.
62
+ 8. **Run Verify.**
63
+
64
+ ### What `strict` adds
65
+
66
+ The template contract does not depend on `strict`: with `"strict": false` on 7.0.2, 6.0.3 and 5.9.3, `tsc` and `ts-dom check` still report `missing-binding`, `missing-handler` (from `define()`), an unknown `this.refs.x`, an unknown `this.show()` name, a child prop of the wrong type through `ts-dom-tags.ts`, a wrong `EmitEvent` detail type, `emit` with a missing, extra or undeclared event, writes to deeply read-only state, and a `data-prop` that is not a prop of the item component. What `strict` adds is everything about `null`, `undefined` and `any`: `'x' is possibly 'null'` (`TS18047`) from `querySelector`, `null` assigned to a prop or passed to `set()`, `hit.key` of an untyped `EventHit` used as a `PropertyKey` although it may be `undefined`, and `Parameter 'e' implicitly has an 'any' type` (`TS7006`). With `"strict": false` plus `"strictNullChecks": true` and `"noImplicitAny": true`, every one of those is reported again, but doctor still warns, because it reads only `strict`.
67
+
68
+ ### Compatibility
69
+
70
+ Each option set on the config that covers the fixture, on 7.0.2, 6.0.3 and 5.9.3 (`tsc --noEmit`, and `ts-dom check`):
71
+
72
+ | Option | Result |
73
+ |---|---|
74
+ | `noUncheckedIndexedAccess` | works |
75
+ | `exactOptionalPropertyTypes` | works; `set({ x: undefined })` becomes an error, as for any `Partial` |
76
+ | `verbatimModuleSyntax` (with `NodeNext`, and with `bundler`) | works; emitting with `tsc` keeps the generated imports correct |
77
+ | `isolatedModules` | works |
78
+ | `erasableSyntaxOnly` | works |
79
+ | `noImplicitOverride` | works; your own `connected`, `render`, `slotchange` need `override`, as the guides write them |
80
+ | `noUnusedLocals`, `noUnusedParameters` | works, including generated files for templates without refs or with several root elements; name unused handler parameters `_e`, `_hit` |
81
+ | `noPropertyAccessFromIndexSignature`, `noImplicitReturns`, `noFallthroughCasesInSwitch`, `noUncheckedSideEffectImports` | works |
82
+ | `allowImportingTsExtensions` | works; still import templates as `./x.html.js` (Known limitations) |
83
+ | `skipLibCheck` `true` or `false` | both work; `false` also checks the library's own declarations, which pass |
84
+ | all of the above together, and `extends: ["@tsconfig/strictest/tsconfig.json", ...]` | works |
85
+ | `declaration` or `composite` (with `emitDeclarationOnly`, `rootDir`, `outDir`) | works; `ts-dom check` on a `composite` config passes too |
86
+ | `isolatedDeclarations` | does not work (Known limitations) |
87
+ | `moduleResolution` `bundler`, `NodeNext`, `Node16`; `module` `CommonJS` or `Preserve` with the default resolution | works |
88
+ | `moduleResolution` `classic` | does not work: see step 4 |
89
+ | `moduleResolution` `node10` (or `node`) | works on 5.9.3; rejected by TypeScript 6 (`TS5107`) and 7 (`TS5108`) |
90
+ | `lib` / `target` `ES2015` or `ES2017` with `DOM` | works; `target` `ES5` is rejected by TypeScript 7 itself (`TS5108`) |
91
+
92
+ ### How `ts-dom check` and `ts-dom doctor` choose configs
93
+
94
+ | | `ts-dom check [dirs]` | `ts-dom doctor [dirs]` |
95
+ |---|---|---|
96
+ | Default | `tsconfig.json` in the working folder; a solution-style one (`"files": []` with `references`) is replaced by each referenced config, and `tsc --noEmit -p <config>` runs once per config | `tsconfig.json` and, when solution-style, its references; the option checks run on the configs whose `include` covers a template folder, or on all of them when none does |
97
+ | Another config | `--project=<path>` | `--project=<path>` |
98
+ | Checker | `vue-tsc` when `package.json` lists it, otherwise `typescript`'s `tsc` | does not type-check |
99
+ | No config | `tsc-failed` (an error) | `no-tsconfig` (an error) |
100
+
101
+ A `ts-dom check` whose config does not cover the Nativele folder still runs and can pass with the components unchecked: on a `tsconfig.json` that includes only Node scripts, it reported only the lint findings, not the `missing-binding` the same run with `--project=` found. Doctor reports that layout as `tsconfig-misses-templates`.
102
+
103
+ ### Framework type checkers
104
+
105
+ This Skill decides what the tsconfig that covers the Nativele folder needs (the table in step 4); that applies unchanged when the checker is `vue-tsc`, `svelte-check` or `ngc`, which read the same tsconfig. The framework's Skill decides which checker runs, its script, its own config (`vueCompilerOptions`, Svelte or Angular options) and the declarations that type Nativele tags in the framework's templates. What this Skill states to them:
106
+
107
+ - They need the generated files first, as in step 7.
108
+ - At the versions below they need TypeScript 6: `vue-tsc@3.3.14` with `typescript@7.0.2` stops with `Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath './lib/tsc' is not defined by "exports"` (and `ts-dom check` reports that as `tsc-failed`); `svelte-check@4.7.8` declares the peer range `typescript@"^5.0.0 || ^6.0.0"` and npm refuses `typescript@7.0.2` with `ERESOLVE`; `@angular/compiler-cli@22.2.4` declares `typescript: '>=6.0 <6.1'`. The requirements in step 4 are the same on 6.0.3.
109
+ - `ts-dom check` runs `vue-tsc` itself when `package.json` lists it (verified: it reported a `TS2322` inside an `.vue` file); it does not run `svelte-check` or `ngc`.
110
+
111
+ ## Example
112
+
113
+ A Vite app with a solution-style root, the Nativele components in `src/nv`, and a strict preset through `extends`. Before:
114
+
115
+ ```json
116
+ // tsconfig.json
117
+ { "files": [], "references": [{ "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" }] }
118
+ ```
119
+
120
+ ```json
121
+ // tsconfig.app.json
122
+ {
123
+ "extends": ["@tsconfig/strictest/tsconfig.json"],
124
+ "compilerOptions": {
125
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
126
+ "target": "ES2022",
127
+ "module": "ESNext",
128
+ "moduleResolution": "bundler",
129
+ "verbatimModuleSyntax": true,
130
+ "noEmit": true
131
+ },
132
+ "include": ["src"]
133
+ }
134
+ ```
135
+
136
+ `tsconfig.node.json` covers only `vite.config.ts` and has `"lib": ["ES2023"]`.
137
+
138
+ Checking it: the configs that count are the references, and `tsconfig.app.json` is the one whose `include` (`src`) covers `src/nv`. `npx tsc --showConfig -p tsconfig.app.json` shows `"strict": true` (from the preset) and `"moduleResolution": "bundler"`, and no `lib`, so the default for the target applies, which includes DOM. `npx ts-dom doctor src/nv` reports no `tsconfig-*` issue; it does not judge `tsconfig.node.json`, which covers no template folder, so its `"lib": ["ES2023"]` stays. Nothing in the tsconfigs changes. The one requirement left goes to the owner of the build script: `"build": "tsc -b && vite build"` type-checks before anything generates, and doctor reports it as `typecheck-before-gen` (the `.gitignore` lists `*.html.ts`); the Vite Skill or the framework's Skill decides how the script changes.
139
+
140
+ Verified with that layout: `npx ts-dom check src/nv` prints `ts-dom check: no issues`, `npx tsc -b` exits 0, and `npx tsc --listFilesOnly -p tsconfig.app.json` lists every `x.html.ts` and `src/nv/ts-dom-tags.ts`.
141
+
142
+ ## Known limitations
143
+
144
+ 1. **`strict` absent means different things.** TypeScript 6 and 7 turn `strict` on when the key is absent; TypeScript 5 leaves it off, so the same tsconfig loses the null checks on 5.9.3. `tsc --showConfig` does not show either default, and doctor warns `tsconfig-not-strict` for `"strict": false`, and for an absent `strict` only when the installed TypeScript is 5. Write `"strict": true`.
145
+ 2. **A `classic` default on TypeScript 5.** `"module": "ESNext"` (any module from ES2015, or a `target` from ES2015 without `module`) without `moduleResolution` resolves as `classic` on TypeScript 5 (`tsc --showConfig` shows `"moduleResolution": "classic"`), and every import of `@nativele/ts-dom` fails with `TS2792`. Doctor reports `tsconfig-classic-resolution` for it when it finds TypeScript 5 installed; the fix is `bundler` or `NodeNext`.
146
+ 3. **`include` and `exclude` can leave `ts-dom-tags.ts` out.** An `include` of `"src/nv/**/*.tsx"`, or an `exclude` of `**/ts-dom-tags.ts`, leaves it out of the program; doctor reports `tsconfig-misses-tag-map` (an error), and the symptoms are those of a missing `include` in step 4. Do not exclude the generated files from the config that covers the components, and let `include` select `.ts` files there. Excluding only `**/*.html.ts` is harmless (they are reached through imports) but has no purpose.
147
+ 4. **Doctor starts from `tsconfig.json` unless told otherwise.** When that file is a plain config for something else (Node scripts, say) and another config covers the components, doctor reports `tsconfig-no-dom-lib` and `tsconfig-misses-templates` against `tsconfig.json`. Run `ts-dom doctor --project=<the other config>` with the same config as `ts-dom check --project=`, and do not add `DOM` or the component folder to a config that is not for them. A solution-style `tsconfig.json` (`"files": []` with `references` to both configs) makes the layout explicit; that renames what `tsc -p tsconfig.json` means, so it is the user's decision.
148
+ 5. **`isolatedDeclarations` does not work** for the folder with components: every generated file fails with `TS9037: Default exports can't be inferred with --isolatedDeclarations.` and every component with `TS9021: Extends clause can't contain an expression with --isolatedDeclarations.` (`class X extends Component(tpl, ...)`). Keep it off in the config that covers the Nativele folder; a library package that needs it keeps its Nativele components in a package of their own (a workspace of several packages: `nativele-monorepo`).
149
+ 6. **Template imports stay `./x.html.js`**, also with `allowImportingTsExtensions`. `import tpl from "./x.html.ts"` compiles, but the Nativele tools match `.html.js`: the template is reported as `no-importer`, and a `missing-binding` points at the `.ts` instead of the template line. With `allowImportingTsExtensions`, `tsc` requires `noEmit` or `emitDeclarationOnly`; `ts-dom check` always runs `tsc --noEmit`.
150
+ 7. **Emitting with `tsc` on TypeScript 6 and 7** needs `rootDir` when `outDir` is set and the sources are in a subfolder: without it, `TS5011: The common source directory of 'tsconfig.json' is './src'. The 'rootDir' setting must be explicitly set`. TypeScript 5.9.3 emitted without it. This concerns projects without a bundler, where `tsc` writes the JavaScript.
151
+ 8. **A plain `tsc` on a solution-style root checks nothing**: `npx tsc --noEmit` exited 0 with a broken component in `src/nv`; `tsc -b`, `tsc -p tsconfig.app.json` and `ts-dom check` reported it.
152
+
153
+ ## Verify
154
+
155
+ ```sh
156
+ npx tsc --showConfig -p <config> # expect: lib absent or with dom; moduleResolution bundler, nodenext or node16; strict true; the files include src/nv/ts-dom-tags.ts
157
+ npx ts-dom doctor src/nv # expect: no tsconfig-* issue (or only those Known limitations 4 explains; pass `--project=` for another config)
158
+ npx ts-dom check src/nv # expect: ts-dom check: no issues
159
+ npx tsc -b # with a solution-style root (or tsc --noEmit -p <config>); expect: exit code 0
160
+ ```
161
+
162
+ Then prove the check reaches the components: add `<p>{{ nope }}</p>` to a template of a component that has `state`, run `npx ts-dom check src/nv`, and expect `error [missing-binding] "nope" is used by {{ nope }} ...` at the template's line. If `strict` is on, also expect `const el = document.querySelector("x-y"); el.remove();` in a component file to give `TS18047: 'el' is possibly 'null'`. Remove both afterwards.
163
+
164
+ ## Troubleshooting
165
+
166
+ | Symptom | Cause | Fix |
167
+ |---|---|---|
168
+ | `TS2307: Cannot find module './x.html.js' or its corresponding type declarations` | the generated files do not exist yet (clean checkout) | run `ts-dom gen <dir>` before the type check, or `ts-dom check <dir>` (step 7) |
169
+ | `TS2792: Cannot find module '@nativele/ts-dom'. Did you mean to set the 'moduleResolution' option to 'nodenext'` | `classic` resolution, written or the TypeScript 5 default for `module` `ESNext` | `"moduleResolution": "bundler"` (or `NodeNext`) |
170
+ | `TS5107: Option 'moduleResolution=node10' is deprecated` / `TS5108: ... has been removed` | TypeScript 6 / 7 with `node10` (or `node`) or `classic` | `bundler` or `NodeNext`; not `ignoreDeprecations` |
171
+ | `TS2304: Cannot find name 'Element'` in `node_modules/@nativele/ts-dom/dist/index.d.mts` | `lib` without `DOM` | add `"DOM"` to `lib` |
172
+ | `TS2339: Property '<prop>' does not exist on type 'HTMLElement'` on `document.createElement("x-y")`, or `item-not-in-tag-map` although `ts-dom gen` ran | `ts-dom-tags.ts` is not in the program: `include` misses the folder, selects only `.tsx`, or `exclude` drops it | fix `include`/`exclude`; confirm with `tsc --listFilesOnly -p <config>` |
173
+ | `ts-dom check: no issues` although a template is wrong | the config `ts-dom check` used does not cover the folder | `ts-dom doctor <dir>`; pass `--project=` or fix `include` |
174
+ | `TS2344: Type 'HTMLElementTagNameMap[K]' does not satisfy the constraint 'Element'` in `lib.dom.d.ts` | a follow-up of a component class broken by a contract error (`missing-binding`, ...); `ts-dom check` leaves it out in the class's own file and says in other files which error it follows from | fix the contract error that `ts-dom check` reports first |
175
+ | `tsc-failed`: `vue-tsc did not check the types: ... ERR_PACKAGE_PATH_NOT_EXPORTED ... './lib/tsc'` | `vue-tsc` with TypeScript 7 | TypeScript 6 in a Vue project; see the Vue Skill |
176
+ | `TS9037` / `TS9021` with `isolatedDeclarations` | Known limitations 5 | turn it off for the Nativele folder's config |
177
+ | `tsconfig-not-strict` | `"strict": false`, or `strict` absent on TypeScript 5 (on 6 and 7 an absent `strict` is on and not reported) | write `"strict": true` |
178
+
179
+ ## See also
180
+
181
+ - Setup with TypeScript only, and the no-bundler tsconfig: https://github.com/Pixmi/nativele/blob/main/docs/tooling.md#installation-and-setup
182
+ - What each tool checks, `ts-dom check`, `ts-dom doctor` and which command writes files: https://github.com/Pixmi/nativele/blob/main/docs/tooling.md#checks
183
+ - The generated `x.html.ts` and why it is not committed: https://github.com/Pixmi/nativele/blob/main/docs/templates.md#how-types-are-generated
184
+ - `ts-dom-tags.ts` and element types across components: https://github.com/Pixmi/nativele/blob/main/docs/components.md#composition-using-multiple-components-together
185
+ - Several packages in a workspace, each with its own tsconfig: https://github.com/Pixmi/nativele/blob/main/skills/nativele-monorepo/SKILL.md
186
+ - How Skills share a project, and who owns which configuration: https://github.com/Pixmi/nativele/blob/main/skills/README.md#how-skills-work-together