@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
package/CHANGELOG.md CHANGED
@@ -10,6 +10,66 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Version
10
10
  - Adding a lint rule is a minor change; lint rules are warnings or opt-in and do not count as breaking.
11
11
  - Before every release run `npm run verify:pack` (`publint` and `attw`), `tsc`, the unit tests, the browser tests, `npm run smoke` and `npm run smoke:no-bundler`; all of them run in `.github/workflows/ci.yml`.
12
12
 
13
+ ## 0.6.0
14
+
15
+ Five new Official Skills cover the technical areas every Nativele project shares, and the Skills now work together by clear ownership: each configuration area has one owning Skill, and the others state what they need and point to it.
16
+
17
+ ### Changed
18
+
19
+ - Whitespace `collapse` keeps the whitespace the browser renders, so a formatter's line breaks and indentation no longer change the text on the page. It follows the default display of each element, as Prettier and oxfmt assume it. The generated data of existing `collapse` projects changes:
20
+ - A run of whitespace between two inline-level siblings (text, phrasing elements such as `<a>`, `<b>`, `<span>`, `<time>`, inline-blocks such as `<button>` and `<input>`, custom and unknown elements) becomes one space even when it contains a line break; it used to be dropped. `<a>{{ author }}</a>\n <time>{{ date }}</time>` renders `ann 2 days ago` as the browser does, not `ann2 days ago`. The same holds at the start and end of an inline element's content (`<a>\n <x-icon></x-icon>\n Label\n</a>`), so `childNodes` and `textContent` gain those spaces, and a space now separates custom elements written on separate lines.
21
+ - Whitespace next to a block-level element (`<div>`, `<p>`, `<section>`, `<ul>`, `<li>`, headings, table parts, ...) and at the start and end of a block or inline-block element's content is dropped, with or without a line break: `<button>\n Toggle\n</button>` has the text `"Toggle"`, no longer `" Toggle "`, and `<div>a</div> <div>b</div>` no longer has a `" "` between the blocks.
22
+ - Only HTML whitespace (space, tab, line feed, form feed, carriage return) is collapsed: a non-breaking space (`&nbsp;`) is kept; it used to become a plain space.
23
+ - `&#32;` between two inline elements is no longer needed to keep a space a formatter turns into a line break (it still works).
24
+ - In SVG, whitespace between elements is dropped, and the content of a `<text>` follows the inline rule: `<tspan>`, `<textPath>` and `<a>` inside it are inline, as the browser lays them out, so `<text>Hello <tspan>{{ name }}</tspan> <tspan>b</tspan></text>` keeps both spaces. oxfmt formats SVG text content the same way; Prettier formats it as blocks and puts a line break, which renders as a space, where text touches a `<tspan>`.
25
+ - `<pre>`, `<textarea>`, `<style>`, `<script>` and `preserve` are unchanged.
26
+ - `tsc-not-found` is an error instead of a warning, so `ts-dom check` exits with 1 when TypeScript is not installed (for example in a CI job that omits the dev dependencies) instead of passing without a type check.
27
+
28
+ ### New checks
29
+
30
+ - `ts-dom doctor` reports `tsconfig-misses-tag-map` (error): the tsconfig `include` does not match a scanned folder's `ts-dom-tags.ts` (for example `"include": ["src/**/*.tsx"]`), or an `exclude` pattern removes it. Nothing imports the tag map, so it is in the program only through `include`; left out, tags are typed as plain `HTMLElement` and lists of item components fail with `item-not-in-tag-map`. Excluding the generated `x.html.ts` files is not reported: their components import them. Nor is a tag map that a TypeScript file in the program or in the scanned folder imports, re-exports or references through a relative path (a package entry with `export type {} from "./ts-dom-tags.js"`), which brings it into the program.
31
+ - `tsconfig-classic-resolution` also reports TypeScript 5's implicit classic resolution: `module` ES2015 to ESNext (or `amd`, `umd`, `system`, `none`), or `target` ES2015 or later without `module`, and no `moduleResolution`.
32
+ - `ts-dom doctor` accepts `--project=<path>`, like `check`, and `--strict`, which exits with 1 on warnings (info never changes the exit code).
33
+
34
+ ### Fixes
35
+
36
+ - The generated `x.html.ts` files write an empty `refs` or `lists` as `Record<never, never>` instead of `{}`, so typescript-eslint's `recommended` config (`@typescript-eslint/no-empty-object-type`) reports nothing on them. The types mean the same to `tsc`.
37
+ - `tsconfig-not-strict` follows the project's TypeScript (resolved from the project, with `strict` read through `extends`): TypeScript 6 and 7 turn `strict` on when it is left out, so only `"strict": false`, or TypeScript 5 without `strict`, is reported; when the project's TypeScript cannot be resolved, a missing `strict` is reported with a message that says the version is unknown and TypeScript 5 leaves it off. The message says what is lost (possibly null values, implicit `any`); the names in templates are checked either way.
38
+ - `tsconfig-classic-resolution` says what fails: classic cannot resolve the runtime package that every generated file imports (TS2792) on TypeScript 5; TypeScript 6 deprecates it (TS5107) and 7 removes it (TS5108), and both stop before checking anything.
39
+ - `duplicate-define` says what happens at runtime: `define()` skips a tag that is already registered, so the class whose `define()` runs first wins and the other is dropped without an error.
40
+ - `ts-dom check` reports TS2717 in a generated `ts-dom-tags.ts` (one tag declared with different classes by two tag maps in one program, for example a folder scanned separately or a package) as `duplicate-define` with a rename fix, instead of asking to run `ts-dom gen`.
41
+ - The `item-not-in-tag-map` fix also names a tsconfig `include` or `exclude` that leaves `ts-dom-tags.ts` out of the program.
42
+ - `ts-dom check` keeps the `tsc` errors in other files that follow from a component's contract error (for example appending the broken component: `'HelloCard' is not assignable to ... 'string | Node'`), and each says which error it follows from and where. In the component's own file they are still left out. An error follows from a broken class only when a type quoted in its message is the class (`'HelloCard'`, `'typeof HelloCard'`, or a member of a union or intersection such as `'HelloCard & { extra: number; }'`), so with a broken class named `Index` or `Task`, `Index signature for type ...` or `'Task["status"]'` stay ordinary errors with their own `Fix:`.
43
+ - `ts-dom doctor` detects `.oxlintrc.jsonc` and `oxlint.config.ts` for `oxlint-plugin-missing`, and every Prettier config file (`.prettierrc.yaml`, `.yml`, `.json5`, `.toml`, `.prettierrc.*js`/`*ts`, `prettier.config.*`), the `prettier` key in package.json and `oxfmt.config.*` for `formatter-whitespace`.
44
+ - In a package of a workspace, `ts-dom doctor` reads the `.gitignore` files and the installed Skill copies of the folders up to the workspace root (the nearest folder with a package.json `workspaces` field or a `pnpm-workspace.yaml`, else the git root), so `typecheck-before-gen` sees a root `.gitignore` and `skill-available` is not repeated for Skills installed at the root. The formatter set-up for `formatter-whitespace` (Prettier and oxfmt config files, the `prettier` key, the `prettier` and `oxfmt` dependencies and scripts in package.json, `.prettierignore`) and the lint config for `oxlint-plugin-missing` are looked up the same way, nearest first, and the issue names the file found. The search stops at the home directory, which is read only when doctor runs in it, so a home folder kept in git (dotfiles) is not taken for the root of the projects inside it.
45
+ - `unknown-prop`, `invalid-prop-value` and `unknown-event` check a child tag against the definition that applies to the template: when several folders define the same tag (separate apps or packages in one working directory, or several folders given to `ts-dom lint`), the oxlint plugin and `ts-dom lint` use the one in the template's own package (nearest package.json), and check nothing for that tag when that is not exactly one, instead of using whichever definition was scanned last.
46
+ - `ts-dom doctor` reads `include`, `exclude` and `files` through `extends` as tsc does: each is taken from the nearest config that sets it, and `include` defaults to `**/*` only when the whole chain sets neither `include` nor `files`. With an array of bases where one sets `include` and a later one `files`, the include is no longer dropped, so `tsconfig-misses-templates` and `tsconfig-misses-tag-map` see the files tsc checks.
47
+ - `ts-dom rules` no longer says that `no-importer` can be enabled as `ts-dom/no-importer`: the oxlint plugin sees one file at a time and cannot tell that no file imports a template, so only `ts-dom lint` reports it.
48
+
49
+ ### New Skills
50
+
51
+ - `nativele-typescript`: checks and adjusts the tsconfig that covers the components: what Nativele needs and why (`DOM`, a module resolution that finds packages, an `include` that brings in `ts-dom-tags.ts`, `strict`), the stricter options that work and the one that does not (`isolatedDeclarations`), how `ts-dom check` and `ts-dom doctor` choose configs, and what framework type checkers need; verified with TypeScript 7, 6 and 5.
52
+ - `nativele-linting`: brings the Nativele lint rules into the project's existing oxlint, Vite+ (`vp lint`) or ESLint flat config: groups, single rule ids, severities and ignore lists, what runs in the linter and what only in `ts-dom lint` and `ts-dom check`, generated files and test folders.
53
+ - `nativele-formatting`: keeps oxfmt, Prettier or `vp fmt` and the templates consistent: what reformatting does to the rendered spacing in each whitespace mode, why `collapse` is the mode with a formatter, how to check that a reformat changed no text, and keeping the formatter off the generated files.
54
+ - `nativele-ci`: the Nativele steps in an existing CI: generating before any type check, `ts-dom check` when the generated files are git-ignored and `gen --check` plus `check --no-write` when they are committed, which exit codes fail the job, JSON output, and the browser real-browser tests need.
55
+ - `nativele-monorepo`: Nativele across the packages of a workspace (npm or pnpm, optionally turbo): a component package that generates its own files and exports its tag map, an app that imports it, where `@nativele/ts-dom` and `typescript` are declared, and the order in which packages generate or build.
56
+
57
+ ### Skills
58
+
59
+ - `skills/README.md` has "How Skills work together": a Skill owns one task or technical area, states what it needs from another area instead of configuring it, keeps the user's existing configuration, acts on what the project has, and works without any other Skill installed. When two Skills disagree about one setting, the Skill that owns the setting decides.
60
+ - The existing Skills no longer give their own instructions for the tsconfig, the linter, the formatter, CI or a workspace: each states its requirement in one sentence and points to the owning Skill, and keeps its own verified facts (framework behavior, the framework's type checker, the errors it hit). The Vite Skill now says that with a formatter the plugin and the scripts use the whitespace mode the formatting Skill decides, and the testing Skill notes that a formatter's whitespace can reach `textContent`.
61
+ - The Skills state no verification process and name no file a reader cannot open; `nativele-zod` names the Zod and Valibot versions it was verified with, `nativele-monorepo` gives `tsc-not-found` as the error it is, `nativele-linting` leaves the build script to the build owner, and `nativele-angular` also generates before the template's `watch` script.
62
+ - `nativele-core` gives the `fill` callback of a list of plain elements (`text`, `refs`, `show`, `el`, and `hit.key` in an item's event handler), points Vite set-up to `nativele-vite`, whose build script generates before `tsc`; `nativele-monorepo` says that the tag prefix is for new tags while a moved component keeps its tag.
63
+ - The guides in `docs/` ship in the package, so an agent without network reads them under `node_modules/@nativele/ts-dom/docs/`.
64
+
65
+ ### Guides
66
+
67
+ - `ts-dom check --no-write` in CI only works when the generated files are committed; with git-ignored files, run `ts-dom check` and then check that the working tree is unchanged.
68
+ - The lint `ignorePatterns` example also lists `**/ts-dom-tags.ts`, and the lint guide says that the oxlint plugin also loads in ESLint flat config (ESLint 9 and 10, with typescript-eslint's parser), and that ESLint needs the generated files in its ignores because it does not read `.gitignore`.
69
+ - Turning `unused-markup` off for test directories only silences templates a test file imports itself; a ref that only a test reads is still reported in the component.
70
+ - Scanning several folders uses two folders of one package as its example: scanning another package's folder writes relative imports into its files.
71
+ - A list of item components defined outside the scanned directories rejects items typed by an interface (`Index signature for type 'string' is missing`); the lists guide said it accepted any item with the key field.
72
+
13
73
  ## 0.5.5
14
74
 
15
75
  Skills are verified by agent trials and stand on their own: each Skill can be followed and is accurate without its example project, which stays in the repository only as a reference.
package/README.md CHANGED
@@ -73,6 +73,11 @@ npx ts-dom skills add nativele-core # copy it into .claude/skills/ (when the
73
73
  | `nativele-tanstack-query` | data fetching with `@tanstack/query-core` (Experimental) |
74
74
  | `nativele-zod` | form validation with Zod or Valibot (Experimental) |
75
75
  | `nativele-tailwind` | Tailwind CSS in templates and Shadow DOM (Experimental) |
76
+ | `nativele-typescript` | the tsconfig: what Nativele needs, stricter options, the generated files in the type check |
77
+ | `nativele-linting` | the Nativele lint rules in oxlint, Vite+ or ESLint: rules, severities, ignore lists |
78
+ | `nativele-formatting` | oxfmt, Prettier or `vp fmt` with templates: the whitespace mode, keeping the formatter off the generated files |
79
+ | `nativele-ci` | the Nativele steps in CI: generating first, committed or ignored generated files, exit codes |
80
+ | `nativele-monorepo` | Nativele across the packages of a workspace: a shared component package, build order |
76
81
 
77
82
  Tiers: Official Skills are verified through agent trials and maintained by the maintainers; Community Skills are contributed and show their author and last verified versions; Experimental Skills are not fully verified yet. `ts-dom skills` marks a Skill as stale when its verification is more than six months old or does not cover the installed version.
78
83
 
package/README.zh-TW.md CHANGED
@@ -73,6 +73,11 @@ npx ts-dom skills add nativele-core # 複製到 .claude/skills/(專案有 .
73
73
  | `nativele-tanstack-query` | 用 `@tanstack/query-core` 取資料(Experimental) |
74
74
  | `nativele-zod` | 用 Zod 或 Valibot 驗證表單(Experimental) |
75
75
  | `nativele-tailwind` | 在範本與 Shadow DOM 中使用 Tailwind CSS(Experimental) |
76
+ | `nativele-typescript` | tsconfig:Nativele 需要什麼、更嚴格的選項、型別檢查如何納入產生的檔案 |
77
+ | `nativele-linting` | 在 oxlint、Vite+ 或 ESLint 中使用 Nativele 的 lint 規則:規則、嚴重度、忽略清單 |
78
+ | `nativele-formatting` | 以 oxfmt、Prettier 或 `vp fmt` 格式化範本:空白模式、讓格式化工具避開產生的檔案 |
79
+ | `nativele-ci` | CI 中的 Nativele 步驟:先產生檔案、產生的檔案提交或忽略、結束碼 |
80
+ | `nativele-monorepo` | 在 workspace 的多個套件間使用 Nativele:共用的元件套件、建置順序 |
76
81
 
77
82
  等級:Official 經 agent 實際驗收、由維護者維護;Community 由社群貢獻,標示作者與最後驗證版本;Experimental 尚未充分驗證。驗證超過六個月,或不涵蓋已安裝的版本時,`ts-dom skills` 會標示為可能過期。
78
83
 
@@ -217,7 +217,9 @@ const union = (xs) => {
217
217
  const arr = [...new Set(xs)];
218
218
  return arr.length ? arr.map(q).join(" | ") : "never";
219
219
  };
220
- const obj = (m) => `{ ${[...m].map(([n, t]) => `${q(n)}: ${t}`).join("; ")} }`;
220
+ const EMPTY_OBJECT = "Record<never, never>";
221
+ const members = (entries) => entries.length ? `{ ${entries.join("; ")} }` : EMPTY_OBJECT;
222
+ const obj = (m) => members([...m].map(([n, t]) => `${q(n)}: ${t}`));
221
223
  /** The type of the template's only top-level element (a `<style>` does not count); `never` when there is not exactly one, or other text sits beside it. */
222
224
  function rootElementType(html) {
223
225
  const tops = parseFragment(html).childNodes.filter((n) => n.nodeName !== "#comment" && !(n.nodeName === "#text" && !n.value.trim()));
@@ -261,28 +263,118 @@ const KEEPS_WHITESPACE = /* @__PURE__ */ new Set([
261
263
  "style",
262
264
  "script"
263
265
  ]);
264
- function toNodes(parent, collapse = false) {
265
- const children = "content" in parent && parent.nodeName === "template" ? parent.content.childNodes : parent.childNodes;
266
+ const SVG_NS = "http://www.w3.org/2000/svg";
267
+ const BLOCK_LEVEL = /* @__PURE__ */ new Set([
268
+ "address",
269
+ "article",
270
+ "aside",
271
+ "blockquote",
272
+ "body",
273
+ "caption",
274
+ "center",
275
+ "col",
276
+ "colgroup",
277
+ "dd",
278
+ "details",
279
+ "dialog",
280
+ "dir",
281
+ "div",
282
+ "dl",
283
+ "dt",
284
+ "fieldset",
285
+ "figcaption",
286
+ "figure",
287
+ "footer",
288
+ "form",
289
+ "h1",
290
+ "h2",
291
+ "h3",
292
+ "h4",
293
+ "h5",
294
+ "h6",
295
+ "header",
296
+ "hgroup",
297
+ "hr",
298
+ "html",
299
+ "legend",
300
+ "li",
301
+ "listing",
302
+ "main",
303
+ "menu",
304
+ "nav",
305
+ "ol",
306
+ "optgroup",
307
+ "option",
308
+ "p",
309
+ "param",
310
+ "plaintext",
311
+ "pre",
312
+ "script",
313
+ "search",
314
+ "section",
315
+ "source",
316
+ "style",
317
+ "summary",
318
+ "table",
319
+ "tbody",
320
+ "td",
321
+ "tfoot",
322
+ "th",
323
+ "thead",
324
+ "tr",
325
+ "track",
326
+ "ul",
327
+ "xmp"
328
+ ]);
329
+ const INLINE_BLOCK = /* @__PURE__ */ new Set([
330
+ "audio",
331
+ "button",
332
+ "input",
333
+ "marquee",
334
+ "meter",
335
+ "object",
336
+ "progress",
337
+ "select",
338
+ "video"
339
+ ]);
340
+ const HTML_WHITESPACE = /[ \t\n\f\r]+/g;
341
+ const inSvgText = (n) => {
342
+ for (let p = n.parentNode; p && isEl$1(p) && p.namespaceURI === SVG_NS; p = p.parentNode) if (p.tagName === "text") return true;
343
+ return false;
344
+ };
345
+ const isBlockLevel = (n) => !!n && isEl$1(n) && (n.namespaceURI === SVG_NS ? n.tagName !== "svg" && !inSvgText(n) : n.namespaceURI === HTML_NS && BLOCK_LEVEL.has(n.tagName));
346
+ const trimsEdges = (n) => isBlockLevel(n) || (n.namespaceURI === SVG_NS ? n.tagName === "svg" : n.namespaceURI === HTML_NS && INLINE_BLOCK.has(n.tagName));
347
+ /** In `collapse`, `edges` says whether whitespace at the start and end of `parent`'s content is not rendered. */
348
+ function toNodes(parent, collapse = false, edges = true) {
349
+ const children = ("content" in parent && parent.nodeName === "template" ? parent.content.childNodes : parent.childNodes).filter((n) => n.nodeName === "#text" || n.nodeName === "#comment" || isEl$1(n));
266
350
  const out = [];
267
- for (const n of children) if (n.nodeName === "#text") {
268
- const value = n.value;
269
- if (!collapse) out.push(value);
270
- else if (!(/^\s*$/.test(value) && value.includes("\n"))) out.push(value.replace(/\s+/g, " "));
271
- } else if (n.nodeName === "#comment") out.push(["#comment", n.data]);
272
- else if (isEl$1(n)) {
273
- const attrs = Object.fromEntries(n.attrs.map((a) => [a.name, a.value]));
274
- const inner = toNodes(n, collapse && !KEEPS_WHITESPACE.has(n.tagName));
275
- out.push(n.namespaceURI === HTML_NS ? [
276
- n.tagName,
277
- attrs,
278
- inner
279
- ] : [
280
- n.tagName,
281
- attrs,
282
- inner,
283
- n.namespaceURI
284
- ]);
285
- }
351
+ children.forEach((n, i) => {
352
+ if (n.nodeName === "#text") {
353
+ let value = n.value;
354
+ if (collapse) {
355
+ const prev = children.slice(0, i).reverse().find((c) => c.nodeName !== "#comment");
356
+ const next = children.slice(i + 1).find((c) => c.nodeName !== "#comment");
357
+ value = value.replace(HTML_WHITESPACE, " ");
358
+ if (prev ? isBlockLevel(prev) : edges) value = value.replace(/^ /, "");
359
+ if (next ? isBlockLevel(next) : edges) value = value.replace(/ $/, "");
360
+ }
361
+ if (value) out.push(value);
362
+ } else if (n.nodeName === "#comment") out.push(["#comment", n.data]);
363
+ else if (isEl$1(n)) {
364
+ const attrs = Object.fromEntries(n.attrs.map((a) => [a.name, a.value]));
365
+ const inner = toNodes(n, collapse && !KEEPS_WHITESPACE.has(n.tagName), n.tagName === "template" || trimsEdges(n));
366
+ out.push(n.namespaceURI === HTML_NS ? [
367
+ n.tagName,
368
+ attrs,
369
+ inner
370
+ ] : [
371
+ n.tagName,
372
+ attrs,
373
+ inner,
374
+ n.namespaceURI
375
+ ]);
376
+ }
377
+ });
286
378
  return out;
287
379
  }
288
380
  /**
@@ -299,10 +391,10 @@ function generateModule(html, options = {}, imports = [], defs = []) {
299
391
  const source = !def ? "external" : def.exported ? "local" : "unexported";
300
392
  const item = bound ? `; item: { tag: ${q(bound.tag)}; prop: ${q(bound.prop)}; key: ${q(bound.key)}; source: ${q(source)} }` : "";
301
393
  return `${q(n)}: { refs: ${obj(s.refs)}; text: ${union(s.text)}; ifs: ${union(s.ifs)}${item} }`;
302
- }).join("; ");
394
+ });
303
395
  const type = `{
304
396
  refs: ${obj(a.scope.refs)};
305
- lists: { ${lists} };
397
+ lists: ${members(lists)};
306
398
  ifs: ${union(a.scope.ifs)};
307
399
  slots: ${union(a.slots)};
308
400
  rootElement: ${rootElementType(html)};
@@ -461,7 +553,7 @@ function crossDirDuplicates(dirs) {
461
553
  col: second.col ?? 1,
462
554
  severity: "error",
463
555
  rule: "duplicate-define",
464
- message: `The tag <${tag}> is defined more than once in the scanned folders (${at(first)} and ${at(second)}), so the second define() fails at runtime. Fix: each custom element name can be defined only once; rename one of them.`
556
+ message: `The tag <${tag}> is defined more than once in the scanned folders (${at(first)} and ${at(second)}), so at runtime only the class whose define() runs first is registered, and the other define() is skipped without an error. Fix: each custom element name can be defined only once; rename one of them.`
465
557
  });
466
558
  }
467
559
  return out;
@@ -499,7 +591,7 @@ function renderTags(dir) {
499
591
  const seen = /* @__PURE__ */ new Map();
500
592
  for (const d of defs) {
501
593
  const prev = seen.get(d.tag);
502
- if (prev && (prev.from !== d.from || prev.className !== d.className)) throw new CodegenError(`${dir}: [duplicate-define] the tag <${d.tag}> is defined more than once (${prev.from} and ${d.from}). Fix: each custom element name can be defined only once; rename one of them.`);
594
+ if (prev && (prev.from !== d.from || prev.className !== d.className)) throw new CodegenError(`${dir}: [duplicate-define] the tag <${d.tag}> is defined more than once (${prev.from} and ${d.from}), so at runtime only the class whose define() runs first is registered, and the other define() is skipped without an error. Fix: each custom element name can be defined only once; rename one of them.`);
503
595
  seen.set(d.tag, d);
504
596
  }
505
597
  if (!defs.length) return {
@@ -860,4 +952,4 @@ function checkA11y(html, ignore = []) {
860
952
  for (const { id, el } of labelFor) if (!ids.has(id)) report("label-for", el, `<label for="${id}"> points to an id that does not exist in this template. Fix: add id="${id}" to the control, or wrap the control in the label.`);
861
953
  return out.sort((a, b) => a.line - b.line || a.col - b.col);
862
954
  }
863
- export { staleGenerated as _, analyze as a, formatDiagnostics as c, isFullPage as d, isSelected as f, scanDefines as g, renderTags as h, CodegenError as i, generateDirs as l, listTs as m, checkA11y as n, checkGenerated as o, listHtml as p, CODEGEN_RULES as r, crossDirDuplicates as s, A11Y_RULES as t, generateFile as u, watchAll as v };
955
+ export { scanTags as _, analyze as a, formatDiagnostics as c, isFullPage as d, isSelected as f, scanDefines as g, renderTags as h, CodegenError as i, generateDirs as l, listTs as m, checkA11y as n, checkGenerated as o, listHtml as p, CODEGEN_RULES as r, crossDirDuplicates as s, A11Y_RULES as t, generateFile as u, staleGenerated as v, watchAll as y };