uidex 0.5.2 → 0.7.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 (39) hide show
  1. package/README.md +3 -3
  2. package/dist/cli/cli.cjs +1542 -1227
  3. package/dist/cli/cli.cjs.map +1 -1
  4. package/dist/cloud/index.cjs +385 -175
  5. package/dist/cloud/index.cjs.map +1 -1
  6. package/dist/cloud/index.d.cts +192 -4
  7. package/dist/cloud/index.d.ts +192 -4
  8. package/dist/cloud/index.js +377 -177
  9. package/dist/cloud/index.js.map +1 -1
  10. package/dist/headless/index.cjs +116 -251
  11. package/dist/headless/index.cjs.map +1 -1
  12. package/dist/headless/index.d.cts +6 -11
  13. package/dist/headless/index.d.ts +6 -11
  14. package/dist/headless/index.js +116 -253
  15. package/dist/headless/index.js.map +1 -1
  16. package/dist/index.cjs +776 -1055
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.cts +152 -160
  19. package/dist/index.d.ts +152 -160
  20. package/dist/index.js +792 -1066
  21. package/dist/index.js.map +1 -1
  22. package/dist/react/index.cjs +801 -1019
  23. package/dist/react/index.cjs.map +1 -1
  24. package/dist/react/index.d.cts +102 -86
  25. package/dist/react/index.d.ts +102 -86
  26. package/dist/react/index.js +821 -1038
  27. package/dist/react/index.js.map +1 -1
  28. package/dist/scan/index.cjs +1550 -1220
  29. package/dist/scan/index.cjs.map +1 -1
  30. package/dist/scan/index.d.cts +210 -12
  31. package/dist/scan/index.d.ts +210 -12
  32. package/dist/scan/index.js +1547 -1219
  33. package/dist/scan/index.js.map +1 -1
  34. package/package.json +22 -21
  35. package/templates/claude/SKILL.md +71 -0
  36. package/templates/claude/references/audit.md +43 -0
  37. package/templates/claude/{rules.md → references/conventions.md} +25 -28
  38. package/templates/claude/audit.md +0 -43
  39. /package/templates/claude/{api.md → references/api.md} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uidex",
3
- "version": "0.5.2",
3
+ "version": "0.7.0",
4
4
  "description": "Convention-driven UI element registry and devtools surface for React apps.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -52,27 +52,14 @@
52
52
  "uidex": "dist/cli/cli.cjs"
53
53
  },
54
54
  "engines": {
55
- "node": ">=22"
55
+ "node": ">=22.12.0"
56
56
  },
57
57
  "publishConfig": {
58
58
  "access": "public"
59
59
  },
60
- "scripts": {
61
- "dev": "tsup --watch",
62
- "build": "pnpm run generate:api-routes && pnpm run build:css && tsup && pnpm run check:bundles",
63
- "generate:api-routes": "tsx scripts/generate-api-routes.ts",
64
- "build:css": "tailwindcss --input src/browser/styles/tailwind.css --output src/browser/styles/tailwind.built.css",
65
- "check:bundles": "node scripts/check-bundles.mjs",
66
- "test": "vitest run",
67
- "test:watch": "vitest",
68
- "lint": "eslint src/",
69
- "typecheck": "tsc --noEmit",
70
- "views-map": "tsx scripts/extract-views-map.ts",
71
- "views-map:check": "tsx scripts/extract-views-map.ts --check"
72
- },
73
60
  "dependencies": {
74
- "@hey-api/client-fetch": "^0.10.0",
75
61
  "@clack/prompts": "^1.2.0",
62
+ "@hey-api/client-fetch": "^0.10.0",
76
63
  "@zag-js/core": "^1.40.0",
77
64
  "@zag-js/dialog": "^1.40.0",
78
65
  "@zag-js/menu": "^1.40.0",
@@ -88,6 +75,7 @@
88
75
  "lucide": "^1.8.0",
89
76
  "lucide-react": "^1.7.0",
90
77
  "modern-screenshot": "^4.7.0",
78
+ "oxc-parser": "^0.135.0",
91
79
  "tailwind-merge": "^3.5.0",
92
80
  "xstate": "^5.30.0",
93
81
  "zod": "^4.3.6",
@@ -102,9 +90,6 @@
102
90
  "@types/node": "^22.10.5",
103
91
  "@types/react": "^19.2.14",
104
92
  "@types/react-dom": "^19.2.3",
105
- "@uidex/api-client": "workspace:*",
106
- "@uidex/eslint-config": "workspace:*",
107
- "@uidex/tsconfig": "workspace:*",
108
93
  "@vitejs/plugin-react": "^4.3.4",
109
94
  "eslint": "^9.18.0",
110
95
  "jsdom": "^26.0.0",
@@ -114,7 +99,10 @@
114
99
  "tsup": "^8.3.5",
115
100
  "tsx": "^4.7.0",
116
101
  "typescript": "^5.9.3",
117
- "vitest": "^2.1.8"
102
+ "vitest": "^2.1.8",
103
+ "@uidex/api-client": "0.0.1",
104
+ "@uidex/eslint-config": "0.0.0",
105
+ "@uidex/tsconfig": "0.0.0"
118
106
  },
119
107
  "keywords": [
120
108
  "uidex",
@@ -129,5 +117,18 @@
129
117
  "repository": {
130
118
  "type": "git",
131
119
  "url": "https://github.com/soel/uidex"
120
+ },
121
+ "scripts": {
122
+ "dev": "tsup --watch",
123
+ "build": "pnpm run generate:api-routes && pnpm run build:css && pnpm run typecheck && tsup && pnpm run check:bundles",
124
+ "generate:api-routes": "tsx scripts/generate-api-routes.ts",
125
+ "build:css": "tailwindcss --input src/browser/styles/tailwind.css --output src/browser/styles/tailwind.built.css",
126
+ "check:bundles": "node scripts/check-bundles.mjs",
127
+ "test": "vitest run",
128
+ "test:watch": "vitest",
129
+ "lint": "eslint src/",
130
+ "typecheck": "tsc --noEmit",
131
+ "views-map": "tsx scripts/extract-views-map.ts",
132
+ "views-map:check": "tsx scripts/extract-views-map.ts --check"
132
133
  }
133
- }
134
+ }
@@ -0,0 +1,71 @@
1
+ ---
2
+ name: uidex
3
+ description: "uidex UI tracking conventions, audit diagnostics, and API client. Use when: adding/editing data-uidex* attributes, export const uidex blocks, uidex.page.ts / uidex.feature.ts files, Playwright @uidex:flow tests, .uidex.json config, running uidex scan / uidex scaffold, calling the uidex API, or fixing uidex scan --audit diagnostics."
4
+ ---
5
+
6
+ # uidex
7
+
8
+ uidex is a UI element tracking and feedback ingestion platform. This skill covers conventions for annotating UI entities, running the scanner, fixing audit diagnostics, and using the API client.
9
+
10
+ ## AI behavior
11
+
12
+ Before editing any file inside a feature directory (`src/features/<name>/`) or a Next.js route directory, read the entity's well-known metadata file first:
13
+
14
+ - Feature directories: `uidex.feature.ts` — the feature's `export const uidex` block lives here.
15
+ - Route directories: `uidex.page.ts` — the page's `export const uidex` block lives here.
16
+
17
+ These files declare `acceptance` criteria and a `description`. Treat them as **context steering**, not test assertions:
18
+
19
+ - Acceptance criteria capture what matters about the page or feature so changes preserve the right behaviors. They are NOT the testing source of truth — Playwright flows under `e2e/**` are.
20
+ - Before making a change, check whether it would violate a declared acceptance criterion. If it would, flag the conflict to the user before proceeding.
21
+ - If a change adds or removes user-visible behavior, update the acceptance list in the well-known file alongside the code change.
22
+
23
+ If a feature or page directory has no `uidex.feature.ts` / `uidex.page.ts`, the metadata may live on an arbitrary file in the directory (legacy convention) — `uidex scan --audit` will surface a `prefer-well-known-file` diagnostic with the current location.
24
+
25
+ ## Reference guides
26
+
27
+ Read on demand when the task touches these areas:
28
+
29
+ - `./references/conventions.md` — Entity model, DOM annotations, `export const uidex` authoring, Playwright flows, scanner usage, configuration
30
+ - `./references/audit.md` — All `uidex scan --audit` diagnostic codes with fix instructions
31
+ - `./references/api.md` — uidex API client: authentication, making requests, common workflows, route tags
32
+
33
+ ## Quick reference
34
+
35
+ ### Scanner commands
36
+
37
+ ```bash
38
+ npx uidex scan # regenerate src/uidex.gen.ts
39
+ npx uidex scan --check # fail non-zero if the committed gen file is stale
40
+ npx uidex scan --audit # validate (--check + --lint; exits non-zero on errors)
41
+ npx uidex scan --fix # apply mechanical fixes (annotate unannotated elements), then rescan
42
+ npx uidex scan --json # emit diagnostics as JSON (fixable ones carry fixable: true)
43
+ npx uidex scaffold <widget|page|feature> <id> # emit Playwright stub from declared acceptance
44
+ npx uidex rename <element|widget|region> <old-id> <new-id> # rename an id everywhere
45
+ ```
46
+
47
+ Run `uidex scan` after any `data-uidex*` attribute change, `export const uidex` edit, or new flow.
48
+
49
+ ### DOM annotations
50
+
51
+ - `data-uidex="id"` — every interactive element (button, input, link, select, textarea)
52
+ - `data-uidex-region="id"` — structural regions (only when HTML5 landmarks don't suffice)
53
+ - `data-uidex-widget="id"` — composite behavioural units (video player, date picker, etc.)
54
+ - `data-uidex-primitive="name"` — reusable components outside `src/components/ui/**`
55
+
56
+ ### Module-scoped metadata
57
+
58
+ ```tsx
59
+ import type { Uidex } from "@/uidex.gen"
60
+
61
+ export const uidex = {
62
+ page: "org-settings",
63
+ acceptance: [
64
+ "User can update the organization name",
65
+ "Error message is shown if the update fails",
66
+ ],
67
+ features: ["billing"],
68
+ } as const satisfies Uidex.Page
69
+ ```
70
+
71
+ Kinds: `page`, `feature`, `widget`, `region`, `primitive`, `flow`, `notFlow` (opt-out).
@@ -0,0 +1,43 @@
1
+ # uidex audit diagnostics
2
+
3
+ Run `npx uidex scan --fix` first. This applies every machine-generated fix — `missing-element-annotation` (inserts a deterministic `data-uidex` id derived from the element's accessible name / visible text / tag, deduped with a numeric suffix) and `uidex-export-empty-name` (drops empty `name` fields) — then rescans and rewrites the gen file. In `--json` output, fixable diagnostics carry `fixable: true`. Review the generated ids and rename any that read poorly (`npx uidex rename`).
4
+
5
+ Then fix the remaining diagnostics, which need judgment:
6
+
7
+ 1. **`acceptance-uncovered`** — a declared acceptance criterion has no flow exercising it.
8
+ - Run `npx uidex scaffold <widget|page|feature> <id>` to generate a Playwright stub in `e2e/` with one `test()` per criterion; fill in the stubs.
9
+
10
+ 2. **`unknown-reference`** — a flow's `uidex('<id>')` call, or a `features:` / `widgets:` array in an `export const uidex`, references an id that isn't in the registry.
11
+ - If the id is a typo, fix the string literal.
12
+ - If the component was removed, remove the reference.
13
+ - If the component lost its `data-uidex`, restore it.
14
+
15
+ 3. **`duplicate-id`** — the same kind+id is annotated in more than one file. The registry keeps only one entry, so flows and the runtime overlay have an ambiguous target.
16
+ - Element/region duplicates (INFO) are fine when they are variants of the same logical element (desktop/mobile nav); otherwise rename one with `npx uidex rename <kind> <old-id> <new-id>` — it updates flow references too.
17
+ - Widget/primitive duplicates (WARNING) are definition collisions; rename one.
18
+
19
+ 4. **`dynamic-flow-reference`** — a `uidex(…)` call in a tagged flow uses a dynamic expression. The id is invisible to coverage and registry validation; use a string literal.
20
+
21
+ 5. **`scope-leak`** — a primitive scoped to `feature:<name>` or `page:<name>` is being imported from outside that scope.
22
+ - Either move the primitive to `src/components/ui/**` so it becomes global, or route the consumer through an appropriate boundary. Type-only imports are exempt.
23
+
24
+ 6. **`missing-element-annotation`** (INFO, machine-fixable) — an interactive `<button>`, `<a>`, `<input>`, `<select>`, or `<textarea>` has no `data-uidex`.
25
+ - `npx uidex scan --fix` inserts a generated id; review it and rename if it reads poorly. Skip (leave unannotated) only when the element is purely decorative (e.g., a skip link that never needs automated reference).
26
+
27
+ 7. **`uidex-export-satisfies-mismatch`** — the `satisfies Uidex.<Kind>` annotation contradicts the declared discriminator (e.g. `{ page: ... } satisfies Uidex.Widget`). Fix whichever side is wrong.
28
+
29
+ 8. **`widget-missing-dom` / `widget-id-mismatch`** — the widget id must appear identically on `data-uidex-widget="…"` at the DOM root and on `export const uidex = { widget: "…" }`. Align the two (prefer `npx uidex rename widget …` if the id itself is changing).
30
+
31
+ Renaming an id by hand is error-prone (flow specs reference it); prefer `npx uidex rename <element|widget|region> <old-id> <new-id>`, which rewrites the DOM attribute, `uidex()` calls, widget exports, and `widgets:` arrays, then regenerates the gen file. Occurrences it cannot edit (const indirection, ternaries, patterns) are listed as `MANUAL` lines.
32
+
33
+ After manual fixes, run `npx uidex scan` to regenerate the gen file before re-running the audit.
34
+
35
+ ## Decision rules for primitives
36
+
37
+ Add `data-uidex-primitive="kebab-name"` ONLY when the component lives outside `src/components/ui/**` and meets all of:
38
+
39
+ - Exports a reusable presentational component (not a page, layout, or feature orchestrator).
40
+ - Wraps native elements or other primitives — no business logic or data fetching (`useQuery`, `useMutation`, etc.).
41
+ - Imported by multiple consumers.
42
+
43
+ If it's in `src/components/ui/**`, the scanner auto-promotes it — no attribute needed.
@@ -1,22 +1,5 @@
1
1
  # uidex conventions
2
2
 
3
- This project uses [uidex](https://github.com/soel/uidex) for UI element tracking and feedback ingestion.
4
-
5
- ## AI behavior
6
-
7
- Before editing any file inside a feature directory (`src/features/<name>/`) or a Next.js route directory, read the entity's well-known metadata file first:
8
-
9
- - Feature directories: `uidex.feature.ts` — the feature's `export const uidex` block lives here.
10
- - Route directories: `uidex.page.ts` — the page's `export const uidex` block lives here.
11
-
12
- These files declare `acceptance` criteria and a `description`. Treat them as **context steering**, not test assertions:
13
-
14
- - Acceptance criteria capture what matters about the page or feature so changes preserve the right behaviors. They are NOT the testing source of truth — Playwright flows under `e2e/**` are.
15
- - Before making a change, check whether it would violate a declared acceptance criterion. If it would, flag the conflict to the user before proceeding.
16
- - If a change adds or removes user-visible behavior, update the acceptance list in the well-known file alongside the code change.
17
-
18
- If a feature or page directory has no `uidex.feature.ts` / `uidex.page.ts`, the metadata may live on an arbitrary file in the directory (legacy convention) — `uidex scan --audit` will surface a `prefer-well-known-file` diagnostic with the current location.
19
-
20
3
  ## Entity model
21
4
 
22
5
  Eight entity kinds make up the registry. Most are discovered by convention; annotations only override.
@@ -44,7 +27,9 @@ Four attributes. All use kebab-case ids. DOM attributes are the sole surface for
44
27
  <a data-uidex="nav-home" href="/">Home</a>
45
28
  ```
46
29
 
47
- `uidex scan --audit` flags unannotated `<button>`, `<a>`, `<input>`, `<select>`, `<textarea>` as INFO diagnostics. Rename carefully — ids are referenced by flow tests.
30
+ `uidex scan --audit` flags unannotated `<button>`, `<a>`, `<input>`, `<select>`, `<textarea>` as INFO diagnostics (`missing-element-annotation`, or `spread-attr` when the element's only attribute is a `{...props}` spread). `missing-element-annotation` is machine-fixable: `npx uidex scan --fix` inserts a deterministic `data-uidex` id derived from the element's accessible name (aria-label), visible text, or tag, deduping collisions with a numeric suffix. Review the generated ids and rename any that read poorly via `npx uidex rename` — ids are referenced by flow tests, and the command rewrites those references too. (`spread-attr` is left alone: the annotation may be forwarded through props.)
31
+
32
+ Dynamic attribute values resolve statically when possible: string literals in containers, unambiguous same-file `const` references, ternaries with literal branches (both ids register), and template literals with static text in any position (`` `tag-${x}-remove` `` registers the pattern `tag-*-remove`; `*` matches any run of characters at runtime, most-static-text pattern wins). Anything else is a `dynamic-attr` warning.
48
33
 
49
34
  ### `data-uidex-region` — structural regions (only when HTML5 doesn't suffice)
50
35
 
@@ -56,6 +41,20 @@ HTML5 landmarks (`<header>`, `<nav>`, `<main>`, `<aside>`, `<footer>`, `role="re
56
41
  </div>
57
42
  ```
58
43
 
44
+ **Important:** The attribute value must be a string literal. `data-uidex-region={variable}` cannot be resolved statically and triggers a `region-dynamic-attr` warning. When a wrapper component forwards the region via a prop, declare it with `export const uidex` on the consuming file instead:
45
+
46
+ ```tsx
47
+ import type { Uidex } from "@/uidex.gen"
48
+
49
+ export const uidex = {
50
+ region: "members-page",
51
+ } as const satisfies Uidex.Region
52
+
53
+ export function MembersPage() {
54
+ return <EntityPageShell region="members-page" />
55
+ }
56
+ ```
57
+
59
58
  ### `data-uidex-widget` — composite behavioural units
60
59
 
61
60
  Place on the root of a self-contained UI unit with internal state (video player, date picker, rich text editor). The DOM attribute is the runtime boundary; acceptance criteria live on the `export const uidex` at the component definition.
@@ -124,11 +123,12 @@ Kinds (discriminator fields):
124
123
  | `page` | `page: PageId \| false` | `acceptance?`, `features?`, `widgets?`, `description?` |
125
124
  | `feature` | `feature: FeatureId \| false` | `acceptance?`, `description?` |
126
125
  | `widget` | `widget: WidgetId` | `acceptance?`, `description?` |
126
+ | `region` | `region: RegionId \| false` | `description?` |
127
127
  | `primitive` | `primitive: PrimitiveId \| false` | `description?` |
128
128
  | `flow` | `flow: FlowId` | `description?` |
129
129
  | opt-out | `notFlow: true` | (no other fields — opts a Playwright spec out of flow auto-promotion) |
130
130
 
131
- Setting `page: false` / `feature: false` / `primitive: false` explicitly opts a module out of auto-promotion.
131
+ Setting `page: false` / `feature: false` / `primitive: false` / `region: false` explicitly opts a module out of auto-promotion.
132
132
 
133
133
  ### Typed cross-references
134
134
 
@@ -180,8 +180,10 @@ Rules:
180
180
  npx uidex scan # regenerate src/uidex.gen.ts
181
181
  npx uidex scan --check # fail non-zero if the committed gen file is stale
182
182
  npx uidex scan --audit # validate (--check + --lint; exits non-zero on errors)
183
- npx uidex scan --json # emit diagnostics as JSON
184
- npx uidex scaffold widget <id> # emit Playwright stub from declared acceptance
183
+ npx uidex scan --fix # apply mechanical fixes (annotate unannotated elements), then rescan and write
184
+ npx uidex scan --json # emit diagnostics as JSON (fixable ones carry fixable: true)
185
+ npx uidex scaffold <widget|page|feature> <id> # emit Playwright stub from declared acceptance
186
+ npx uidex rename <element|widget|region> <old-id> <new-id> # rename an id everywhere
185
187
  ```
186
188
 
187
189
  Run `uidex scan` after any `data-uidex*` attribute change, `export const uidex` edit, or new flow. The gen file (`src/uidex.gen.ts` by default) is auto-generated and **committed** — consumers import types from it directly.
@@ -202,12 +204,11 @@ Install the matching bundler plugin so `uidex.gen.ts` regenerates on file save:
202
204
  {
203
205
  "sources": [{ "rootDir": "src" }],
204
206
  "output": "src/uidex.gen.ts",
205
- "flows": ["e2e/**/*.spec.ts"],
206
- "typeMode": "strict"
207
+ "flows": ["e2e/**/*.spec.ts"]
207
208
  }
208
209
  ```
209
210
 
210
- `typeMode: "strict"` (default) emits literal id unions `tsc` rejects unknown ids at the `satisfies Uidex.X` site. `typeMode: "loose"` emits `string` — a **temporary migration knob** for repos mid-migration from legacy JSDoc.
211
+ `uidex.gen.ts` always emits literal id unions, so `tsc` rejects unknown ids at the `satisfies Uidex.X` site.
211
212
 
212
213
  Multi-source (monorepo) example:
213
214
 
@@ -221,7 +222,3 @@ Multi-source (monorepo) example:
221
222
  "flows": ["apps/web/e2e/**/*.spec.ts"]
222
223
  }
223
224
  ```
224
-
225
- ## Migration from legacy JSDoc
226
-
227
- JSDoc tags (`@uidex page|feature|widget`, `@acceptance`, `@uidex:not-flow`) are **no longer recognised** — the scanner registers nothing from them. `uidex scan --lint` ships a `legacy-jsdoc` rule that warns on every legacy tag and points at the replacement `export const uidex` form. Set `typeMode: "loose"` in `.uidex.json` during migration to keep `tsc` quiet; flip to `strict` once every JSDoc tag is gone.
@@ -1,43 +0,0 @@
1
- Run `npx uidex scan --audit` and fix all reported diagnostics.
2
-
3
- 1. **`marker-md-ignored`** — legacy `UIDEX_PAGE.md` / `UIDEX_FEATURE.md` files from v1. Migrate their content:
4
- - Create a `uidex.page.ts` (in the route directory) or `uidex.feature.ts` (in the feature directory) and add an `export const uidex` block carrying the `page`/`feature` id, the description, and the acceptance list.
5
- - Each `- [ ]` checkbox becomes one entry in the `acceptance` array.
6
- - Delete the `UIDEX_PAGE.md` / `UIDEX_FEATURE.md` file.
7
-
8
- 2. **`acceptance-uncovered`** — a declared `@acceptance` criterion has no flow exercising it.
9
- - For widgets: run `npx uidex scaffold widget <id>` to generate a Playwright stub in `e2e/` with one `test()` per criterion; fill in the stubs.
10
- - For features/pages: write a Playwright spec under `e2e/**` tagged `{ tag: "@uidex:flow" }` that exercises the criterion by calling `uidex('<element-id>').*()` on the relevant elements.
11
-
12
- 3. **`unknown-reference`** — a flow's `uidex('<id>')` call references an id that isn't in the registry.
13
- - If the id is a typo, fix the string literal in the spec.
14
- - If the component was removed, remove the call.
15
- - If the component lost its `data-uidex`, restore it.
16
- - Dynamic ids (variables, template literals) are invisible to the scanner. If a literal is flagged, it is actually a literal in the source.
17
-
18
- 4. **`scope-leak`** — a primitive scoped to `feature:<name>` or `page:<name>` is being imported from outside that scope.
19
- - Either move the primitive to `src/components/ui/**` so it becomes global, or route the consumer through an appropriate boundary.
20
-
21
- 5. **`missing-element-annotation`** (INFO) — an interactive `<button>`, `<a>`, `<input>`, `<select>`, or `<textarea>` has no `data-uidex`.
22
- - Add `data-uidex="<kebab-id>"`. Skip this diagnostic only when the element is purely decorative (e.g., a skip link that never needs automated reference).
23
-
24
- 6. **`acceptance-orphan`** — an `@acceptance` tag appears on a JSDoc that doesn't declare `@uidex page|feature|widget`.
25
- - Move the acceptance tags into the correct JSDoc block, or remove them.
26
-
27
- 7. **`prefer-well-known-file`** (INFO) — a page or feature's `export const uidex` lives on an arbitrary file instead of the well-known file (`uidex.page.ts` for pages, `uidex.feature.ts` for features). Migrate:
28
- - Create `uidex.page.ts` (in the route directory) or `uidex.feature.ts` (in the feature directory).
29
- - Move the `export const uidex` block from the current file into the new well-known file.
30
- - Remove the export (and any now-unused `Uidex` type import) from the original file.
31
- - Run `npx uidex scan` to regenerate the gen file — `loc.file` updates but the entity itself is unchanged.
32
-
33
- After fixing, run `npx uidex scan` to regenerate the gen file before re-running the audit.
34
-
35
- ## Decision rules for primitives
36
-
37
- Add `data-uidex-primitive="kebab-name"` ONLY when the component lives outside `src/components/ui/**` and meets all of:
38
-
39
- - Exports a reusable presentational component (not a page, layout, or feature orchestrator).
40
- - Wraps native elements or other primitives — no business logic or data fetching (`useQuery`, `useMutation`, etc.).
41
- - Imported by multiple consumers.
42
-
43
- If it's in `src/components/ui/**`, the scanner auto-promotes it — no attribute needed.
File without changes