@nativele/ts-dom 0.5.4 → 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.
- package/CHANGELOG.md +69 -0
- package/README.md +6 -1
- package/README.zh-TW.md +6 -1
- package/dist/{a11y-CiEJj_e6.mjs → a11y-KXbo-8o3.mjs} +119 -27
- package/dist/cli.mjs +273 -50
- package/dist/{lint-Did4w-NL.mjs → lint-DUsdDKZ-.mjs} +33 -8
- package/dist/oxlint-plugin.mjs +3 -3
- package/dist/vite-plugin.mjs +1 -1
- package/docs/architecture.md +188 -0
- package/docs/components.md +367 -0
- package/docs/contributing.md +80 -0
- package/docs/design.md +55 -0
- package/docs/lists.md +150 -0
- package/docs/performance.md +24 -0
- package/docs/recipes.md +189 -0
- package/docs/templates.md +73 -0
- package/docs/tooling.md +251 -0
- package/llms.txt +5 -5
- package/package.json +2 -1
- package/skills/README.md +51 -14
- package/skills/nativele-angular/SKILL.md +15 -11
- package/skills/nativele-architecture/SKILL.md +29 -16
- package/skills/nativele-ci/SKILL.md +148 -0
- package/skills/nativele-core/SKILL.md +16 -13
- package/skills/nativele-formatting/SKILL.md +167 -0
- package/skills/nativele-integration/SKILL.md +12 -9
- package/skills/nativele-linting/SKILL.md +174 -0
- package/skills/nativele-monorepo/SKILL.md +298 -0
- package/skills/nativele-react/SKILL.md +13 -10
- package/skills/nativele-state-management/SKILL.md +6 -6
- package/skills/nativele-svelte/SKILL.md +10 -8
- package/skills/nativele-tailwind/SKILL.md +8 -7
- package/skills/nativele-tanstack-query/SKILL.md +5 -5
- package/skills/nativele-testing/SKILL.md +22 -16
- package/skills/nativele-typescript/SKILL.md +186 -0
- package/skills/nativele-vite/SKILL.md +21 -17
- package/skills/nativele-vue/SKILL.md +10 -8
- package/skills/nativele-zod/SKILL.md +6 -6
package/docs/tooling.md
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
# Setup, checks and lint
|
|
2
|
+
|
|
3
|
+
Part of the [Nativele](../README.md) documentation.
|
|
4
|
+
|
|
5
|
+
## Installation and Setup
|
|
6
|
+
|
|
7
|
+
You only need TypeScript; **no Vite or any other bundler is required**:
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm i -D typescript
|
|
11
|
+
npm i @nativele/ts-dom
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
`tsconfig.json` (the same as in an ordinary TS project, with no special options):
|
|
15
|
+
|
|
16
|
+
```json
|
|
17
|
+
{
|
|
18
|
+
"compilerOptions": {
|
|
19
|
+
"target": "ES2022",
|
|
20
|
+
"module": "NodeNext",
|
|
21
|
+
"moduleResolution": "NodeNext",
|
|
22
|
+
"lib": ["ES2022", "DOM"],
|
|
23
|
+
"strict": true,
|
|
24
|
+
"rootDir": "src",
|
|
25
|
+
"outDir": "out"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Set `"type": "module"` in `package.json`: with `"module": "NodeNext"`, a package without it is CommonJS, so `tsc` emits `require`/`exports` and the browser stops with `ReferenceError: exports is not defined`.
|
|
31
|
+
|
|
32
|
+
Workflow: first generate the template modules, then compile and type-check with `tsc`.
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
npx ts-dom gen # generate x.html.ts for every x.html under src/
|
|
36
|
+
npx tsc # type-check and compile to out/
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Whitespace in text is kept as written. To keep only the whitespace the browser renders, use `npx ts-dom gen --whitespace=collapse` or the Vite plugin option `whitespace: "collapse"`: a run of whitespace between two inline-level siblings (text, `<a>`, `<b>`, `<span>`, `<button>`, `<input>`, custom elements) becomes one space, line breaks included; whitespace next to a block-level element (`<div>`, `<p>`, `<li>`, headings, table parts) and at the start and end of a block or inline-block element's content is dropped; an inline element keeps one space at the start and end of its content; a non-breaking space (` `) is kept; `<pre>`, `<textarea>`, `<style>` and `<script>` are left alone. In SVG, the content of a `<text>` follows the inline rule (`<tspan>`, `<textPath>` and `<a>` in it are inline), and whitespace between other SVG elements is dropped. It decides by each element's default display, as formatters such as Prettier and oxfmt do for HTML, so an element whose CSS changes its display is still judged by its tag: a `<div>` styled `display: inline` loses the space next to it. With a formatter, `collapse` keeps the rendered text and `textContent` unchanged when a template is reformatted, with one exception: Prettier treats SVG text as blocks and puts a line break between text and a `<tspan>` that touch, which renders as a space (oxfmt does not). Each generated file records its mode, and a run without `--whitespace` keeps the mode a file was last written with (a new file gets `preserve`), so pass `--whitespace=preserve` to switch back. `check` and `doctor` accept `--whitespace=`, `--include=` and `--exclude=` as `gen` does, and use them to decide which generated files are stale.
|
|
40
|
+
|
|
41
|
+
To scan several folders of one package, list them: `npx ts-dom gen src/app src/shared` (the same for `--check`, `--watch`, `check`, `doctor` and `lint`, and the Vite plugin's `dirs` option). The folders are generated together: a template in one folder that uses a component defined in another gets the import of that component, as the relative path from the template to the file with the `define()`, and a tag defined with different classes in two folders is a `duplicate-define` error. Do not scan another package's folder: the relative imports are written into that package's generated files. A package of shared components generates its own files, and the app imports the package.
|
|
42
|
+
|
|
43
|
+
During development, use `npx ts-dom gen --watch` to keep watching `.html` files, together with `tsc --watch`.
|
|
44
|
+
|
|
45
|
+
The generated files (`x.html.ts` next to each template, and `ts-dom-tags.ts` in each scanned folder) are derived from the templates, so most projects do not commit them: add `*.html.ts` and `ts-dom-tags.ts` to `.gitignore`.
|
|
46
|
+
|
|
47
|
+
`npx ts-dom gen --check` writes nothing: it lists generated files that are missing, outdated (their template or a `define()` changed) or orphaned (their template was deleted, or a `ts-dom-tags.ts` is left in a subfolder that is not itself scanned, for example after the components moved), and exits with code 1 if there are any (`--json` for machine-readable output). Use it in CI if your project commits the generated files, or to confirm they are fresh before reading type errors.
|
|
48
|
+
|
|
49
|
+
In your code, import templates with a `.js` suffix (standard NodeNext style; TS maps it to the generated `x.html.ts`):
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
import tpl from "./user-card.html.js";
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Running in the browser (no bundler)
|
|
56
|
+
|
|
57
|
+
The compiled JS is standard ES modules. Only one bare module specifier, `@nativele/ts-dom`, needs to be mapped, and an import map does it:
|
|
58
|
+
|
|
59
|
+
```html
|
|
60
|
+
<script type="importmap">
|
|
61
|
+
{ "imports": { "@nativele/ts-dom": "/node_modules/@nativele/ts-dom/dist/index.mjs" } }
|
|
62
|
+
</script>
|
|
63
|
+
<user-card></user-card>
|
|
64
|
+
<script type="module" src="/out/main.js"></script>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
This workflow (installing only `typescript`, no Vite, from `gen` to running in a real browser) is verified automatically by `npm run smoke:no-bundler`.
|
|
68
|
+
|
|
69
|
+
### With Vite (optional)
|
|
70
|
+
|
|
71
|
+
When you use a bundler you do not need an import map. The `@nativele/ts-dom/vite` plugin is just a convenience: it generates the `.html.ts` files when the build, the dev server or Vitest starts, watches `.html` and regenerates during dev, and offers optional ARIA warnings. It never type-checks: run `ts-dom check` (or `ts-dom gen` and `tsc`) for that. A template that codegen rejects is printed only in the terminal that runs Vite (at build start the build fails; in dev the page keeps the last generated module), not in the browser.
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
import { defineConfig } from "vite-plus"; // or "vite"
|
|
75
|
+
import { tsDom } from "@nativele/ts-dom/vite";
|
|
76
|
+
|
|
77
|
+
export default defineConfig({
|
|
78
|
+
plugins: [tsDom()], // options: dirs (default ["src"]), runtime, whitespace, include, exclude, a11y
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
While the dev server runs, the plugin keeps the generated files in step: adding or editing a template regenerates its `x.html.ts` and, when the module has been loaded, sends a full reload to every connected client (a save that changes nothing does not reload), deleting a template removes its `x.html.ts`, and adding or removing a `define()` updates `ts-dom-tags.ts` and the child imports of the templates. `dirs` are relative to Vite's `root`, not the working directory; an entry that does not exist there is reported as a `dir-not-found` warning when the build or dev server starts. The page reloads rather than hot-swapping the component because a custom element tag can be defined only once, so a new class cannot replace the running one.
|
|
83
|
+
|
|
84
|
+
A `vitest.config.ts` replaces `vite.config.ts` for Vitest, so add `tsDom()` to its `plugins` too, or merge the Vite config into it (`mergeConfig(viteConfig, defineConfig({ test: {} }))` with `viteConfig` imported from `./vite.config`); otherwise the tests start without generated files.
|
|
85
|
+
|
|
86
|
+
Vite loads `vite.config.ts` as an ES module only when `package.json` has `"type": "module"` (`npm pkg set type=module`); without it, Vite warns that the config is loaded as CommonJS.
|
|
87
|
+
|
|
88
|
+
When installing, we recommend installing `vite-plus` first and then `ts-dom` (when both are installed in the same npm run, `vite` may not be hoisted to the top level).
|
|
89
|
+
|
|
90
|
+
### Lint (optional)
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
// lint settings in vite.config.ts (vp lint / oxlint)
|
|
94
|
+
lint: {
|
|
95
|
+
ignorePatterns: ["node_modules", "dist", "**/*.html.ts", "**/ts-dom-tags.ts"],
|
|
96
|
+
jsPlugins: [{ name: "ts-dom", specifier: "@nativele/ts-dom/oxlint" }],
|
|
97
|
+
rules: {
|
|
98
|
+
"ts-dom/unused-markup": "error",
|
|
99
|
+
"ts-dom/a11y": ["warn", { ignore: [] }], // optional: ARIA checks, see [ARIA / Accessibility Checks](#aria--accessibility-checks)
|
|
100
|
+
"ts-dom/data-flow": ["warn", { ignore: [] }], // optional: avoid passing props and events layer by layer, see [Data Flow](components.md#data-flow-props-down-events-up)
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
The same module is an ESLint plugin for flat config (verified with ESLint 9 and 10, with typescript-eslint's parser for `.ts` files): `import tsDom from "@nativele/ts-dom/oxlint"` and a config object `{ plugins: { "ts-dom": tsDom }, rules: { "ts-dom/unused-markup": "error" } }`. ESLint does not read `.gitignore`, so add the generated files to its ignores: `globalIgnores(["**/*.html.ts", "**/ts-dom-tags.ts"])` from `eslint/config`.
|
|
106
|
+
|
|
107
|
+
Each rule id is also a rule of its own, so a project can enable rules one by one and give each its own severity, for example `"ts-dom/unused-ref": "warn"` or `"ts-dom/missing-handler": "error"` instead of the whole `ts-dom/unused-markup` group. `ts-dom rules` lists the ids. The exception is `no-importer`, which only `ts-dom lint` reports: the oxlint plugin sees one file at a time and cannot tell that no file imports a template.
|
|
108
|
+
|
|
109
|
+
**In the editor.** Install the Oxc extension for VS Code (it runs `oxlint --lsp`) and keep the plugin in your oxlint config: the findings show on the template's import line as you type, and `missing-handler` offers a quick fix that adds the method to the component class. From the command line, `oxlint --fix-suggestions` applies the same fix. A fix can only change the file being linted, so problems that live in the HTML (an unused `data-ref`, broken markup) are reported but not fixed there; for markup structure and layout use a formatter such as oxfmt.
|
|
110
|
+
|
|
111
|
+
Without oxlint, the same checks are available through the CLI:
|
|
112
|
+
|
|
113
|
+
```sh
|
|
114
|
+
npx ts-dom lint [dirs...] # unused markup (errors) and data flow suggestions (warnings)
|
|
115
|
+
npx ts-dom lint --a11y # additionally check ARIA / accessibility (warnings)
|
|
116
|
+
npx ts-dom lint --strict # warnings also make the exit code 1 (errors always do)
|
|
117
|
+
npx ts-dom lint --json # output JSON: { "issues": [{ file, line, col, severity, rule, message }] }
|
|
118
|
+
npx ts-dom rules # list all rule ids and descriptions
|
|
119
|
+
npx ts-dom rules --explain=unused-ref # why the rule exists, a wrong example and the fix
|
|
120
|
+
npx ts-dom rules --json # every rule as data: { id, group, description, why, bad, good }
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Every message has the same format. It explains **what is wrong, why, and how to fix it**, and includes the line and column in the HTML:
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
a.html:2:1 error [unused-ref] data-ref="dead" is never used by the component class. Fix: use this.refs.dead, or remove the attribute if the element needs no TypeScript access.
|
|
127
|
+
a.html:3:1 warning [unknown-prop] <x-kid lable> is not a declared prop of <x-kid>. Declared props: label, count. Did you mean "label"? Fix: declare "lable" in the child's props option, or remove the attribute.
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Exit codes: 0 = no problems, 1 = there are errors (or warnings when `--strict` is used). Messages from the oxlint plugin start with `./x.html.js:line:column [rule id] message` and are reported on the `import` line.
|
|
131
|
+
|
|
132
|
+
Recommended check workflow: `ts-dom check` (it runs `gen`, `tsc` and `lint` in one pass; see [Checks](#checks)).
|
|
133
|
+
|
|
134
|
+
## Checks
|
|
135
|
+
|
|
136
|
+
Each tool checks what it can see, and none repeats another:
|
|
137
|
+
|
|
138
|
+
| Tool | What it checks |
|
|
139
|
+
|---|---|
|
|
140
|
+
| A formatter such as oxfmt (`oxfmt --check`, or `vp fmt --check`) | Whether the template markup is well formed (a misnested closing tag such as `<p><div></p></div>` is a syntax error there, while the HTML parser the library uses would silently repair it into a different tree) and laid out consistently. It knows nothing of `{{ }}` or `data-*` names. Generate with whitespace `collapse` when you use one, or reformatting can change the spacing on the page. |
|
|
141
|
+
| `ts-dom gen` | The template rules: holes, `data-each`, `data-if`, `data-on-*`, forbidden markup. |
|
|
142
|
+
| `tsc` | That every name in the HTML exists in the component with the right type, and that lists, props and events fit. |
|
|
143
|
+
| `ts-dom lint` / the oxlint plugin | What the HTML declares but the class never uses, data flow between components, accessibility. |
|
|
144
|
+
| `ts-dom doctor` | The project set-up that the other tools depend on. |
|
|
145
|
+
| `ts-dom check` | Runs `gen`, `tsc` and `lint` in one pass and points the template contract errors at the template. |
|
|
146
|
+
|
|
147
|
+
Four complementary checks. **Every message has a stable rule id and explains what is wrong, why, and how to fix it**; `npx ts-dom rules` lists all rules.
|
|
148
|
+
|
|
149
|
+
**When the set-up itself may be wrong: `npx ts-dom doctor [dirs...]`.** It checks what a project needs before any of the checks can work: a tsconfig with `strict` (required on TypeScript 5, which leaves it off, and when the installed version cannot be determined; on 6 and 7 it only must not be `false`), the DOM lib, a module resolution that resolves the runtime package the generated files import (not `classic`, which TypeScript 5 also picks when `module` is ES2015 to ESNext and `moduleResolution` is left out), an `include` that covers the template folders, and an `include` and `exclude` that keep `ts-dom-tags.ts` in the program (`tsconfig-misses-tag-map`, an error, unless a file in the program imports or re-exports it, as a component package's entry does; otherwise its tags would be typed as plain `HTMLElement`); the runtime package being installed; templates imported as `./x.html.js` (not `./x.html`); generated files that are stale; the Vite plugin and the oxlint plugin being configured, including a `vitest.config.*` that replaces the Vite config without the plugin (`vite-plugin-missing`); when a formatter such as oxfmt or Prettier is set up, templates generated with whitespace `collapse`, and generated files that were written with `preserve` while the scripts or the Vite config ask for `collapse` (`formatter-whitespace`); a package.json script (with its `pre` script and the scripts it runs through `npm run`) that runs `tsc`, `vue-tsc`, `svelte-check` or `ng build`/`serve`/`test` before any `ts-dom gen` or `ts-dom check` while `.gitignore` leaves out `*.html.ts`, which fails with TS2307 on a clean checkout (`typecheck-before-gen`, skipped for a `tsc -p` whose config covers none of the scanned folders); and an `.html` in a scanned folder that a TypeScript file names as an Angular `templateUrl`, which is a framework template and means the folder should not be scanned (`framework-template`). `.gitignore` files, Skill copies, and formatter and oxlint configuration are read from the folder up to the workspace root, nearest first, and never above the home directory (a `package.json` with `workspaces`, a `pnpm-workspace.yaml`, else the git root), and the oxlint (`.oxlintrc.json`, `.oxlintrc.jsonc`), Prettier (`.prettierrc*`, `prettier.config.*`, the `prettier` key of `package.json`) and oxfmt (`.oxfmtrc.json`, `.oxfmtrc.jsonc`, `oxfmt.config.*`) config files are all recognized. `--project=<path>` picks the tsconfig to check, and with `--strict` a warning also gives exit code 1 (errors always do). Every issue has a stable rule id and a `Fix:` hint, and `--json` prints them as data.
|
|
150
|
+
|
|
151
|
+
**One command for all of them: `npx ts-dom check [dirs...]`.** It reports template diagnostics, regenerates the generated files (with `--no-write` it reports stale ones instead), runs the project's own `tsc` (TypeScript 5, 6 or 7; `--project=` picks the tsconfig, and a solution-style tsconfig, with `"files": []` and `references`, runs `tsc` once per referenced project; when the project's package.json lists `vue-tsc` in dependencies or devDependencies, it runs that instead, so `.vue` files are checked too), and runs the lint rules (`--a11y` adds the accessibility checks). TypeScript errors that come from the template contract point at the template, not at the `Component(...)` call: a `{{ title }}` with nothing named `title` is reported at its line and column in the `.html` as `missing-binding`, and a missing handler as `missing-handler`; item component problems get their own rule ids, and the errors that only follow from a broken class are left out in the broken component's own file, including those inside its body (a `bind` callback's parameter without a type, say), and in `node_modules`; in other files they are kept and their message says which contract error they follow from. A tag declared with two different classes by two tag maps in one program (TS2717 in `ts-dom-tags.ts`) is reported as `duplicate-define`. Without TypeScript installed `tsc-not-found` is an error, so a CI job that omits the dev dependencies fails instead of passing unchecked. An error located in a generated `x.html.ts` or `ts-dom-tags.ts` says to run `ts-dom gen`, or to delete the file when it is left over from a template or folder that is no longer scanned, instead of to edit it. `--json` prints the issues as data, and the exit code is 1 when there is an error (with `--strict`, a warning too).
|
|
152
|
+
|
|
153
|
+
Which command writes files:
|
|
154
|
+
|
|
155
|
+
| Command | Writes | Runs |
|
|
156
|
+
|---|---|---|
|
|
157
|
+
| `ts-dom gen` | the generated `x.html.ts` files and `ts-dom-tags.ts`, and deletes orphaned ones | codegen only |
|
|
158
|
+
| `ts-dom gen --check` | nothing | codegen only: lists missing, outdated or orphaned generated files |
|
|
159
|
+
| `ts-dom check` | the generated files (they are derived and not committed, so this is what a developer or an agent wants after a change) | codegen, `tsc`, lint |
|
|
160
|
+
| `ts-dom check --no-write` | nothing: stale generated files are reported instead of refreshed, and `tsc` runs on the files as they are | codegen, `tsc`, lint |
|
|
161
|
+
|
|
162
|
+
In a CI job, `ts-dom check --no-write` (or `ts-dom gen --check` when you only want to know whether the generated files are current) only works when the generated files are committed: on a clean checkout without them it fails, because every generated file is missing. When they are git-ignored, run `ts-dom check`, then check that the working tree is unchanged (`git status --porcelain` prints nothing).
|
|
163
|
+
|
|
164
|
+
1. **codegen diagnostics** (`ts-dom gen` and the Vite plugin, with line and column numbers; on errors they abort and write no files): duplicate `data-ref`, `data-each` without a single root element, `data-each` not on a `<template>`, nested `data-each`, an incomplete or invalid `data-key` / `data-prop`, a `data-key` list whose item is not one custom element, `data-if` on an item root element, `<slot>` inside an item, `<style>` not at the first level, invalid holes, holes inside attributes, invalid `data-on-*` (including names that collide with DOM or ts-dom members), `<html>`, `<head>` and `<body>` inside a template, and markup that runs code or Trusted Types blocks: `<script>`, `on*` attributes, `srcdoc`, and `javascript:` URLs.
|
|
165
|
+
2. **tsc**: an error when TypeScript uses a name the HTML does not declare (`this.refs.nope`, `this.show("nope", true)`); all constraints between `state`, `props`, `derive`, `emits` and the HTML (see [Data Flow](components.md#data-flow-props-down-events-up)); child component prop names and types; `define()` checks that the methods for `data-on-*` exist.
|
|
166
|
+
3. **Runtime**: `Component()` and components, at definition, connection, and use, throw errors that carry a `[rule id]` and a fix for the same problems.
|
|
167
|
+
4. **lint** (`ts-dom/unused-markup`, `ts-dom/data-flow`, `ts-dom/a11y`): markup declared in the HTML but never used (errors, with the HTML location), data flow suggestions and child component contracts (warnings), ARIA (warnings).
|
|
168
|
+
|
|
169
|
+
Rules of `unused-markup`:
|
|
170
|
+
|
|
171
|
+
| Id | Detection |
|
|
172
|
+
|---|---|
|
|
173
|
+
| `unused-ref` | A `data-ref` is not used by the component |
|
|
174
|
+
| `unused-if` | A `data-if` is never toggled (`data-if` is hidden by default, so the content never appears) |
|
|
175
|
+
| `unused-text` | A `{{ hole }}` is never filled and displays blank |
|
|
176
|
+
| `unused-list` | A `data-each` is never rendered, so the list is always empty |
|
|
177
|
+
| `unused-item-name` | A name inside a list item is never referenced |
|
|
178
|
+
| `missing-handler` | A method referenced by `data-on-*` is not defined by the class |
|
|
179
|
+
| `no-importer` | No TypeScript file imports this template |
|
|
180
|
+
|
|
181
|
+
lint compares per **class body** of `class X extends Component(tpl)`. When the `state` / `props` / `derive` options are used, `{{ }}` and `data-if` are handled by automatic binding and are not required to appear in the code, but the methods referenced by `data-on-*` must exist (if the code uses dynamic access such as `refs[k]`, `text[k]`, or `lists[k]`, it cannot tell which names are used, so it conservatively treats all of them as used). Hence importing multiple templates in the same file does not make them overwrite each other:
|
|
182
|
+
|
|
183
|
+
```ts
|
|
184
|
+
import listTpl from "./list.html.js"; // data-ref="title"
|
|
185
|
+
import cardTpl from "./card.html.js"; // data-ref="title" (Card never uses it)
|
|
186
|
+
class List extends Component(listTpl) { connected() { this.refs.title.hidden = true } }
|
|
187
|
+
class Card extends Component(cardTpl) {} // → reports that title in card.html is unused
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Note: refs, text, and so on should be used by the component's own class. Access through an instance outside the class (for example in tests) is not counted. Turning this rule off for test directories silences only templates that a test file imports itself: a ref that only a test reads is still reported in the component, so remove it and query the DOM in the test. When no corresponding class can be found (for example when `Component` is wrapped by a helper), it falls back to comparing the whole file.
|
|
191
|
+
|
|
192
|
+
## Skills
|
|
193
|
+
|
|
194
|
+
The package ships Skills: task procedures for coding agents (set-up, a complete example, known limitations, verification commands, and the exact versions they were verified with).
|
|
195
|
+
|
|
196
|
+
```sh
|
|
197
|
+
npx ts-dom skills # list them: tier, verified date and versions, stale or not
|
|
198
|
+
npx ts-dom skills --json # the same as data
|
|
199
|
+
npx ts-dom skills nativele-react # print one
|
|
200
|
+
npx ts-dom skills add nativele-core # copy into .claude/skills/ when the project has .claude, otherwise .agents/skills/
|
|
201
|
+
npx ts-dom skills add nativele-core --to=tools/skills
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
A Skill is marked stale when its verification is more than six months old or its range of Nativele versions does not include the installed one. `ts-dom skills add` only copies files; it installs nothing. `ts-dom doctor` reports, as `info` that never changes the exit code, the Skills whose dependencies the project has and that it has no copy of (`skill-available`), and a copy that differs from the one shipped with the installed version (`skill-outdated`; run `ts-dom skills add` again to replace it). The format of a Skill and how to contribute one are in [skills/README.md](../skills/README.md).
|
|
205
|
+
|
|
206
|
+
## ARIA / Accessibility Checks
|
|
207
|
+
|
|
208
|
+
**Off** by default. When enabled, it produces **warnings** (not errors) for places in the template where ARIA or a label is recommended. There are three ways to enable it, all sharing the same checks:
|
|
209
|
+
|
|
210
|
+
```ts
|
|
211
|
+
// 1. lint: shown by both the editor and vp lint, warnings are reported on the import line (the message contains the html line and column)
|
|
212
|
+
lint: { rules: { "ts-dom/a11y": ["warn", { ignore: ["img-alt"] }] } }
|
|
213
|
+
|
|
214
|
+
// 2. build: shown in the terminal during dev / build
|
|
215
|
+
plugins: [tsDom({ a11y: true })] // or { a11y: { ignore: ["img-alt"] } }
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
```sh
|
|
219
|
+
# 3. CLI
|
|
220
|
+
npx ts-dom lint --a11y [--strict]
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
`ignore` takes rule ids and can turn off individual checks:
|
|
224
|
+
|
|
225
|
+
| Id | Check |
|
|
226
|
+
|---|---|
|
|
227
|
+
| `img-alt` | `<img>` needs `alt` (use `alt=""` for decorative images) |
|
|
228
|
+
| `control-name` | `<button>`, `<a href>`, and `<summary>` need a readable name (text, `{{ }}`, `<slot>`, `aria-label`, or `aria-labelledby`) |
|
|
229
|
+
| `form-label` | `<input>`, `<select>`, and `<textarea>` need a `<label for>`, to be wrapped by a `<label>`, or `aria-label` |
|
|
230
|
+
| `iframe-title` | `<iframe>` needs `title` |
|
|
231
|
+
| `dialog-name` | `<dialog>` needs `aria-label` or `aria-labelledby` |
|
|
232
|
+
| `heading-empty` | `h1`–`h6` must not be empty |
|
|
233
|
+
| `aria-role` | `role` must be a valid ARIA role |
|
|
234
|
+
| `aria-attr` | `aria-*` must be a valid ARIA attribute |
|
|
235
|
+
| `role-tabindex` | A non-natively-focusable element with an interactive role (button, tab, checkbox…) needs `tabindex` |
|
|
236
|
+
| `tabindex-positive` | Do not use a `tabindex` greater than 0 |
|
|
237
|
+
| `hidden-focusable` | A focusable element must not have `aria-hidden="true"` |
|
|
238
|
+
| `label-for` | `<label for>` must point to an `id` that exists in the template |
|
|
239
|
+
| `id-in-list` | An `id` inside a `data-each` item is duplicated with each item; use `data-ref` or a class instead |
|
|
240
|
+
| `duplicate-id` | An `id` must not be duplicated within a template |
|
|
241
|
+
| `aria-idref` | The ids referenced by `aria-labelledby`, `describedby`, `controls`, `owns`, etc. must exist in the template (use `ignore` to turn it off when pointing to elements outside the template) |
|
|
242
|
+
| `heading-order` | Heading levels must not skip (`h3` directly after `h1`), judged by document order within the template |
|
|
243
|
+
| `table-header` | `<table>` needs `<th>`; for layout tables set `role="presentation"` |
|
|
244
|
+
| `list-structure` | The children of `<ul>`/`<ol>` (including the root element of a `data-each` item) must be `<li>`; `<li>` must be inside `<ul>`, `<ol>`, or `<menu>` |
|
|
245
|
+
| `landmark-label` | When a template has multiple `<nav>` or `<aside>`, they need to be distinguished with `aria-label` |
|
|
246
|
+
| `redundant-role` | `role` is the same as the element's native semantics, for example `<button role="button">` |
|
|
247
|
+
| `click-semantics` | `data-on-click` should be on an operable element (`button`, `a[href]`, `label`, or an element with an interactive role plus `tabindex`); otherwise keyboards and assistive technologies cannot use it |
|
|
248
|
+
|
|
249
|
+
To avoid many false positives, elements with `data-ref` are treated as "managed by TS", and the checks whose "name or reference may be set in TS" are skipped for them: `img-alt`, `control-name`, `form-label`, `iframe-title`, `dialog-name`, `heading-empty`, `role-tabindex`, `table-header`, `landmark-label`, `aria-idref`.
|
|
250
|
+
|
|
251
|
+
This is a static check that looks only at the HTML template: it does not analyze attributes that TS adds at runtime, and it does not cover items that can only be judged with actual rendering, such as color contrast, focus management, and keyboard interaction, so it cannot replace tools like axe and manual testing.
|
package/llms.txt
CHANGED
|
@@ -79,10 +79,10 @@ define("my-counter", MyCounter);
|
|
|
79
79
|
- `data-each` is never nested: a list inside an item is the item component's own `data-each`.
|
|
80
80
|
|
|
81
81
|
## Rules for agents
|
|
82
|
-
- Skills: `npx ts-dom skills` lists the task procedures shipped with the package (`nativele-core` for building components, `nativele-react`, `nativele-vue`, `nativele-svelte` and `nativele-angular` for those frameworks, `nativele-vite` for Vite, `nativele-integration` for other frameworks, `nativele-state-management`, `nativele-testing`, `nativele-tanstack-query`, `nativele-zod` and `nativele-tailwind` for those tools, `nativele-architecture` for laying out an app with several pages, ...), each with verified versions; `npx ts-dom skills <name>` prints one, `npx ts-dom skills add <name>` copies it into `.claude/skills/` or `.agents/skills/`, and `npx ts-dom doctor` suggests the ones that match the project's dependencies.
|
|
82
|
+
- Skills: `npx ts-dom skills` lists the task procedures shipped with the package (`nativele-core` for building components, `nativele-react`, `nativele-vue`, `nativele-svelte` and `nativele-angular` for those frameworks, `nativele-vite` for Vite, `nativele-integration` for other frameworks, `nativele-state-management`, `nativele-testing`, `nativele-tanstack-query`, `nativele-zod` and `nativele-tailwind` for those tools, `nativele-architecture` for laying out an app with several pages, `nativele-typescript`, `nativele-linting`, `nativele-formatting` and `nativele-ci` for the tsconfig, the linter, the formatter and CI, `nativele-monorepo` for a workspace of several packages, ...), each with verified versions; `npx ts-dom skills <name>` prints one, `npx ts-dom skills add <name>` copies it into `.claude/skills/` or `.agents/skills/`, and `npx ts-dom doctor` suggests the ones that match the project's dependencies.
|
|
83
83
|
- `npx ts-dom rules --explain=<id>` (or `rules --json`) explains any rule id from a diagnostic: why it exists, a wrong example and the fix.
|
|
84
|
-
- Markup well-formedness is a formatter's job: `oxfmt --check` reports misnested tags that the library's parser would silently repair. Generate with `--whitespace=collapse` when a formatter is used.
|
|
85
|
-
- If checks behave oddly (no types, a template not found), run `npx ts-dom doctor --json` first: it reports tsconfig, import path, runtime, generated file and plugin problems with a `Fix:` each.
|
|
84
|
+
- Markup well-formedness is a formatter's job: `oxfmt --check` reports misnested tags that the library's parser would silently repair. Generate with `--whitespace=collapse` when a formatter is used: it keeps only the whitespace the browser renders, judged by each element's default display as formatters judge it, so reformatting a template changes neither the page nor `textContent` (a `<div>` restyled `display: inline` still counts as a block: use an inline tag for inline content). In SVG, only the content of `<text>` is inline (`<tspan>`, `<textPath>`, `<a>` inside it), as the browser lays it out and oxfmt formats it; Prettier formats SVG text content as blocks and puts a line break, which renders as a space, where text touches a `<tspan>`.
|
|
85
|
+
- If checks behave oddly (no types, a template not found), run `npx ts-dom doctor --json` first: it reports tsconfig, import path, runtime, generated file and plugin problems with a `Fix:` each (`--project=<path>` picks the tsconfig, `--strict` also fails on warnings).
|
|
86
86
|
- `npx ts-dom gen --check` (writes nothing, exit 1) tells whether the generated files are stale before you trust `tsc` errors.
|
|
87
87
|
- Never edit `x.html.ts` or `ts-dom-tags.ts` (generated). After any `.html` or component change run `npx ts-dom check --json`: it regenerates, runs tsc, maps template contract errors to the template line/col (`missing-binding`, `missing-handler`, item rules), and runs lint. Fix every issue by its `Fix:` hint; never cast a type error away.
|
|
88
88
|
- No expressions in HTML; computed values go in `derive`. Change state only with `this.set()`. Lists, attributes and focus go in `render(view, changed)`; check `changed.has("items")` before rebuilding a list, and annotate `changed` with the names you check (`ReadonlySet<"items" | "visible">`), not `ReadonlySet<string>`, so tsc checks them.
|
|
@@ -91,7 +91,7 @@ define("my-counter", MyCounter);
|
|
|
91
91
|
## Setup
|
|
92
92
|
```sh
|
|
93
93
|
npm i -D typescript && npm i @nativele/ts-dom
|
|
94
|
-
npx ts-dom gen && npx tsc # tsconfig: module/moduleResolution NodeNext (or bundler), lib includes DOM, strict
|
|
94
|
+
npx ts-dom gen && npx tsc # tsconfig: module/moduleResolution NodeNext (or bundler), lib includes DOM, strict true (required on TypeScript 5; 6 and 7 only reject false)
|
|
95
95
|
```
|
|
96
96
|
In a browser without a bundler map the bare name once: `<script type="importmap">{"imports":{"@nativele/ts-dom":"/node_modules/@nativele/ts-dom/dist/index.mjs"}}</script>`; relative imports in your own code need `.js` extensions. With Vite, `@nativele/ts-dom/vite` is optional.
|
|
97
97
|
|
|
@@ -106,5 +106,5 @@ In a browser without a bundler map the bare name once: `<script type="importmap"
|
|
|
106
106
|
|
|
107
107
|
## Docs
|
|
108
108
|
- [README](README.md): introduction, the short version, rules for agents, quick start. [README.zh-TW.md](README.zh-TW.md): the same in Traditional Chinese.
|
|
109
|
-
- Guides (English): [templates](docs/templates.md), [components and data flow](docs/components.md), [recipes](docs/recipes.md), [lists](docs/lists.md), [architecture](docs/architecture.md), [setup, checks and lint](docs/tooling.md), [performance](docs/performance.md), [design](docs/design.md), [developing Nativele](docs/contributing.md).
|
|
109
|
+
- Guides (English): [templates](docs/templates.md), [components and data flow](docs/components.md), [recipes](docs/recipes.md), [lists](docs/lists.md), [architecture](docs/architecture.md), [setup, checks and lint](docs/tooling.md), [performance](docs/performance.md), [design](docs/design.md), [developing Nativele](docs/contributing.md). The same guides are in the installed package under `node_modules/@nativele/ts-dom/docs/`.
|
|
110
110
|
- `npx ts-dom rules`: every rule id with a description.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nativele/ts-dom",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Designed for AI agents. Driven by TypeScript. Defined in vanilla HTML. A lightweight library for building web components: typed templates, native custom elements, no virtual DOM, no template expressions, no bundler required.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"html",
|
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
"README.md",
|
|
50
50
|
"README.zh-TW.md",
|
|
51
51
|
"llms.txt",
|
|
52
|
+
"docs/*.md",
|
|
52
53
|
"skills/README.md",
|
|
53
54
|
"skills/*/SKILL.md",
|
|
54
55
|
"LICENSE",
|
package/skills/README.md
CHANGED
|
@@ -19,7 +19,7 @@ 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
25
|
│ └── verify.mjs # browser checks run by `npm run verify` (or the project's own tests, see below)
|
|
@@ -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,13 +51,13 @@ 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.
|
|
58
58
|
6. **Verify**: the commands that prove the integration works in the user's project (for example `npx ts-dom check`, `npx tsc --noEmit`, the project's build, a test), with the output to expect.
|
|
59
59
|
7. **Troubleshooting**: symptoms seen while verifying, their cause and fix.
|
|
60
|
-
8. **See also**: links to the guides in `docs/` for the facts used.
|
|
60
|
+
8. **See also**: links to the guides in `docs/` for the facts used, as full GitHub URLs (the same files ship in the package under `node_modules/@nativele/ts-dom/docs/`).
|
|
61
61
|
|
|
62
62
|
Rules for the content:
|
|
63
63
|
|
|
@@ -66,16 +66,28 @@ Rules for the content:
|
|
|
66
66
|
- No promises beyond the verified versions.
|
|
67
67
|
- English, plain and concrete; no restating of `docs/`.
|
|
68
68
|
|
|
69
|
+
## How Skills work together
|
|
70
|
+
|
|
71
|
+
Skills are cut by task intent or technical area (building components, Vue, testing, TypeScript, linting), never by a workflow that strings tools together. Each one owns its area, and several of them meet in one project with no Skill in charge of the others. These rules make that work:
|
|
72
|
+
|
|
73
|
+
1. **Single responsibility.** A Skill covers its own area and does not take over another tool's configuration. When its task needs a change in another area, it states the requirement (for example "the tsconfig that covers the components needs `strict`") and leaves the how to the Skill or the documentation that owns that area.
|
|
74
|
+
2. **Non-destructive configuration.** Before changing a configuration file, read what is there and keep the user's choices. Add or adjust only what the task needs; do not replace a tool, reformat a file or rewrite a configuration unless the task asks for it.
|
|
75
|
+
3. **Context-aware.** Look at what the project already has (installed tools, configuration files, scripts, the runtime and package manager) and act on that. A Skill never assumes a starter project's defaults, and its steps say what to check before what to change.
|
|
76
|
+
4. **Explicit ownership.** Every configuration file, or block in a shared file, has one owning area: `tsconfig*.json` belongs to TypeScript, lint configuration to linting, formatter configuration to formatting, `vite.config.*` to the build. A Skill that must touch a block it does not own follows the rules of the owning Skill or of `docs/` instead of defining its own.
|
|
77
|
+
5. **Composable.** A Skill may point to another Skill or to a guide, but does not copy its knowledge, and never requires a fixed set of Skills to be installed. Each Skill works alone; another Skill only adds to it.
|
|
78
|
+
|
|
79
|
+
When two Skills disagree about one setting, the Skill that owns the setting decides, and the other is fixed to point to it.
|
|
80
|
+
|
|
69
81
|
## Tiers
|
|
70
82
|
|
|
71
83
|
| Tier | Who maintains it | Requirements | Shown by `ts-dom skills` |
|
|
72
84
|
|---|---|---|---|
|
|
73
|
-
| Official | the Nativele maintainers | accepted through an agent trial
|
|
85
|
+
| Official | the Nativele maintainers | accepted through an agent trial and maintained by the maintainers; verified within the last 6 months | as official |
|
|
74
86
|
| 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
87
|
| Experimental | its author | the SKILL.md format; verification not complete | as experimental, with a warning |
|
|
76
88
|
|
|
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
|
|
89
|
+
- 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.
|
|
90
|
+
- 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
91
|
|
|
80
92
|
## Delivering Skills to projects
|
|
81
93
|
|
|
@@ -84,16 +96,15 @@ The library already lives in the user's `node_modules`, so its Skills travel wit
|
|
|
84
96
|
- `ts-dom skills` lists the Skills shipped with the installed version: name, tier, description, verified date and versions, whether it is stale, and its path inside `node_modules`. `--json` prints the same as data for agents.
|
|
85
97
|
- `ts-dom skills <name>` prints that Skill (so an agent can read it without knowing the path).
|
|
86
98
|
- `ts-dom skills add <name...>` copies Skills into the project: into `.claude/skills/<name>/` when the project has a `.claude` folder, otherwise into `.agents/skills/<name>/` (`--to=<dir>` for another folder), where agents that load skills pick them up. It only copies; it never installs the third-party packages a Skill mentions.
|
|
87
|
-
- `ts-dom doctor` reads the project's `package.json` and, for each Skill whose `detects` dependency is present and that the project has no copy of, reports it as an `info` issue (`skill-available`); a copy that differs from the shipped one is reported as `skill-outdated`, for example: `package.json:1:1 info [skill-available] react is a dependency
|
|
99
|
+
- `ts-dom doctor` reads the project's `package.json` and, for each Skill whose `detects` dependency is present and that the project has no copy of, reports it as an `info` issue (`skill-available`); a copy that differs from the shipped one is reported as `skill-outdated`, for example: `package.json:1:1 info [skill-available] react is a dependency, and the nativele-react Skill covers it: <the Skill's description> Fix: run ts-dom skills add nativele-react, or read it with ts-dom skills nativele-react.` Info issues never change the exit code.
|
|
88
100
|
|
|
89
101
|
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
102
|
|
|
91
|
-
##
|
|
103
|
+
## Examples
|
|
104
|
+
|
|
105
|
+
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.
|
|
92
106
|
|
|
93
|
-
|
|
94
|
-
- `nativele-core` has no example of its own: the library's CI, which checks every example in the repository, verifies it.
|
|
95
|
-
- The workflow `.github/workflows/skills.yml` runs `npm run verify` for each Official Skill when an `example/` changes, when the library's runtime changes, and once a month, so a third-party release that breaks a Skill is found. A change to `SKILL.md` runs only `.github/workflows/skills-format.yml`, which checks every `SKILL.md` against this specification. Neither is a required check for `main`.
|
|
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.
|
|
107
|
+
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
108
|
|
|
98
109
|
## Contributing a Skill
|
|
99
110
|
|
|
@@ -117,7 +128,28 @@ The Skill is accepted when:
|
|
|
117
128
|
- the agent followed the Skill's steps as written, and no step was wrong for the stated versions;
|
|
118
129
|
- 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
130
|
|
|
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.
|
|
131
|
+
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:
|
|
132
|
+
|
|
133
|
+
```yaml
|
|
134
|
+
trial:
|
|
135
|
+
skill: nativele-vue
|
|
136
|
+
round: 2 # 1 for the first trial; a new agent each round
|
|
137
|
+
date: 2026-11-05
|
|
138
|
+
nativele: 0.5.4 # the packed version the agent installed
|
|
139
|
+
tools: vue@3.5.44 vite@8.3.6 vue-tsc@3.3.14 typescript@6.0.3
|
|
140
|
+
agent: Claude Code # or Codex, Cursor, ...
|
|
141
|
+
model: <model id>
|
|
142
|
+
start: create-vite vue-ts, built once before Nativele was added
|
|
143
|
+
task: |
|
|
144
|
+
An inventory screen: an item component in a keyed list Vue sorts, a two-way bound input,
|
|
145
|
+
Light DOM and shadow slots with Vue-managed children, a store bound under <KeepAlive>.
|
|
146
|
+
verify: # the Skill's Verify commands, as the maintainer re-ran them
|
|
147
|
+
"npx ts-dom check src/nv": "ts-dom check: no issues"
|
|
148
|
+
"npm run build": "built"
|
|
149
|
+
browser: 22 checks passed, no console errors or Vue warnings
|
|
150
|
+
gaps: [] # each guess or error the Skill did not cover; empty to accept
|
|
151
|
+
result: accepted # or: gaps sent back
|
|
152
|
+
```
|
|
121
153
|
|
|
122
154
|
## Skills
|
|
123
155
|
|
|
@@ -136,4 +168,9 @@ Otherwise the maintainers send the gaps back to the contributor as review commen
|
|
|
136
168
|
| `nativele-zod` | Experimental | available | form validation with Zod or Valibot: per-field messages, nested and array fields, `aria-invalid` |
|
|
137
169
|
| `nativele-tailwind` | Experimental | available | Tailwind CSS v4 in templates: source detection, `shadow: true` with adopted style sheets, `<style scoped>` layers |
|
|
138
170
|
| `nativele-architecture` | Official | available | app layout: feature folders, shared components and stores, pages as custom elements mounted by a router (Navigation API or universal-router), lazy pages, one scanned folder, unit/browser/end-to-end split |
|
|
171
|
+
| `nativele-typescript` | Official | available | the tsconfig covering the components: what Nativele needs and why, stricter options and layouts, the generated files in the type check, framework type checkers |
|
|
172
|
+
| `nativele-linting` | Official | available | the Nativele lint rules in an existing oxlint, Vite+ or ESLint set-up: groups, single rule ids, severities, ignore lists, generated files and test folders |
|
|
173
|
+
| `nativele-formatting` | Official | available | oxfmt, Prettier or `vp fmt` with templates: the whitespace mode, spaces lost to a reformat, keeping the formatter off the generated files |
|
|
174
|
+
| `nativele-ci` | Official | available | the Nativele steps in an existing CI: generating before the type check, committed or git-ignored generated files, exit codes and JSON output, the browser for real-browser tests |
|
|
175
|
+
| `nativele-monorepo` | Official | available | Nativele across the packages of a workspace: a component package and its tag map, where each package generates and checks, dependencies, build order |
|
|
139
176
|
| Other libraries | Experimental or Community | open for contributions | |
|
|
@@ -3,8 +3,8 @@ name: nativele-angular
|
|
|
3
3
|
description: "Use Nativele web components inside an existing Angular 22 app with standalone components: CUSTOM_ELEMENTS_SCHEMA, property and event bindings, change detection, content projection into slots, lifecycle, and generating the templates for the Angular CLI build. Use when a project depends on @angular/core and @nativele/ts-dom."
|
|
4
4
|
metadata:
|
|
5
5
|
tier: experimental
|
|
6
|
-
nativele: ">=0.
|
|
7
|
-
verified: "2026-11-
|
|
6
|
+
nativele: ">=0.6.0 <0.7.0"
|
|
7
|
+
verified: "2026-11-06"
|
|
8
8
|
tools: "@angular/core@22.2.4 @angular/compiler-cli@22.2.4 @angular/build@22.2.6 @angular/cli@22.2.6 typescript@6.0.3 vitest@5.1.0 jsdom@30.1.3"
|
|
9
9
|
author: "Nativele maintainers"
|
|
10
10
|
detects: "@angular/core"
|
|
@@ -25,29 +25,30 @@ 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.
|
|
28
|
+
| @nativele/ts-dom | 0.6.0 (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 |
|
|
32
32
|
| vitest (through `ng test`) | 5.1.0 |
|
|
33
33
|
| jsdom | 30.1.3 |
|
|
34
34
|
|
|
35
|
-
Verified on 2026-11-
|
|
35
|
+
Verified on 2026-11-06 in Google Chrome 148, against `ng serve` (development and production configurations), in an app made with `npx @angular/cli@latest new <name> --defaults --skip-git`. That app is zoneless (no `zone.js`); the same checks also passed with `zone.js` 0.16.3 and `provideZoneChangeDetection()`. With a different major version of Angular, treat every step as unverified.
|
|
36
36
|
|
|
37
37
|
## Set-up
|
|
38
38
|
|
|
39
39
|
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`.
|
|
40
40
|
2. Install: `npm i @nativele/ts-dom`.
|
|
41
|
-
3.
|
|
41
|
+
3. The tsconfig that covers `src/nv` must meet Nativele's requirements (`tsconfig.app.json` and `tsconfig.spec.json` extend the root `tsconfig.json`). Check and adjust it with the TypeScript Skill (`npx ts-dom skills nativele-typescript`). The Angular 22 CLI template leaves `strict` out, which is on by default in TypeScript 6 (the version this Skill was verified with), so doctor reports `tsconfig-not-strict` only for an explicit `"strict": false`; on TypeScript 5 it must be set.
|
|
42
42
|
4. Generate the template modules before every Angular command. The Angular CLI build (`@angular/build:application`) takes no Vite plugins from the project, so `tsDom()` cannot be added; run `ts-dom gen` from npm's `pre` scripts instead:
|
|
43
43
|
|
|
44
44
|
```sh
|
|
45
45
|
npm pkg set scripts.prestart="ts-dom gen src/nv --whitespace=collapse" \
|
|
46
46
|
scripts.prebuild="ts-dom gen src/nv --whitespace=collapse" \
|
|
47
|
-
scripts.pretest="ts-dom gen src/nv --whitespace=collapse"
|
|
47
|
+
scripts.pretest="ts-dom gen src/nv --whitespace=collapse" \
|
|
48
|
+
scripts.prewatch="ts-dom gen src/nv --whitespace=collapse"
|
|
48
49
|
```
|
|
49
50
|
|
|
50
|
-
`npm start`, `npm run build` and `npm
|
|
51
|
+
`npm start`, `npm run build`, `npm test` and the template's `npm run watch` then generate first (`npx ng ...` called directly does not, so a CI job runs these scripts or `ts-dom gen` first; see `npx ts-dom skills nativele-ci`). The CLI template sets up Prettier, and with a formatter the whitespace mode is decided by the formatting Skill (`npx ts-dom skills nativele-formatting`), which asks for `collapse`; every script that generates passes the same flag. Write test expectations from the collapsed text (rules in `nativele-formatting`): with label `x`, count 1 and an item named `A1:new`, `info-card`'s `textContent` reads `x 1 A1:new Pick`. Inline elements keep one space between them, while block elements (`<p>`, `<ul>`, `<li>`) drop the whitespace around them: a card whose items are `<li>` elements reads `Fruit 1applepearPick`.
|
|
51
52
|
|
|
52
53
|
While `npm start` runs, keep a second terminal with `npx ts-dom gen src/nv --watch --whitespace=collapse`: editing a template or adding a component regenerates its module, and `ng serve` rebuilds and reloads the page.
|
|
53
54
|
|
|
@@ -296,7 +297,7 @@ npm test -- --watch=false # expect: the specs pass
|
|
|
296
297
|
```
|
|
297
298
|
|
|
298
299
|
- `ts-dom check` runs `tsc` on the app's TypeScript through the references of the root `tsconfig.json`, so it reports a type error in `src/app/app.ts` too. It does not read Angular templates. `ngc` (or the build) is what type-checks the Angular templates, including the `EmitEvent` casts.
|
|
299
|
-
- Run `ts-dom check` with `--whitespace=collapse` or after `gen`:
|
|
300
|
+
- Run `ts-dom check` with `--whitespace=collapse` or after `gen`: on a clean checkout it would write the files with the other mode (`nativele-formatting`; for CI, `nativele-ci`).
|
|
300
301
|
|
|
301
302
|
In the browser, against `npm start` (the development server shows Angular's warnings in the console), check:
|
|
302
303
|
|
|
@@ -322,9 +323,9 @@ With Playwright, wait for each expected change instead of reading the DOM right
|
|
|
322
323
|
| The element stays empty | its module is not imported | import it in `src/main.ts` (step 7) |
|
|
323
324
|
| A slotted child shows in the wrong slot or the slot fallback stays visible | `@if`/`@for` directly inside a Light DOM component with slots | `shadow: true`, or a stable element per slot |
|
|
324
325
|
| `ts-dom check` reports `invalid-hole` or `no-importer` in `src/app/app.html` | the command ran without a folder and read Angular templates | pass the Nativele folder: `ts-dom check src/nv` |
|
|
325
|
-
| `ts-dom doctor` warns `tsconfig-not-strict` |
|
|
326
|
-
| `ts-dom doctor` warns `formatter-whitespace` | Prettier is set up and no script generates with `--whitespace=collapse` | step 4 |
|
|
327
|
-
| `Property 'info-card' does not exist on type 'HTMLElementTagNameMap'` in a spec | `tsconfig.spec.json` does not include `ts-dom-tags.ts` | import the component class type instead |
|
|
326
|
+
| `ts-dom doctor` warns `tsconfig-not-strict` | `"strict": false` in the config (an absent `strict` is on with TypeScript 6; TypeScript 5 needs it set) | step 3 (`nativele-typescript`) |
|
|
327
|
+
| `ts-dom doctor` warns `formatter-whitespace` | Prettier is set up and no script generates with `--whitespace=collapse` | step 4 (`nativele-formatting`) |
|
|
328
|
+
| `Property 'info-card' does not exist on type 'HTMLElementTagNameMap'` in a spec | `tsconfig.spec.json` does not include `ts-dom-tags.ts` | import the component class type instead; whether the spec tsconfig should cover the Nativele folder is for `nativele-typescript` |
|
|
328
329
|
| The Angular view does not update after a store change or a timer | zoneless change detection does not run for plain fields changed outside template listeners | keep that value in a signal |
|
|
329
330
|
|
|
330
331
|
## See also
|
|
@@ -334,4 +335,7 @@ With Playwright, wait for each expected change instead of reading the DOM right
|
|
|
334
335
|
- Lifecycle and `bind`: https://github.com/Pixmi/nativele/blob/main/docs/components.md#components
|
|
335
336
|
- Stores: https://github.com/Pixmi/nativele/blob/main/docs/components.md#state-store-when-not-using-state
|
|
336
337
|
- Setup, `gen --watch`, whitespace and checks: https://github.com/Pixmi/nativele/blob/main/docs/tooling.md
|
|
338
|
+
- The tsconfig and `ngc`'s TypeScript version: https://github.com/Pixmi/nativele/blob/main/skills/nativele-typescript/SKILL.md
|
|
339
|
+
- The whitespace mode with Prettier: https://github.com/Pixmi/nativele/blob/main/skills/nativele-formatting/SKILL.md
|
|
340
|
+
- CI: https://github.com/Pixmi/nativele/blob/main/skills/nativele-ci/SKILL.md
|
|
337
341
|
- A framework integration with typed templates: `npx ts-dom skills nativele-react`
|