@atscript/ui-styles 0.1.59

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/README.md ADDED
@@ -0,0 +1,133 @@
1
+ <p align="center">
2
+ <a href="https://ui.atscript.dev" target="_blank" rel="noopener">
3
+ <img src="https://ui.atscript.dev/logo.svg" alt="Atscript UI" height="96" />
4
+ </a>
5
+ </p>
6
+
7
+ # @atscript/ui-styles
8
+
9
+ 📚 **Documentation:** [ui.atscript.dev](https://ui.atscript.dev)
10
+
11
+ Shared UnoCSS preset, shortcuts, icon loader, per-component class safelist, and pre-built CSS for [`@atscript/vue-form`](../vue-form), [`@atscript/vue-table`](../vue-table), and [`@atscript/vue-wf`](../vue-wf).
12
+
13
+ > **This README is contributor-oriented.** It covers what the package ships, why each piece exists, and the load-bearing decisions that shape the code. End-user installation and usage docs live at [`/docs/guide/styling.md`](../../docs/guide/styling.md).
14
+
15
+ ## What this package ships
16
+
17
+ - **`asPresetVunor(opts)`** — the consumer-facing UnoCSS preset. Returns `Preset[]` with vunor's preset, an icons preset, shortcuts merged across `form` / `table` / `wf` / `common`, and a custom **Extractor** that pulls per-component class lists into the consumer's CSS bundle without any `node_modules` filesystem scan.
18
+ - **`createAsBaseUnoConfig(opts)`** — same presets + shortcuts as `asPresetVunor` but **without** the extractor. Internal scripts only (see Decision 13).
19
+ - **`allShortcuts`** + per-area exports (`formShortcuts`, `tableShortcuts`, `wfShortcuts`, `commonShortcuts`) — direct access to the shortcut maps.
20
+ - **`asIconsPreset(opts)`** + **`createIconsLoader(opts)`** — local-SVG icons collection with the `as` prefix.
21
+ - **Generated safelist** at [`src/generated/component-classes.ts`](src/generated/component-classes.ts) — committed (Decision 12). Re-export public API: `componentClasses`, `helperAliases`, `componentPackages`, `getComponentClasses(...)`, `getHelperClasses(...)`.
22
+ - **Pre-built CSS** at `dist/css/{all,form,table,wf}.css` — for non-UnoCSS consumers (Decision 5). Default theme only; cannot be themed or filtered.
23
+ - **`AsResolver()`** at the `@atscript/ui-styles/vite` subpath — `unplugin-vue-components` resolver that maps `As*` PascalCase tags to the matching per-component subpath (`@atscript/vue-{form,table,wf}/<as-name>`). Tag-only — composables must be imported explicitly. Driven by `componentPackages` from the generated module, so new components are picked up automatically.
24
+
25
+ ## Why this package exists
26
+
27
+ UnoCSS only generates CSS for class names it sees in the consumer's source tree. When `@atscript/vue-form` etc. are installed from npm, their classes live inside `node_modules` and UnoCSS won't scan there. This package solves that with a custom **Extractor** that scans the consumer's own source for component imports / tags / helper calls and pulls in pre-computed class lists per component.
28
+
29
+ ## Load-bearing decisions
30
+
31
+ The full design rationale lives in [`STYLES.md`](../../STYLES.md). Quick contributor cheat-sheet:
32
+
33
+ ### Decision 6 — Custom UnoCSS Extractor with five match patterns
34
+
35
+ `createAsExtractor` ([`src/extractor.ts`](src/extractor.ts)) recognizes:
36
+
37
+ 1. Subpath imports — `from '@atscript/vue-form/as-form'`
38
+ 2. Barrel named imports — `import { AsForm, AsField } from '@atscript/vue-form'`
39
+ 3. PascalCase tags — `<AsForm`, `<AsTableRoot` (the dominant pattern in real consumer code)
40
+ 4. Kebab-case tags — `<as-form`
41
+ 5. Helper-function calls — `createDefaultTypes(`, `createDefaultControls(`, `createDefaultCellTypes(`
42
+
43
+ The unknown-name guard is `componentClasses[kebab]` — anything not in the dict is a no-op. PascalCase / kebab equivalence is handled by [`src/kebab.ts`](src/kebab.ts).
44
+
45
+ ### Decision 11 — Helper-call detection (mandatory for default components)
46
+
47
+ `createDefaultTypes()`, `createDefaultControls()`, and `createDefaultCellTypes()` ship default field/cell components consumers never tag directly. A tag-only extractor would silently drop their classes. The extraction script (`scripts/extract-component-classes.ts`) walks each helper file, parses the single import from `../components/defaults`, kebab-cases the named identifiers, and emits `helperAliases`. The extractor unions a helper's class set into the result whenever it sees the helper name as a function call (member calls like `obj.createDefaultTypes()` are excluded by a leading `(?:^|[^.\w])` guard).
48
+
49
+ When new helpers are added, they must conform to **Decision 17**'s layout (`src/composables/create-*.ts`, single import from `../components/defaults`) or the parser will hard-fail on regenerate.
50
+
51
+ ### Decision 13 — `createAsBaseUnoConfig` / `asPresetVunor` cycle break
52
+
53
+ The extractor inside `asPresetVunor()` imports `componentClasses`. The extraction script needs to run UnoCSS with the same shortcuts/presets to compute `componentClasses`. If the script imported `asPresetVunor()`, you'd have a chicken-and-egg.
54
+
55
+ Two factories:
56
+
57
+ - **`createAsBaseUnoConfig({})`** — presets + shortcuts, no extractor. Used by `scripts/extract-component-classes.ts` and `scripts/build-css.ts`.
58
+ - **`asPresetVunor(opts)`** — same base + the extractor that imports `componentClasses`. Used by consumers.
59
+
60
+ Don't collapse these. Don't make the build scripts depend on the extractor.
61
+
62
+ ### Decision 15 — `excludeComponents` is post-match, consumer-side, UnoCSS-only
63
+
64
+ `asPresetVunor({ excludeComponents: ["as-filter-dialog"] })` lets a power user drop classes for default components they've replaced with their own. The exclusion is applied **after** the regex matches — matched-but-excluded components silently don't add classes. This is documented; replacing with `<AsFilterDialog>` after excluding it gives an unstyled component and is the user's explicit choice.
65
+
66
+ **Pre-built CSS does not honor `excludeComponents`.** The CSS files in `dist/css/` are generated once at our build time with the full safelist for each package. The option is a runtime extractor knob; it has no place in a pre-baked artifact. Users who need to drop default components must use the UnoCSS path.
67
+
68
+ ### `AsResolver` — `componentPackages` is the single source of truth
69
+
70
+ The resolver in [`src/vite.ts`](src/vite.ts) does **not** maintain its own dispatch table. It looks up every `As*` tag in `componentPackages` and forms `${PKG_TO_NPM[pkg]}/${kebab}`. When a new public component lands in any vue-\* package, the extraction script picks it up, `componentPackages` regenerates, and the resolver routes to it on the next build — no resolver edits needed.
71
+
72
+ `unplugin-vue-components` is wired as an **optional** peerDependency. Consumers who don't auto-import (use explicit `import { AsForm } from "@atscript/vue-form"` everywhere) don't need to install it; consumers who do auto-import already have it.
73
+
74
+ ### Decision 17 — Canonical component layout in vue-\* packages
75
+
76
+ Every `@atscript/vue-*` package follows one layout:
77
+
78
+ - **Public root** components at `src/components/*.vue`.
79
+ - **Public defaults** at `src/components/defaults/*.vue`, re-exported through `src/components/defaults/index.ts` (a pure re-export barrel — no logic, no helper functions).
80
+ - **Private internals** at `src/components/internal/*.vue` — not exported, no subpath, but classes still reach the safelist via the dependency walk.
81
+ - **Helpers** at `src/composables/create-*.ts`, importing components only via `import { AsX, ... } from "../components/defaults"`. The helper-alias parser in `scripts/extract-component-classes.ts` depends on this exact shape — a non-conforming helper file fails the build.
82
+
83
+ Any new component or helper must conform; otherwise tooling silently drops or hard-fails it.
84
+
85
+ ## Pre-built CSS — the trade-off (Decision 5)
86
+
87
+ For consumers who don't run UnoCSS, this package ships four pre-built CSS files in `dist/css/`:
88
+
89
+ | File | Contents |
90
+ | ----------- | ------------------------------------------- |
91
+ | `all.css` | Every component across form + table + wf. |
92
+ | `form.css` | Components from `@atscript/vue-form` only. |
93
+ | `table.css` | Components from `@atscript/vue-table` only. |
94
+ | `wf.css` | Components from `@atscript/vue-wf` only. |
95
+
96
+ Caveats:
97
+
98
+ - **No theming.** Built once at our publish time with the default `asPresetVunor()` palette. Consumers who want a custom theme must use the UnoCSS path.
99
+ - **No `excludeComponents`.** See Decision 15 above.
100
+ - **Per-package files overlap.** Each is independently complete — `as-spacer` lands in every per-package file that uses it. **Don't combine** `form.css` + `table.css` + `wf.css`; load `all.css` instead.
101
+
102
+ The `wf.css` file is small today (vue-wf has one component) but exists for symmetry — vue-wf will grow and consumers can already load just what they need.
103
+
104
+ ## Generated artifacts
105
+
106
+ - [`src/generated/component-classes.ts`](src/generated/component-classes.ts) — committed to git (Decision 12). Three maps + two helper functions. Regenerate with `pnpm extract-classes`. CI must run the script and fail on drift; PRs that change `.vue` templates without regenerating will be flagged.
107
+ - `dist/css/{all,form,table,wf}.css` — gitignored build artifacts, shipped via the published tarball through `files: ["dist"]`. Built by `pnpm build-css`.
108
+
109
+ ## Build pipeline
110
+
111
+ ```bash
112
+ pnpm --filter @atscript/ui-styles run build
113
+ # expands to:
114
+ pnpm extract-classes && vp pack && pnpm build-css
115
+ ```
116
+
117
+ - `extract-classes` (`scripts/extract-component-classes.ts`) — walks vue-form/table/wf source files, runs UnoCSS, emits `src/generated/component-classes.ts`. Reads source on disk; **does not** consume their `dist/`. A clean checkout can run this even before vue-\* packages are built (Decision 8).
118
+ - `vp pack` — bundles two TS entry points: `src/index.ts` (the main surface) and `src/vite.ts` (the `AsResolver`). Output: `dist/{index,vite}.{mjs,cjs,d.mts,d.cts}`.
119
+ - `build-css` (`scripts/build-css.ts`) — uses `createAsBaseUnoConfig()` (Decision 13) plus `componentClasses` + `componentPackages` from the generated module to emit the four CSS files. Output is byte-deterministic across runs.
120
+
121
+ ## Iterating on shortcuts
122
+
123
+ After Phase 6, the playground and demo configs no longer source-scan `vue-{form,table,wf}/src/**`. Editing a shortcut and seeing it reflected in the playground requires `@atscript/ui-styles` to be in watch mode:
124
+
125
+ ```bash
126
+ # terminal 1
127
+ pnpm --filter @atscript/ui-styles run dev # vp pack --watch
128
+
129
+ # terminal 2
130
+ pnpm dev
131
+ ```
132
+
133
+ This trades dev ergonomics for a clean, prod-like setup that matches what end users get.