@getvitops/vite 1.0.0 → 2.1.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 +266 -0
- package/README.md +6 -1
- package/dist/index.d.mts +110 -5
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +97 -14
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,271 @@
|
|
|
1
1
|
# @getvitops/vite
|
|
2
2
|
|
|
3
|
+
## 2.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 9bf975a: Add `vitops media` — encode raw video into web-ready outputs, instead of hand-rolling an ffmpeg
|
|
8
|
+
script per project.
|
|
9
|
+
|
|
10
|
+
Keep unprocessed video in a `raw/` directory and run:
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
vitops media --raw raw --out src/assets/processed
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Each source becomes three files: **VP9/WebM**, an **H.264/MP4** fallback, and a **JPG poster**.
|
|
17
|
+
Import them like any other asset, so your bundler content-hashes them:
|
|
18
|
+
|
|
19
|
+
```astro
|
|
20
|
+
---
|
|
21
|
+
import hero from '../assets/processed/hero.webm';
|
|
22
|
+
import poster from '../assets/processed/hero.jpg';
|
|
23
|
+
---
|
|
24
|
+
<video poster={poster.src} autoplay muted loop playsinline>
|
|
25
|
+
<source src={hero} type="video/webm" />
|
|
26
|
+
</video>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
In Astro it's an integration option — `vitops({ css, media: { raw: 'raw', out: 'src/assets/processed' } })`
|
|
30
|
+
— and it runs in the same pass that generates your CSS. `@getvitops/vite` gains a matching `media`
|
|
31
|
+
option, and `@getvitops/utils/media` exports `processMedia()` for anything else.
|
|
32
|
+
|
|
33
|
+
Defaults: capped at 1920px wide, CRF 32, audio dropped (the common case is a muted autoplay loop),
|
|
34
|
+
poster from frame 0. All of them are flags. `--dry` prints exactly what a run would do.
|
|
35
|
+
|
|
36
|
+
**Runs are cached**, on source content plus encode settings, in `.vitops/media-manifest.json` — a
|
|
37
|
+
24 MB clip that took 88 seconds the first time takes 0.14 seconds the second. A missing output
|
|
38
|
+
re-encodes; a corrupt manifest re-encodes everything. Neither ever reads as "already done", because
|
|
39
|
+
that failure is silent and no rebuild would fix it.
|
|
40
|
+
|
|
41
|
+
**Commit the outputs and the manifest.** A fresh CI clone has neither and would re-encode from
|
|
42
|
+
scratch; committing both means CI never needs ffmpeg. It also keeps your history clean — ffmpeg
|
|
43
|
+
output isn't byte-reproducible across versions, so a CI re-encode would rewrite every video on any
|
|
44
|
+
toolchain bump. Use `--force` when you mean to re-encode.
|
|
45
|
+
|
|
46
|
+
**`ffmpeg` is an external tool, not an npm dependency** — install it yourself (`brew install
|
|
47
|
+
ffmpeg`, `apt install ffmpeg`, `winget install Gyan.FFmpeg`). The command fails without it rather
|
|
48
|
+
than skipping: a page referencing a video that was never encoded is broken, not degraded.
|
|
49
|
+
|
|
50
|
+
Two defaults worth knowing about. The MP4 exists because older iOS and the in-app webviews inside
|
|
51
|
+
social apps still don't decode VP9, and it's written with `+faststart` — without which a browser
|
|
52
|
+
downloads the whole file before showing frame one. And a poster taken from frame 0 is often black
|
|
53
|
+
on a clip that fades in; that's `--poster-time`, not a bug.
|
|
54
|
+
|
|
55
|
+
### Patch Changes
|
|
56
|
+
|
|
57
|
+
- Updated dependencies [20e518e]
|
|
58
|
+
- Updated dependencies [9bf975a]
|
|
59
|
+
- @getvitops/generator@2.1.0
|
|
60
|
+
- @getvitops/utils@2.1.0
|
|
61
|
+
|
|
62
|
+
## 2.0.0
|
|
63
|
+
|
|
64
|
+
### Minor Changes
|
|
65
|
+
|
|
66
|
+
- bf453b0: Anywhere the toolchain takes a config, that file may now be a `design-system.json` **or** the larger
|
|
67
|
+
site config that embeds one.
|
|
68
|
+
|
|
69
|
+
If you keep your tokens inside a `company.json` / `site.json`, you had to maintain a second file for
|
|
70
|
+
the tooling's sake — or duplicate your whole token set, because a site config must carry a complete
|
|
71
|
+
`designSystem`. Now you point the same option at the file you already have:
|
|
72
|
+
|
|
73
|
+
```js
|
|
74
|
+
// astro.config.mjs
|
|
75
|
+
getvitops({ css: { input: 'company.json', format: 'css' } });
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
vitops generate --input company.json --format css --out dist
|
|
80
|
+
vitops lint --input company.json --src src
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The design system is taken from `designSystem.themes[theme]` with its `extends` chain resolved —
|
|
84
|
+
`defaultTheme`, else `default`, else whatever `--theme` / the `theme` option names. Nothing changes
|
|
85
|
+
for a plain `design-system.json`; the two are told apart by shape, not by filename, so you can call
|
|
86
|
+
the file anything.
|
|
87
|
+
|
|
88
|
+
**A site config also supplies the site-level facts generation reads**, so the path is declared once
|
|
89
|
+
rather than per option:
|
|
90
|
+
- `designSystem.defaultColorScheme: "system"` — emits the `prefers-color-scheme` block, which is
|
|
91
|
+
what makes `<color-scheme-toggle>`'s System position do anything.
|
|
92
|
+
- `legal.*.enabled` — renders the documents. `legal: {}` is now the whole declaration; `legal.input`
|
|
93
|
+
is optional.
|
|
94
|
+
- `icons.sprite` — builds `icons.svg`.
|
|
95
|
+
- `fonts` — `fonts: true` reads the families from it.
|
|
96
|
+
|
|
97
|
+
Each of those is already gated on a field in that config, so nothing new appears unless the config
|
|
98
|
+
asks for it. An explicit `site` option still wins when the two are genuinely different files.
|
|
99
|
+
|
|
100
|
+
Also:
|
|
101
|
+
- **`vitops validate` routes on the file's shape.** Pointed at a site config it used to report a
|
|
102
|
+
single `unrecognized_keys` for `designSystem` and nothing about the file's actual contents — a
|
|
103
|
+
wrong answer that reads like a right one. It now validates a site config as one, including the
|
|
104
|
+
cross-field integrity JSON Schema can't express, and checks every theme resolves to a complete
|
|
105
|
+
design system (`validateSite` only checked that the `extends` chain resolved, not what it resolved
|
|
106
|
+
to).
|
|
107
|
+
- **`generate()` gained `theme` and `siteEnv`.** The A/B variant for `siteEnv` is applied before the
|
|
108
|
+
theme is selected, so a variant can override tokens.
|
|
109
|
+
- **The theme editor's Save to source follows the design system into a site config.** Its patch is
|
|
110
|
+
design-system-relative, so the dev server merges into that subtree and writes only the surrounding
|
|
111
|
+
file whole. It locates the subtree in the raw on-disk object rather than the normalised one, so an
|
|
112
|
+
author who wrote either `designSystem` shorthand still gets their own keys edited.
|
|
113
|
+
|
|
114
|
+
- bf453b0: **Breaking (site config):** `designSystem` is now an object, and the toggle's "System" position works.
|
|
115
|
+
|
|
116
|
+
`<color-scheme-toggle>` has always shipped three segments, and "System" did nothing. It removes the
|
|
117
|
+
theme attribute, and with no `prefers-color-scheme` block in the generated CSS the page fell straight
|
|
118
|
+
through to light on every machine — so a third of the control was inert, and a no-JS page could never
|
|
119
|
+
follow the OS at all.
|
|
120
|
+
|
|
121
|
+
The fix is a second, opt-in copy of the dark delta under
|
|
122
|
+
`@media (prefers-color-scheme: dark)`, scoped to "no explicit choice has been made". An explicit
|
|
123
|
+
light choice still wins over a dark OS. It costs **+303 B gzipped** on the colour layer — the block
|
|
124
|
+
repeats only the tokens whose dark value differs, and two identical runs of text compress to almost
|
|
125
|
+
nothing. It is opt-in because switching it on visibly flips an existing site dark for dark-OS
|
|
126
|
+
visitors.
|
|
127
|
+
|
|
128
|
+
Turning it on is a site-level fact, which needed somewhere to live:
|
|
129
|
+
|
|
130
|
+
```jsonc
|
|
131
|
+
"designSystem": {
|
|
132
|
+
"themes": { "default": { … }, "elegant": { "extends": "default", … } },
|
|
133
|
+
"defaultTheme": "default",
|
|
134
|
+
"defaultColorScheme": "system" // 'light' | 'dark' | 'system'
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
**What changed.** `designSystem` was a bare map of themeName → design system, so _every_ key was a
|
|
139
|
+
theme name and there was nowhere to put a system-wide field without it colliding with a theme of that
|
|
140
|
+
name. The map moves under `themes`, and `defaultTheme` + `defaultColorScheme` move inside the block.
|
|
141
|
+
`respectSystemPreference` is gone — `defaultColorScheme: "system"` says the same thing, and the two
|
|
142
|
+
were always read together, so the incoherent combination is no longer expressible.
|
|
143
|
+
|
|
144
|
+
**Migration is automatic at runtime.** `resolveSiteConfig` accepts all three spellings — the
|
|
145
|
+
canonical shape, a bare theme map (the old schema), and a bare design system written inline. But
|
|
146
|
+
`site.schema.json` is published to a stable URL, so an editor pinned to `$schema` will flag the old
|
|
147
|
+
shape; update it when convenient.
|
|
148
|
+
|
|
149
|
+
One behavioural change worth knowing: shorthand normalisation now runs **before** the A/B variant
|
|
150
|
+
merge, not after. Previously an `abTesting` override's key path depended on which shorthand the base
|
|
151
|
+
config happened to use, so the same patch landed in different places in two otherwise-equivalent
|
|
152
|
+
configs. Overrides now always address `designSystem.themes.<name>`.
|
|
153
|
+
|
|
154
|
+
Also: `getvitops({ site: { input } })` gives the Astro integration one place to name the site config
|
|
155
|
+
— `legal`, `fonts` and the colour scheme all read from it — and `css.systemColorScheme` sets the
|
|
156
|
+
appearance directly for consumers who have no site config.
|
|
157
|
+
|
|
158
|
+
Light/dark remains **derived**, not a theme: `functionalRole()` builds both appearances from one ramp,
|
|
159
|
+
which is what lets the contrast contract check both at build time and what gives every consumer a
|
|
160
|
+
working dark mode without authoring one. `themes` is for authored variants.
|
|
161
|
+
|
|
162
|
+
- 04a51d8: Icons: one semantic vocabulary across icon sets, with the bundle derived from your source.
|
|
163
|
+
|
|
164
|
+
Name icons by meaning — `<Icon name="menu" />` resolves to `fa7-solid:bars`, `ph:list` or
|
|
165
|
+
`lucide:menu` depending on the set you configure, so swapping sets is a config edit rather than a
|
|
166
|
+
find-and-replace. A name containing `:` still passes through untouched, which is the escape hatch
|
|
167
|
+
for a set-specific glyph.
|
|
168
|
+
|
|
169
|
+
**New `icons` option on `getvitops()`.** Configures the sets once per site and, under
|
|
170
|
+
`output: 'server'`, derives astro-icon's `include` map by scanning your source — the list most
|
|
171
|
+
projects end up maintaining by hand. On a static build no `include` is passed at all, because
|
|
172
|
+
astro-icon is already zero-config there and a list could only drop a glyph the scan couldn't see.
|
|
173
|
+
Names you declare that don't resolve fail the build; names only the scan found just warn; names
|
|
174
|
+
computed at runtime are reported with file and line so you can declare them.
|
|
175
|
+
|
|
176
|
+
**New `<Icon />` component**, and a real fix with it: `Popover`, `Details` and `Drawer` imported
|
|
177
|
+
`astro-icon/components` at module scope, so the _optional_ peer was resolved whether or not an icon
|
|
178
|
+
ever rendered — hard-failing anyone who hadn't installed it. Engines now load dynamically. The
|
|
179
|
+
per-component `iconResolver` prop still works but is deprecated in favour of the integration option.
|
|
180
|
+
|
|
181
|
+
**New SVG sprite** (`icons.sprite: true` in your site config) for consumers that can't run an icon
|
|
182
|
+
integration — Bricks/WordPress, EmDash renderers, plain HTML. `<use href="…/icons.svg#ph--list">`,
|
|
183
|
+
no JavaScript and no icon-API request. Every semantic name also gets a set-independent `icon-<name>`
|
|
184
|
+
alias, so sprite markup survives an icon-set change. WordPress gets `vitops_icon()`, a
|
|
185
|
+
`[vitops_icon]` shortcode and a **Vitops → Icon** Bricks element.
|
|
186
|
+
|
|
187
|
+
**New `vitops icons`** command: reports which icons your source uses, which names couldn't be
|
|
188
|
+
resolved, and which are computed at runtime; `--sprite` builds the sprite, `--json` for CI.
|
|
189
|
+
|
|
190
|
+
**Renamed, and completed to all four directions.** Chevrons and arrows are the
|
|
191
|
+
one family whose _meaning is_ its direction, and that direction is physical, not
|
|
192
|
+
logical — a chevron in a details marker points down in every writing mode. So they
|
|
193
|
+
are named for where they point: `expand-more`/`expand-less` →
|
|
194
|
+
`chevron-down`/`chevron-up`, and `arrow-forward`/`arrow-back` →
|
|
195
|
+
`arrow-right`/`arrow-left`. `chevron-left`/`chevron-right` keep their names.
|
|
196
|
+
`arrow-up`/`arrow-down` are new, so both families now cover all four directions.
|
|
197
|
+
`lightning` is new.
|
|
198
|
+
|
|
199
|
+
If you passed any of the old names to `<Icon />`, `resolveIcon` or an `icons`
|
|
200
|
+
config, update them. They fail loudly rather than silently: an unresolvable
|
|
201
|
+
declared name throws, and `vitops icons` reports scanned ones.
|
|
202
|
+
|
|
203
|
+
**Fixed three Font Awesome mappings that named no real glyph** and so rendered an
|
|
204
|
+
empty box: `login`/`logout` were mapped to `login`/`logout` (Font Awesome calls
|
|
205
|
+
them `right-to-bracket`/`right-from-bracket`) and `backup` to `backup`
|
|
206
|
+
(`cloud-arrow-up`). Every value in all four UI sets is now checked against the
|
|
207
|
+
installed collection.
|
|
208
|
+
|
|
209
|
+
**Phosphor (`ph`) joins the semantic map**, with all 83 names verified against the real icon set.
|
|
210
|
+
Phosphor keeps every weight in one collection and varies the name (`list`, `list-bold`), unlike Font
|
|
211
|
+
Awesome's per-weight collections, so `resolveIcon` and `generateIconInclude` gained a `weight`
|
|
212
|
+
option for sets shaped that way.
|
|
213
|
+
|
|
214
|
+
Fixes: `site.icons` was a closed object, so any icon collection not in its hand-written key list was
|
|
215
|
+
silently dropped during validation — your config passed and the icons never bundled. It accepts any
|
|
216
|
+
collection name now.
|
|
217
|
+
|
|
218
|
+
- bf453b0: Maskable favicons are now opaque, so they stop rendering as a logo in a black box.
|
|
219
|
+
|
|
220
|
+
`icon-mask.png` is declared `purpose: "maskable"` and `apple-touch-icon.png` is linked on every
|
|
221
|
+
page. Both sit a deliberately-inset logo on a larger canvas — that inset **is** the maskable safe
|
|
222
|
+
zone and was always correct — but the canvas was filled with `alpha: 0`. From any transparent
|
|
223
|
+
source that left 36% of `icon-mask.png` and 40% of `apple-touch-icon.png` transparent, on two files
|
|
224
|
+
whose entire contract is full bleed: the OS crops them to its own shape and composites the rest onto
|
|
225
|
+
whatever it likes, usually black. iOS discards alpha on the apple-touch-icon outright.
|
|
226
|
+
|
|
227
|
+
`backgroundColor` was already the obvious input for this and already plumbed — as far as the web
|
|
228
|
+
manifest, and no further. So the raster and the manifest disagreed about the very colour meant to
|
|
229
|
+
sit behind the icon.
|
|
230
|
+
|
|
231
|
+
Now:
|
|
232
|
+
- those two outputs are composited onto `backgroundColor`, defaulting to `#ffffff` — the same
|
|
233
|
+
default the manifest's `background_color` already used;
|
|
234
|
+
- `favicon.svg`, `icon-{16,32,192,512}.png` and `favicon.ico` keep the source's transparency, since
|
|
235
|
+
none of them is maskable;
|
|
236
|
+
- a transparent source with no `backgroundColor` set now warns, rather than silently inheriting
|
|
237
|
+
white — a dark logo would lose against it;
|
|
238
|
+
- `icon-192`/`icon-512` declare `purpose: "any"` explicitly. Omitting it was legal (unset means
|
|
239
|
+
"any"), but with a maskable present and nothing claiming "any", some launchers picked the maskable
|
|
240
|
+
— the one with the safe-zone inset — for slots that wanted a plain icon.
|
|
241
|
+
|
|
242
|
+
`vitops favicon` gained `--background <hex>`; it previously had no way to set this at all.
|
|
243
|
+
`getvitops({ favicon })` and the Vite plugin now forward the option they were already accepting.
|
|
244
|
+
|
|
245
|
+
### Patch Changes
|
|
246
|
+
|
|
247
|
+
- Updated dependencies [4756788]
|
|
248
|
+
- Updated dependencies [bf453b0]
|
|
249
|
+
- Updated dependencies [bf453b0]
|
|
250
|
+
- Updated dependencies [bf453b0]
|
|
251
|
+
- Updated dependencies [bf453b0]
|
|
252
|
+
- Updated dependencies [bf453b0]
|
|
253
|
+
- Updated dependencies [bf453b0]
|
|
254
|
+
- Updated dependencies [bf453b0]
|
|
255
|
+
- Updated dependencies [bf453b0]
|
|
256
|
+
- Updated dependencies [04a51d8]
|
|
257
|
+
- Updated dependencies [bf453b0]
|
|
258
|
+
- Updated dependencies [bf453b0]
|
|
259
|
+
- Updated dependencies [bf453b0]
|
|
260
|
+
- Updated dependencies [bf453b0]
|
|
261
|
+
- Updated dependencies [bf453b0]
|
|
262
|
+
- Updated dependencies [bf453b0]
|
|
263
|
+
- Updated dependencies [bf453b0]
|
|
264
|
+
- Updated dependencies [bf453b0]
|
|
265
|
+
- Updated dependencies [bf453b0]
|
|
266
|
+
- @getvitops/generator@2.0.0
|
|
267
|
+
- @getvitops/utils@2.0.0
|
|
268
|
+
|
|
3
269
|
## 1.0.0
|
|
4
270
|
|
|
5
271
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ import vitops from '@getvitops/vite';
|
|
|
10
10
|
export default {
|
|
11
11
|
plugins: [
|
|
12
12
|
vitops({
|
|
13
|
-
input: 'design-system.json', // default
|
|
13
|
+
input: 'design-system.json', // default — or a site config (company.json) that embeds one
|
|
14
14
|
format: 'tailwind', // 'bricks' | 'css' | 'tailwind' (default: 'tailwind')
|
|
15
15
|
out: 'src/styles', // default
|
|
16
16
|
}),
|
|
@@ -18,6 +18,11 @@ export default {
|
|
|
18
18
|
};
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
+
`input` accepts either config kind. Pointed at a site config it builds
|
|
22
|
+
`designSystem.themes[theme]` (`theme` option; default the config's `defaultTheme`, else `default`)
|
|
23
|
+
and reads the site-level facts from the same file — so `legal: {}` needs no `input` of its own, and
|
|
24
|
+
the theme editor's **Save to source** writes back into that theme rather than the file root.
|
|
25
|
+
|
|
21
26
|
Then import the generated stylesheet (Tailwind v4):
|
|
22
27
|
|
|
23
28
|
```css
|
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,47 @@
|
|
|
1
1
|
import { Format, LegalOutput } from "@getvitops/generator";
|
|
2
|
+
import { OutputKind } from "@getvitops/utils/media";
|
|
2
3
|
import { Plugin } from "vite";
|
|
3
4
|
|
|
4
5
|
//#region src/index.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* Where the design system lives inside the config file on disk.
|
|
8
|
+
*
|
|
9
|
+
* `[]` for a `design-system.json` — the file *is* the design system. For a site
|
|
10
|
+
* config it is under `designSystem`, and this reads the **raw** object rather
|
|
11
|
+
* than the resolved one because the editor's write-back has to land where the
|
|
12
|
+
* author actually put it. `resolveSiteConfig` normalises the two shorthands in
|
|
13
|
+
* memory; a writer that assumed the canonical shape would grow a second
|
|
14
|
+
* `themes` key beside the author's bare map and silently stop editing anything.
|
|
15
|
+
*/
|
|
16
|
+
declare function designSystemPath(raw: unknown, theme?: string): string[];
|
|
17
|
+
declare const getAt: (obj: unknown, path: string[]) => unknown;
|
|
18
|
+
/** Return a copy of `obj` with `path` replaced by `value`. Creates missing objects. */
|
|
19
|
+
declare function setAt(obj: unknown, path: string[], value: unknown): unknown;
|
|
20
|
+
interface VitopsMediaOptions {
|
|
21
|
+
/** Directory of unprocessed video, walked recursively. Default: 'raw'. */
|
|
22
|
+
raw?: string;
|
|
23
|
+
/**
|
|
24
|
+
* Where encoded outputs go. Default: `'src/assets/processed'`.
|
|
25
|
+
*
|
|
26
|
+
* Under `src/` so Vite content-hashes each file and it can be served immutable;
|
|
27
|
+
* the raw sources stay outside anything Vite scans.
|
|
28
|
+
*/
|
|
29
|
+
out?: string;
|
|
30
|
+
/** Cap the output width, keeping aspect ratio. Default 1920; 0 disables. */
|
|
31
|
+
maxWidth?: number;
|
|
32
|
+
/** Quality on VP9's CRF scale (0–63, lower is better). Default 32. */
|
|
33
|
+
crf?: number;
|
|
34
|
+
/** Optional bitrate ceiling, e.g. `'2M'`. Absent means constant quality. */
|
|
35
|
+
maxBitrate?: string;
|
|
36
|
+
/** Keep the audio track. Default false. */
|
|
37
|
+
audio?: boolean;
|
|
38
|
+
/** Timestamp (seconds) the poster frame comes from. Default 0. */
|
|
39
|
+
posterTime?: number;
|
|
40
|
+
/** Which outputs to produce. Default: webm, mp4 and a poster. */
|
|
41
|
+
outputs?: OutputKind[];
|
|
42
|
+
/** Cache file. Default `.vitops/media-manifest.json`. */
|
|
43
|
+
manifest?: string;
|
|
44
|
+
}
|
|
5
45
|
interface VitopsFaviconOptions {
|
|
6
46
|
/** Source SVG or PNG. */
|
|
7
47
|
source: string;
|
|
@@ -17,14 +57,45 @@ interface VitopsFaviconOptions {
|
|
|
17
57
|
backgroundColor?: string;
|
|
18
58
|
}
|
|
19
59
|
interface VitopsPluginOptions {
|
|
20
|
-
/**
|
|
60
|
+
/**
|
|
61
|
+
* Path to the config. Default: `'design-system.json'`.
|
|
62
|
+
*
|
|
63
|
+
* May be a `design-system.json` **or** the larger site config that embeds one
|
|
64
|
+
* (`company.json` / `site.json`) — told apart by shape. Pointing it at a site
|
|
65
|
+
* config also supplies `site` below, so a consumer who keeps their tokens
|
|
66
|
+
* there declares the path once instead of three times.
|
|
67
|
+
*/
|
|
21
68
|
input?: string;
|
|
69
|
+
/**
|
|
70
|
+
* Which `designSystem.themes` entry to build, when `input` is a site config.
|
|
71
|
+
* Default: the config's `defaultTheme`, else `default`.
|
|
72
|
+
*
|
|
73
|
+
* Also the theme `<wc-theme-editor>`'s **Save to source** writes back into.
|
|
74
|
+
*/
|
|
75
|
+
theme?: string;
|
|
22
76
|
/** Output target. Default: 'tailwind' (the EmDash/Astro case). */
|
|
23
77
|
format?: Format;
|
|
24
78
|
/** Directory to write generated output into. Default: 'src/styles'. */
|
|
25
79
|
out?: string;
|
|
26
80
|
/** Also generate a favicon set on build from this source image. */
|
|
27
81
|
favicon?: VitopsFaviconOptions;
|
|
82
|
+
/**
|
|
83
|
+
* Also encode raw video on build, into WebM + MP4 + a poster frame.
|
|
84
|
+
*
|
|
85
|
+
* Runs inside this pass rather than in the Astro integration's `config:setup`
|
|
86
|
+
* hook, for the same reason the manifest and sprite copies do: it produces bytes,
|
|
87
|
+
* so it belongs where it cannot race the first generation.
|
|
88
|
+
*
|
|
89
|
+
* Cached on source content and encode settings, so re-running costs a hash per
|
|
90
|
+
* file. Needs `ffmpeg` on PATH and fails loudly without it — an unencoded hero
|
|
91
|
+
* video is not a degraded build, it is a broken page.
|
|
92
|
+
*
|
|
93
|
+
* The raw directory is deliberately **not** watched: `watched()` matches config
|
|
94
|
+
* files by exact path, and a multi-minute encode triggered mid-dev-server is a
|
|
95
|
+
* poor trade for it. A video added during dev arrives on restart, or via
|
|
96
|
+
* `vitops media`.
|
|
97
|
+
*/
|
|
98
|
+
media?: VitopsMediaOptions;
|
|
28
99
|
/**
|
|
29
100
|
* Directory to mirror `design-manifest.json` into as `vitops/design-manifest.json`
|
|
30
101
|
* — the URL `<wc-theme-editor>` fetches by default. Set by the Astro integration's
|
|
@@ -32,18 +103,52 @@ interface VitopsPluginOptions {
|
|
|
32
103
|
* it can't race the first generation and can't go stale on regeneration.
|
|
33
104
|
*/
|
|
34
105
|
editorManifestDir?: string;
|
|
106
|
+
/**
|
|
107
|
+
* Directory to mirror the generated `icons.svg` into as `vitops/icons.svg` —
|
|
108
|
+
* the URL `<Icon />` targets under the `sprite` engine. Same reasoning as
|
|
109
|
+
* `editorManifestDir`: copied inside the generate pass, so it can't race a
|
|
110
|
+
* cold start or go stale after a config edit. The *href* is a plain string
|
|
111
|
+
* decided at config:setup; only the bytes arrive here.
|
|
112
|
+
*/
|
|
113
|
+
spriteDir?: string;
|
|
35
114
|
/**
|
|
36
115
|
* Also render the site's legal documents on build, and re-render when the site
|
|
37
116
|
* config changes.
|
|
38
117
|
*
|
|
39
|
-
* Its own option
|
|
40
|
-
*
|
|
118
|
+
* Its own option because it is an *output* you opt into — but its `input` is
|
|
119
|
+
* optional, defaulting to `site.input`, then to `input` when that is itself a
|
|
120
|
+
* site config. `legal: {}` is therefore the whole declaration in the common case.
|
|
41
121
|
*/
|
|
42
122
|
legal?: VitopsLegalOptions;
|
|
123
|
+
/**
|
|
124
|
+
* The site config, for the parts of generation that depend on site-level facts
|
|
125
|
+
* rather than on the design system — currently `designSystem.defaultColorScheme`, which
|
|
126
|
+
* decides whether the colour layer carries a `prefers-color-scheme` block.
|
|
127
|
+
*
|
|
128
|
+
* Only needed when the site config is a *different* file from `input`; when
|
|
129
|
+
* `input` is itself a site config, that one is used. Watched, so an edit
|
|
130
|
+
* regenerates.
|
|
131
|
+
*/
|
|
132
|
+
site?: VitopsSiteOptions;
|
|
133
|
+
/**
|
|
134
|
+
* Emit the `prefers-color-scheme: dark` block. Normally read from the site
|
|
135
|
+
* config's `designSystem.defaultColorScheme: "system"`; set here it wins, for consumers with no
|
|
136
|
+
* site config.
|
|
137
|
+
*/
|
|
138
|
+
systemColorScheme?: boolean;
|
|
43
139
|
}
|
|
44
|
-
interface
|
|
140
|
+
interface VitopsSiteOptions {
|
|
45
141
|
/** Path to the site config (JSON). */
|
|
46
142
|
input: string;
|
|
143
|
+
/** Environment whose A/B variant applies. Default: 'production'. */
|
|
144
|
+
siteEnv?: string;
|
|
145
|
+
}
|
|
146
|
+
interface VitopsLegalOptions {
|
|
147
|
+
/**
|
|
148
|
+
* Path to the site config (JSON). Optional: defaults to `site.input`, then to
|
|
149
|
+
* the plugin's own `input` when that is itself a site config.
|
|
150
|
+
*/
|
|
151
|
+
input?: string;
|
|
47
152
|
/** Directory to write documents into. Default: 'src/content/legal'. */
|
|
48
153
|
out?: string;
|
|
49
154
|
/** Output format. Default: 'md', which suits an Astro content collection. */
|
|
@@ -53,5 +158,5 @@ interface VitopsLegalOptions {
|
|
|
53
158
|
}
|
|
54
159
|
declare function vitops(options?: VitopsPluginOptions): Plugin;
|
|
55
160
|
//#endregion
|
|
56
|
-
export { VitopsFaviconOptions, VitopsLegalOptions, VitopsPluginOptions, vitops as default };
|
|
161
|
+
export { VitopsFaviconOptions, VitopsLegalOptions, VitopsMediaOptions, VitopsPluginOptions, VitopsSiteOptions, vitops as default, designSystemPath, getAt, setAt };
|
|
57
162
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;;;AA6CA;;;;AAAgE;AAWhE;;;iBAXgB,gBAAA,CAAiB,GAAA,WAAc,KAAiB;AAAA,cAWnD,KAAA,GAAS,GAAA,WAAc,IAAc;AAIlD;AAAA,iBAAgB,KAAA,CAAM,GAAA,WAAc,IAAA,YAAgB,KAAA;AAAA,UAyDnC,kBAAA;EAzDI;EA2DnB,GAAA;EA3DkC;;;AAA8B;AAyDlE;;EASE,GAAA;EAYoB;EAVpB,QAAA;EAFA;EAIA,GAAA;EAAA;EAEA,UAAA;EAEA;EAAA,KAAA;EAIA;EAFA,UAAA;EAIA;EAFA,OAAA,GAAU,UAAU;EAEZ;EAAR,QAAA;AAAA;AAAA,UAGe,oBAAA;EAAoB;EAEnC,MAAA;EAEA;EAAA,YAAA;EAIA;EAFA,GAAA;EAMA;EAJA,IAAA;EAIe;EAFf,UAAA;EAKkC;EAHlC,eAAA;AAAA;AAAA,UAGe,mBAAA;EAuCP;;;;;;;;EA9BR,KAAA;EAWA;;;;;;EAJA,KAAA;EA+CA;EA7CA,MAAA,GAAS,MAAA;EAuDT;EArDA,GAAA;EA2DA;EAzDA,OAAA,GAAU,oBAAA;EAyDO;AAGnB;;;;AAIS;AAGT;;;;;;;;;;EAlDE,KAAA,GAAQ,kBAAA;EAgEc;;;;;;EAzDtB,iBAAA;EAyDuE;AAAA;;;;;;EAjDvE,SAAA;;;;;;;;;EASA,KAAA,GAAQ,kBAAA;;;;;;;;;;EAUR,IAAA,GAAO,iBAAA;;;;;;EAMP,iBAAA;AAAA;AAAA,UAGe,iBAAA;;EAEf,KAAA;;EAEA,OAAO;AAAA;AAAA,UAGQ,kBAAA;;;;;EAKf,KAAA;;EAEA,GAAA;;EAEA,MAAA,GAAS,WAAW;;EAEpB,OAAA;AAAA;AAAA,iBAGsB,MAAA,CAAO,OAAA,GAAS,mBAAA,GAA2B,MAAM"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
|
-
import { generate, generateLegal, resolveSiteConfig, validate } from "@getvitops/generator";
|
|
3
|
+
import { generate, generateLegal, isSiteConfig, resolveSiteConfig, validate } from "@getvitops/generator";
|
|
4
4
|
import { generateFavicons, writeFaviconManifest } from "@getvitops/utils";
|
|
5
|
+
import { MEDIA_MANIFEST_PATH, processMedia } from "@getvitops/utils/media";
|
|
5
6
|
|
|
6
7
|
//#region src/index.ts
|
|
7
8
|
/**
|
|
8
9
|
* @getvitops/vite — a Vite plugin that generates Vitops design-system output from a
|
|
9
|
-
* `design-system.json`
|
|
10
|
-
* the config changes in dev.
|
|
10
|
+
* `design-system.json` (or from the larger site config that embeds one) during a
|
|
11
|
+
* Vite/Astro (EmDash) build, and hot-regenerates when the config changes in dev.
|
|
11
12
|
*
|
|
12
13
|
* import vitops from '@getvitops/vite';
|
|
13
14
|
* export default { plugins: [vitops({ input: 'design-system.json', format: 'tailwind', out: 'src/styles' })] };
|
|
@@ -17,6 +18,40 @@ import { generateFavicons, writeFaviconManifest } from "@getvitops/utils";
|
|
|
17
18
|
/** Endpoint `<wc-theme-editor>` probes for, and POSTs its patch to. */
|
|
18
19
|
const EDITOR_ENDPOINT = "/__vitops/design-system";
|
|
19
20
|
/**
|
|
21
|
+
* Where the design system lives inside the config file on disk.
|
|
22
|
+
*
|
|
23
|
+
* `[]` for a `design-system.json` — the file *is* the design system. For a site
|
|
24
|
+
* config it is under `designSystem`, and this reads the **raw** object rather
|
|
25
|
+
* than the resolved one because the editor's write-back has to land where the
|
|
26
|
+
* author actually put it. `resolveSiteConfig` normalises the two shorthands in
|
|
27
|
+
* memory; a writer that assumed the canonical shape would grow a second
|
|
28
|
+
* `themes` key beside the author's bare map and silently stop editing anything.
|
|
29
|
+
*/
|
|
30
|
+
function designSystemPath(raw, theme = "default") {
|
|
31
|
+
if (!isSiteConfig(raw)) return [];
|
|
32
|
+
const ds = raw.designSystem;
|
|
33
|
+
if (ds != null && typeof ds === "object") {
|
|
34
|
+
if ("themes" in ds) return [
|
|
35
|
+
"designSystem",
|
|
36
|
+
"themes",
|
|
37
|
+
theme
|
|
38
|
+
];
|
|
39
|
+
if ("colors" in ds) return ["designSystem"];
|
|
40
|
+
}
|
|
41
|
+
return ["designSystem", theme];
|
|
42
|
+
}
|
|
43
|
+
const getAt = (obj, path) => path.reduce((cur, k) => cur?.[k], obj);
|
|
44
|
+
/** Return a copy of `obj` with `path` replaced by `value`. Creates missing objects. */
|
|
45
|
+
function setAt(obj, path, value) {
|
|
46
|
+
if (!path.length) return value;
|
|
47
|
+
const [head, ...rest] = path;
|
|
48
|
+
const base = typeof obj === "object" && obj !== null ? obj : {};
|
|
49
|
+
return {
|
|
50
|
+
...base,
|
|
51
|
+
[head]: setAt(base[head], rest, value)
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
20
55
|
* Recursively merge a patch into a config. Plain objects merge key-wise;
|
|
21
56
|
* everything else (scalars, and arrays — a token list is replaced, not appended)
|
|
22
57
|
* is overwritten by the patch.
|
|
@@ -64,17 +99,27 @@ function vitops(options = {}) {
|
|
|
64
99
|
const format = options.format ?? "tailwind";
|
|
65
100
|
const outDir = options.out ?? "src/styles";
|
|
66
101
|
let input = resolve(options.input ?? "design-system.json");
|
|
67
|
-
let legalInput = options.legal ? resolve(options.legal.input) : "";
|
|
102
|
+
let legalInput = options.legal?.input ? resolve(options.legal.input) : "";
|
|
103
|
+
let siteInput = options.site ? resolve(options.site.input) : "";
|
|
68
104
|
let root = "";
|
|
105
|
+
const readJson = (p) => JSON.parse(readFileSync(p, "utf8"));
|
|
69
106
|
const run = async () => {
|
|
107
|
+
const raw = readJson(input);
|
|
108
|
+
const inputIsSite = isSiteConfig(raw);
|
|
70
109
|
await generate({
|
|
71
|
-
input,
|
|
110
|
+
input: raw,
|
|
72
111
|
format,
|
|
73
|
-
outDir
|
|
112
|
+
outDir,
|
|
113
|
+
...options.theme != null ? { theme: options.theme } : {},
|
|
114
|
+
siteEnv: options.site?.siteEnv ?? "production",
|
|
115
|
+
...options.systemColorScheme != null ? { systemColorScheme: options.systemColorScheme } : {},
|
|
116
|
+
...siteInput ? { site: readJson(siteInput) } : {}
|
|
74
117
|
});
|
|
75
118
|
if (options.legal) {
|
|
119
|
+
const legalPath = legalInput || siteInput || (inputIsSite ? input : "");
|
|
120
|
+
if (!legalPath) throw new Error("[vitops] legal: needs a site config — set `legal.input`, or the `site` option, or point `input` at a site config.");
|
|
76
121
|
const legalOut = resolve(root, options.legal.out ?? "src/content/legal");
|
|
77
|
-
const files = generateLegal(resolveSiteConfig(
|
|
122
|
+
const files = generateLegal(resolveSiteConfig(readJson(legalPath), options.legal.siteEnv ?? "production"), { output: options.legal.format ?? "md" });
|
|
78
123
|
mkdirSync(legalOut, { recursive: true });
|
|
79
124
|
for (const [name, content] of Object.entries(files)) writeFileSync(join(legalOut, name), content);
|
|
80
125
|
}
|
|
@@ -86,12 +131,21 @@ function vitops(options = {}) {
|
|
|
86
131
|
copyFileSync(src, join(dest, "design-manifest.json"));
|
|
87
132
|
}
|
|
88
133
|
}
|
|
134
|
+
if (options.spriteDir) {
|
|
135
|
+
const src = resolve(root, outDir, "icons.svg");
|
|
136
|
+
if (existsSync(src)) {
|
|
137
|
+
const dest = resolve(root, options.spriteDir, "vitops");
|
|
138
|
+
mkdirSync(dest, { recursive: true });
|
|
139
|
+
copyFileSync(src, join(dest, "icons.svg"));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
89
142
|
if (options.favicon) {
|
|
90
143
|
const faviconOut = resolve(root, options.favicon.out ?? "public");
|
|
91
144
|
await generateFavicons({
|
|
92
145
|
source: resolve(root, options.favicon.source),
|
|
93
146
|
outputDir: faviconOut,
|
|
94
|
-
...options.favicon.lowResSource ? { lowResSource: resolve(root, options.favicon.lowResSource) } : {}
|
|
147
|
+
...options.favicon.lowResSource ? { lowResSource: resolve(root, options.favicon.lowResSource) } : {},
|
|
148
|
+
...options.favicon.backgroundColor ? { backgroundColor: options.favicon.backgroundColor } : {}
|
|
95
149
|
});
|
|
96
150
|
if (options.favicon.name && options.favicon.themeColor) await writeFaviconManifest(faviconOut, {
|
|
97
151
|
name: options.favicon.name,
|
|
@@ -99,14 +153,38 @@ function vitops(options = {}) {
|
|
|
99
153
|
...options.favicon.backgroundColor ? { backgroundColor: options.favicon.backgroundColor } : {}
|
|
100
154
|
});
|
|
101
155
|
}
|
|
156
|
+
if (options.media) {
|
|
157
|
+
const media = options.media;
|
|
158
|
+
const config = {
|
|
159
|
+
...media.maxWidth != null ? { maxWidth: media.maxWidth } : {},
|
|
160
|
+
...media.crf != null ? { crf: media.crf } : {},
|
|
161
|
+
...media.maxBitrate ? { maxBitrate: media.maxBitrate } : {},
|
|
162
|
+
...media.audio != null ? { audio: media.audio } : {},
|
|
163
|
+
...media.posterTime != null ? { posterTime: media.posterTime } : {},
|
|
164
|
+
...media.outputs ? { outputs: media.outputs } : {}
|
|
165
|
+
};
|
|
166
|
+
const result = await processMedia({
|
|
167
|
+
raw: resolve(root, media.raw ?? "raw"),
|
|
168
|
+
out: resolve(root, media.out ?? "src/assets/processed"),
|
|
169
|
+
config,
|
|
170
|
+
manifest: resolve(root, media.manifest ?? MEDIA_MANIFEST_PATH),
|
|
171
|
+
onProgress: (message) => console.log(`[vitops] media: ${message}`)
|
|
172
|
+
});
|
|
173
|
+
if (result.written.length > 0) console.log(`[vitops] media: ${result.written.length} written, ${result.skipped} unchanged`);
|
|
174
|
+
}
|
|
102
175
|
};
|
|
103
|
-
const watched = () =>
|
|
176
|
+
const watched = () => [...new Set([
|
|
177
|
+
input,
|
|
178
|
+
siteInput,
|
|
179
|
+
legalInput
|
|
180
|
+
].filter(Boolean))];
|
|
104
181
|
return {
|
|
105
182
|
name: "@getvitops/vite",
|
|
106
183
|
configResolved(config) {
|
|
107
184
|
root = config.root;
|
|
108
185
|
input = resolve(config.root, options.input ?? "design-system.json");
|
|
109
|
-
if (options.legal) legalInput = resolve(config.root, options.legal.input);
|
|
186
|
+
if (options.legal?.input) legalInput = resolve(config.root, options.legal.input);
|
|
187
|
+
if (options.site) siteInput = resolve(config.root, options.site.input);
|
|
110
188
|
},
|
|
111
189
|
async buildStart() {
|
|
112
190
|
for (const f of watched()) this.addWatchFile(f);
|
|
@@ -129,10 +207,13 @@ function vitops(options = {}) {
|
|
|
129
207
|
res.end(JSON.stringify(body));
|
|
130
208
|
};
|
|
131
209
|
if (req.method === "GET") try {
|
|
210
|
+
const current = readJson(input);
|
|
211
|
+
const at = designSystemPath(current, options.theme);
|
|
132
212
|
return json(200, {
|
|
133
213
|
writable: true,
|
|
134
214
|
path: input,
|
|
135
|
-
|
|
215
|
+
at,
|
|
216
|
+
config: getAt(current, at)
|
|
136
217
|
});
|
|
137
218
|
} catch (err) {
|
|
138
219
|
return json(500, { error: err.message });
|
|
@@ -144,10 +225,12 @@ function vitops(options = {}) {
|
|
|
144
225
|
try {
|
|
145
226
|
const { patch } = JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
146
227
|
if (patch == null || typeof patch !== "object") return json(400, { errors: ["body must be { patch: object }"] });
|
|
147
|
-
const
|
|
228
|
+
const current = readJson(input);
|
|
229
|
+
const at = designSystemPath(current, options.theme);
|
|
230
|
+
const merged = deepMerge(getAt(current, at), patch);
|
|
148
231
|
const result = validate(merged);
|
|
149
232
|
if (!result.ok) return json(400, { errors: result.errors.map((e) => `${e.path.join(".") || "(root)"}: ${e.message}`) });
|
|
150
|
-
writeFileSync(input, `${formatConfig(merged)}\n`);
|
|
233
|
+
writeFileSync(input, `${formatConfig(setAt(current, at, merged))}\n`);
|
|
151
234
|
return json(200, {
|
|
152
235
|
ok: true,
|
|
153
236
|
warnings: result.warnings
|
|
@@ -162,5 +245,5 @@ function vitops(options = {}) {
|
|
|
162
245
|
}
|
|
163
246
|
|
|
164
247
|
//#endregion
|
|
165
|
-
export { vitops as default };
|
|
248
|
+
export { vitops as default, designSystemPath, getAt, setAt };
|
|
166
249
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * @getvitops/vite — a Vite plugin that generates Vitops design-system output from a\n * `design-system.json` during a Vite/Astro (EmDash) build, and hot-regenerates when\n * the config changes in dev.\n *\n * import vitops from '@getvitops/vite';\n * export default { plugins: [vitops({ input: 'design-system.json', format: 'tailwind', out: 'src/styles' })] };\n *\n * Thin wrapper over @getvitops/generator — the same generator the CLI uses.\n */\nimport { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { join, resolve } from 'node:path';\nimport type { Plugin } from 'vite';\nimport {\n generate,\n generateLegal,\n resolveSiteConfig,\n validate,\n type Format,\n type LegalOutput,\n} from '@getvitops/generator';\nimport { generateFavicons, writeFaviconManifest } from '@getvitops/utils';\n\n/** Endpoint `<wc-theme-editor>` probes for, and POSTs its patch to. */\nconst EDITOR_ENDPOINT = '/__vitops/design-system';\n\n/**\n * Recursively merge a patch into a config. Plain objects merge key-wise;\n * everything else (scalars, and arrays — a token list is replaced, not appended)\n * is overwritten by the patch.\n */\nfunction deepMerge(base: unknown, patch: unknown): unknown {\n const isPlain = (v: unknown): v is Record<string, unknown> =>\n typeof v === 'object' && v !== null && !Array.isArray(v);\n if (!isPlain(base) || !isPlain(patch)) return patch;\n const out: Record<string, unknown> = { ...base };\n for (const [k, v] of Object.entries(patch)) out[k] = deepMerge(base[k], v);\n return out;\n}\n\n/** Printed width the config is formatted to — matches the repo formatter's default. */\nconst PRINT_WIDTH = 100;\n\n/**\n * Serialize a config the way a formatter would: 2-space indent, but arrays of\n * scalars kept on one line when they fit.\n *\n * `JSON.stringify(v, null, 2)` explodes every array onto its own lines, so writing\n * back a two-key patch reflowed ~300 lines of `design-system.json` — a diff nobody\n * can review, for a change of two values. Matching the prevailing style keeps the\n * write surgical, which is the whole point of saving to source rather than copying\n * a patch by hand.\n */\nfunction formatConfig(value: unknown, col = 0, indent = 0): string {\n const pad = ' '.repeat(indent);\n if (Array.isArray(value)) {\n if (!value.length) return '[]';\n if (value.every((v) => v === null || typeof v !== 'object')) {\n const inline = `[${value.map((v) => JSON.stringify(v)).join(', ')}]`;\n if (col + inline.length <= PRINT_WIDTH) return inline;\n }\n const items = value.map((v) => `${pad} ${formatConfig(v, indent + 2, indent + 2)}`);\n return `[\\n${items.join(',\\n')}\\n${pad}]`;\n }\n if (value !== null && typeof value === 'object') {\n const entries = Object.entries(value as Record<string, unknown>);\n if (!entries.length) return '{}';\n const items = entries.map(([k, v]) => {\n const head = `${pad} ${JSON.stringify(k)}: `;\n return head + formatConfig(v, head.length, indent + 2);\n });\n return `{\\n${items.join(',\\n')}\\n${pad}}`;\n }\n return JSON.stringify(value) ?? 'null';\n}\n\nexport interface VitopsFaviconOptions {\n /** Source SVG or PNG. */\n source: string;\n /** Optional simplified source for the 16px icon. */\n lowResSource?: string;\n /** Directory to write the favicon set into. Default: 'public'. */\n out?: string;\n /** App name — when set (with `themeColor`), also emit `site.webmanifest`. */\n name?: string;\n /** PWA theme color (also drives `<meta name=\"theme-color\">`). */\n themeColor?: string;\n /** PWA background color. */\n backgroundColor?: string;\n}\n\nexport interface VitopsPluginOptions {\n /** Path to the design-system.json. Default: 'design-system.json'. */\n input?: string;\n /** Output target. Default: 'tailwind' (the EmDash/Astro case). */\n format?: Format;\n /** Directory to write generated output into. Default: 'src/styles'. */\n out?: string;\n /** Also generate a favicon set on build from this source image. */\n favicon?: VitopsFaviconOptions;\n /**\n * Directory to mirror `design-manifest.json` into as `vitops/design-manifest.json`\n * — the URL `<wc-theme-editor>` fetches by default. Set by the Astro integration's\n * `editor` option. Copied inside the generate pass rather than by the caller, so\n * it can't race the first generation and can't go stale on regeneration.\n */\n editorManifestDir?: string;\n /**\n * Also render the site's legal documents on build, and re-render when the site\n * config changes.\n *\n * Its own option rather than part of the design-system pass because it reads a\n * different config kind — a `SiteConfig`, not a `design-system.json`.\n */\n legal?: VitopsLegalOptions;\n}\n\nexport interface VitopsLegalOptions {\n /** Path to the site config (JSON). */\n input: string;\n /** Directory to write documents into. Default: 'src/content/legal'. */\n out?: string;\n /** Output format. Default: 'md', which suits an Astro content collection. */\n format?: LegalOutput;\n /** Environment whose A/B variant applies. Default: 'production'. */\n siteEnv?: string;\n}\n\nexport default function vitops(options: VitopsPluginOptions = {}): Plugin {\n const format: Format = options.format ?? 'tailwind';\n const outDir = options.out ?? 'src/styles';\n let input = resolve(options.input ?? 'design-system.json');\n let legalInput = options.legal ? resolve(options.legal.input) : '';\n let root = '';\n\n const run = async () => {\n await generate({ input, format, outDir });\n if (options.legal) {\n const legalOut = resolve(root, options.legal.out ?? 'src/content/legal');\n const site = resolveSiteConfig(\n JSON.parse(readFileSync(legalInput, 'utf8')),\n options.legal.siteEnv ?? 'production',\n );\n const files = generateLegal(site, { output: options.legal.format ?? 'md' });\n mkdirSync(legalOut, { recursive: true });\n for (const [name, content] of Object.entries(files))\n writeFileSync(join(legalOut, name), content);\n }\n if (options.editorManifestDir) {\n const src = resolve(root, outDir, 'design-manifest.json');\n if (existsSync(src)) {\n const dest = resolve(root, options.editorManifestDir, 'vitops');\n mkdirSync(dest, { recursive: true });\n copyFileSync(src, join(dest, 'design-manifest.json'));\n }\n }\n if (options.favicon) {\n const faviconOut = resolve(root, options.favicon.out ?? 'public');\n await generateFavicons({\n source: resolve(root, options.favicon.source),\n outputDir: faviconOut,\n ...(options.favicon.lowResSource\n ? { lowResSource: resolve(root, options.favicon.lowResSource) }\n : {}),\n });\n if (options.favicon.name && options.favicon.themeColor) {\n await writeFaviconManifest(faviconOut, {\n name: options.favicon.name,\n themeColor: options.favicon.themeColor,\n ...(options.favicon.backgroundColor\n ? { backgroundColor: options.favicon.backgroundColor }\n : {}),\n });\n }\n }\n };\n\n // Both configs are inputs to `run()`, so both must trigger a re-run — a legal\n // document that only refreshes when the design system changes is worse than\n // one that never refreshes, because it looks current.\n const watched = () => (legalInput ? [input, legalInput] : [input]);\n\n return {\n name: '@getvitops/vite',\n // Resolve paths against Vite's project root once it's known.\n configResolved(config) {\n root = config.root;\n input = resolve(config.root, options.input ?? 'design-system.json');\n if (options.legal) legalInput = resolve(config.root, options.legal.input);\n },\n async buildStart() {\n for (const f of watched()) this.addWatchFile(f);\n await run();\n },\n async watchChange(id) {\n if (watched().includes(resolve(id))) await run();\n },\n configureServer(server) {\n for (const f of watched()) server.watcher.add(f);\n server.watcher.on('change', async (file) => {\n if (!watched().includes(resolve(file))) return;\n await run();\n server.ws.send({ type: 'full-reload' });\n });\n\n // Dev-only design-system write-back, for `<wc-theme-editor>`'s \"Save to\n // source\". Registered in configureServer only, so it exists in `vite dev`\n // and never in a build — on a static deploy the editor's probe 404s and the\n // button simply isn't rendered. The only writable path is `input`, already\n // resolved from config: a request supplies a patch, never a destination.\n server.middlewares.use(EDITOR_ENDPOINT, (req, res, next) => {\n const json = (status: number, body: unknown): void => {\n res.statusCode = status;\n res.setHeader('content-type', 'application/json');\n res.end(JSON.stringify(body));\n };\n\n if (req.method === 'GET') {\n try {\n return json(200, {\n writable: true,\n path: input,\n config: JSON.parse(readFileSync(input, 'utf8')),\n });\n } catch (err) {\n return json(500, { error: (err as Error).message });\n }\n }\n\n if (req.method !== 'POST') return next();\n\n const chunks: Buffer[] = [];\n req.on('data', (c: Buffer) => chunks.push(c));\n req.on('end', () => {\n try {\n const { patch } = JSON.parse(Buffer.concat(chunks).toString('utf8')) as {\n patch?: unknown;\n };\n if (patch == null || typeof patch !== 'object')\n return json(400, { errors: ['body must be { patch: object }'] });\n\n const current = JSON.parse(readFileSync(input, 'utf8')) as unknown;\n const merged = deepMerge(current, patch);\n // Validate the *merged* result, not the patch: a patch is a partial\n // by design, so it can't be validated on its own, and writing an\n // invalid config would break the very dev server doing the writing.\n const result = validate(merged);\n if (!result.ok)\n return json(400, {\n errors: result.errors.map((e) => `${e.path.join('.') || '(root)'}: ${e.message}`),\n });\n\n writeFileSync(input, `${formatConfig(merged)}\\n`);\n // The watcher above picks the write up and regenerates + reloads.\n return json(200, { ok: true, warnings: result.warnings });\n } catch (err) {\n return json(400, { errors: [(err as Error).message] });\n }\n });\n });\n },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * @getvitops/vite — a Vite plugin that generates Vitops design-system output from a\n * `design-system.json` (or from the larger site config that embeds one) during a\n * Vite/Astro (EmDash) build, and hot-regenerates when the config changes in dev.\n *\n * import vitops from '@getvitops/vite';\n * export default { plugins: [vitops({ input: 'design-system.json', format: 'tailwind', out: 'src/styles' })] };\n *\n * Thin wrapper over @getvitops/generator — the same generator the CLI uses.\n */\nimport { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { join, resolve } from 'node:path';\nimport type { Plugin } from 'vite';\nimport {\n generate,\n generateLegal,\n isSiteConfig,\n resolveSiteConfig,\n validate,\n type Format,\n type LegalOutput,\n} from '@getvitops/generator';\nimport { generateFavicons, writeFaviconManifest } from '@getvitops/utils';\n// Own subpath: it shells out to ffmpeg, and a project without a `media` option\n// should never load it.\nimport {\n MEDIA_MANIFEST_PATH,\n processMedia,\n type MediaConfig,\n type OutputKind,\n} from '@getvitops/utils/media';\n\n/** Endpoint `<wc-theme-editor>` probes for, and POSTs its patch to. */\nconst EDITOR_ENDPOINT = '/__vitops/design-system';\n\n/**\n * Where the design system lives inside the config file on disk.\n *\n * `[]` for a `design-system.json` — the file *is* the design system. For a site\n * config it is under `designSystem`, and this reads the **raw** object rather\n * than the resolved one because the editor's write-back has to land where the\n * author actually put it. `resolveSiteConfig` normalises the two shorthands in\n * memory; a writer that assumed the canonical shape would grow a second\n * `themes` key beside the author's bare map and silently stop editing anything.\n */\nexport function designSystemPath(raw: unknown, theme = 'default'): string[] {\n if (!isSiteConfig(raw)) return [];\n const ds = (raw as Record<string, unknown>).designSystem as Record<string, unknown>;\n if (ds != null && typeof ds === 'object') {\n if ('themes' in ds) return ['designSystem', 'themes', theme];\n // A bare `DesignSystem` written inline — one theme, and it is `default`.\n if ('colors' in ds) return ['designSystem'];\n }\n return ['designSystem', theme]; // the legacy bare theme map\n}\n\nexport const getAt = (obj: unknown, path: string[]): unknown =>\n path.reduce<unknown>((cur, k) => (cur as Record<string, unknown>)?.[k], obj);\n\n/** Return a copy of `obj` with `path` replaced by `value`. Creates missing objects. */\nexport function setAt(obj: unknown, path: string[], value: unknown): unknown {\n if (!path.length) return value;\n const [head, ...rest] = path as [string, ...string[]];\n const base = (typeof obj === 'object' && obj !== null ? obj : {}) as Record<string, unknown>;\n return { ...base, [head]: setAt(base[head], rest, value) };\n}\n\n/**\n * Recursively merge a patch into a config. Plain objects merge key-wise;\n * everything else (scalars, and arrays — a token list is replaced, not appended)\n * is overwritten by the patch.\n */\nfunction deepMerge(base: unknown, patch: unknown): unknown {\n const isPlain = (v: unknown): v is Record<string, unknown> =>\n typeof v === 'object' && v !== null && !Array.isArray(v);\n if (!isPlain(base) || !isPlain(patch)) return patch;\n const out: Record<string, unknown> = { ...base };\n for (const [k, v] of Object.entries(patch)) out[k] = deepMerge(base[k], v);\n return out;\n}\n\n/** Printed width the config is formatted to — matches the repo formatter's default. */\nconst PRINT_WIDTH = 100;\n\n/**\n * Serialize a config the way a formatter would: 2-space indent, but arrays of\n * scalars kept on one line when they fit.\n *\n * `JSON.stringify(v, null, 2)` explodes every array onto its own lines, so writing\n * back a two-key patch reflowed ~300 lines of `design-system.json` — a diff nobody\n * can review, for a change of two values. Matching the prevailing style keeps the\n * write surgical, which is the whole point of saving to source rather than copying\n * a patch by hand.\n */\nfunction formatConfig(value: unknown, col = 0, indent = 0): string {\n const pad = ' '.repeat(indent);\n if (Array.isArray(value)) {\n if (!value.length) return '[]';\n if (value.every((v) => v === null || typeof v !== 'object')) {\n const inline = `[${value.map((v) => JSON.stringify(v)).join(', ')}]`;\n if (col + inline.length <= PRINT_WIDTH) return inline;\n }\n const items = value.map((v) => `${pad} ${formatConfig(v, indent + 2, indent + 2)}`);\n return `[\\n${items.join(',\\n')}\\n${pad}]`;\n }\n if (value !== null && typeof value === 'object') {\n const entries = Object.entries(value as Record<string, unknown>);\n if (!entries.length) return '{}';\n const items = entries.map(([k, v]) => {\n const head = `${pad} ${JSON.stringify(k)}: `;\n return head + formatConfig(v, head.length, indent + 2);\n });\n return `{\\n${items.join(',\\n')}\\n${pad}}`;\n }\n return JSON.stringify(value) ?? 'null';\n}\n\nexport interface VitopsMediaOptions {\n /** Directory of unprocessed video, walked recursively. Default: 'raw'. */\n raw?: string;\n /**\n * Where encoded outputs go. Default: `'src/assets/processed'`.\n *\n * Under `src/` so Vite content-hashes each file and it can be served immutable;\n * the raw sources stay outside anything Vite scans.\n */\n out?: string;\n /** Cap the output width, keeping aspect ratio. Default 1920; 0 disables. */\n maxWidth?: number;\n /** Quality on VP9's CRF scale (0–63, lower is better). Default 32. */\n crf?: number;\n /** Optional bitrate ceiling, e.g. `'2M'`. Absent means constant quality. */\n maxBitrate?: string;\n /** Keep the audio track. Default false. */\n audio?: boolean;\n /** Timestamp (seconds) the poster frame comes from. Default 0. */\n posterTime?: number;\n /** Which outputs to produce. Default: webm, mp4 and a poster. */\n outputs?: OutputKind[];\n /** Cache file. Default `.vitops/media-manifest.json`. */\n manifest?: string;\n}\n\nexport interface VitopsFaviconOptions {\n /** Source SVG or PNG. */\n source: string;\n /** Optional simplified source for the 16px icon. */\n lowResSource?: string;\n /** Directory to write the favicon set into. Default: 'public'. */\n out?: string;\n /** App name — when set (with `themeColor`), also emit `site.webmanifest`. */\n name?: string;\n /** PWA theme color (also drives `<meta name=\"theme-color\">`). */\n themeColor?: string;\n /** PWA background color. */\n backgroundColor?: string;\n}\n\nexport interface VitopsPluginOptions {\n /**\n * Path to the config. Default: `'design-system.json'`.\n *\n * May be a `design-system.json` **or** the larger site config that embeds one\n * (`company.json` / `site.json`) — told apart by shape. Pointing it at a site\n * config also supplies `site` below, so a consumer who keeps their tokens\n * there declares the path once instead of three times.\n */\n input?: string;\n /**\n * Which `designSystem.themes` entry to build, when `input` is a site config.\n * Default: the config's `defaultTheme`, else `default`.\n *\n * Also the theme `<wc-theme-editor>`'s **Save to source** writes back into.\n */\n theme?: string;\n /** Output target. Default: 'tailwind' (the EmDash/Astro case). */\n format?: Format;\n /** Directory to write generated output into. Default: 'src/styles'. */\n out?: string;\n /** Also generate a favicon set on build from this source image. */\n favicon?: VitopsFaviconOptions;\n /**\n * Also encode raw video on build, into WebM + MP4 + a poster frame.\n *\n * Runs inside this pass rather than in the Astro integration's `config:setup`\n * hook, for the same reason the manifest and sprite copies do: it produces bytes,\n * so it belongs where it cannot race the first generation.\n *\n * Cached on source content and encode settings, so re-running costs a hash per\n * file. Needs `ffmpeg` on PATH and fails loudly without it — an unencoded hero\n * video is not a degraded build, it is a broken page.\n *\n * The raw directory is deliberately **not** watched: `watched()` matches config\n * files by exact path, and a multi-minute encode triggered mid-dev-server is a\n * poor trade for it. A video added during dev arrives on restart, or via\n * `vitops media`.\n */\n media?: VitopsMediaOptions;\n /**\n * Directory to mirror `design-manifest.json` into as `vitops/design-manifest.json`\n * — the URL `<wc-theme-editor>` fetches by default. Set by the Astro integration's\n * `editor` option. Copied inside the generate pass rather than by the caller, so\n * it can't race the first generation and can't go stale on regeneration.\n */\n editorManifestDir?: string;\n /**\n * Directory to mirror the generated `icons.svg` into as `vitops/icons.svg` —\n * the URL `<Icon />` targets under the `sprite` engine. Same reasoning as\n * `editorManifestDir`: copied inside the generate pass, so it can't race a\n * cold start or go stale after a config edit. The *href* is a plain string\n * decided at config:setup; only the bytes arrive here.\n */\n spriteDir?: string;\n /**\n * Also render the site's legal documents on build, and re-render when the site\n * config changes.\n *\n * Its own option because it is an *output* you opt into — but its `input` is\n * optional, defaulting to `site.input`, then to `input` when that is itself a\n * site config. `legal: {}` is therefore the whole declaration in the common case.\n */\n legal?: VitopsLegalOptions;\n /**\n * The site config, for the parts of generation that depend on site-level facts\n * rather than on the design system — currently `designSystem.defaultColorScheme`, which\n * decides whether the colour layer carries a `prefers-color-scheme` block.\n *\n * Only needed when the site config is a *different* file from `input`; when\n * `input` is itself a site config, that one is used. Watched, so an edit\n * regenerates.\n */\n site?: VitopsSiteOptions;\n /**\n * Emit the `prefers-color-scheme: dark` block. Normally read from the site\n * config's `designSystem.defaultColorScheme: \"system\"`; set here it wins, for consumers with no\n * site config.\n */\n systemColorScheme?: boolean;\n}\n\nexport interface VitopsSiteOptions {\n /** Path to the site config (JSON). */\n input: string;\n /** Environment whose A/B variant applies. Default: 'production'. */\n siteEnv?: string;\n}\n\nexport interface VitopsLegalOptions {\n /**\n * Path to the site config (JSON). Optional: defaults to `site.input`, then to\n * the plugin's own `input` when that is itself a site config.\n */\n input?: string;\n /** Directory to write documents into. Default: 'src/content/legal'. */\n out?: string;\n /** Output format. Default: 'md', which suits an Astro content collection. */\n format?: LegalOutput;\n /** Environment whose A/B variant applies. Default: 'production'. */\n siteEnv?: string;\n}\n\nexport default function vitops(options: VitopsPluginOptions = {}): Plugin {\n const format: Format = options.format ?? 'tailwind';\n const outDir = options.out ?? 'src/styles';\n let input = resolve(options.input ?? 'design-system.json');\n let legalInput = options.legal?.input ? resolve(options.legal.input) : '';\n let siteInput = options.site ? resolve(options.site.input) : '';\n let root = '';\n\n const readJson = (p: string) => JSON.parse(readFileSync(p, 'utf8')) as unknown;\n\n const run = async () => {\n // Read once and hand `generate` the parsed object: it has to be inspected\n // here anyway to know whether `input` is a site config, and reading the same\n // file twice per regeneration is how a half-written save gets picked up.\n const raw = readJson(input);\n const inputIsSite = isSiteConfig(raw);\n // `generate` reads the site config only for what the stylesheet depends on;\n // legal documents stay the separate opt-in below.\n await generate({\n input: raw as Parameters<typeof generate>[0]['input'],\n format,\n outDir,\n ...(options.theme != null ? { theme: options.theme } : {}),\n siteEnv: options.site?.siteEnv ?? 'production',\n ...(options.systemColorScheme != null\n ? { systemColorScheme: options.systemColorScheme }\n : {}),\n // An explicit `site` wins; otherwise `input` supplies it when it is one.\n ...(siteInput ? { site: readJson(siteInput) as never } : {}),\n });\n if (options.legal) {\n // The three paths are the same file in almost every project, so the\n // declaration cascades rather than being repeated.\n const legalPath = legalInput || siteInput || (inputIsSite ? input : '');\n if (!legalPath)\n throw new Error(\n '[vitops] legal: needs a site config — set `legal.input`, or the `site` option, or ' +\n 'point `input` at a site config.',\n );\n const legalOut = resolve(root, options.legal.out ?? 'src/content/legal');\n const site = resolveSiteConfig(readJson(legalPath), options.legal.siteEnv ?? 'production');\n const files = generateLegal(site, { output: options.legal.format ?? 'md' });\n mkdirSync(legalOut, { recursive: true });\n for (const [name, content] of Object.entries(files))\n writeFileSync(join(legalOut, name), content);\n }\n if (options.editorManifestDir) {\n const src = resolve(root, outDir, 'design-manifest.json');\n if (existsSync(src)) {\n const dest = resolve(root, options.editorManifestDir, 'vitops');\n mkdirSync(dest, { recursive: true });\n copyFileSync(src, join(dest, 'design-manifest.json'));\n }\n }\n if (options.spriteDir) {\n const src = resolve(root, outDir, 'icons.svg');\n if (existsSync(src)) {\n const dest = resolve(root, options.spriteDir, 'vitops');\n mkdirSync(dest, { recursive: true });\n copyFileSync(src, join(dest, 'icons.svg'));\n }\n }\n if (options.favicon) {\n const faviconOut = resolve(root, options.favicon.out ?? 'public');\n await generateFavicons({\n source: resolve(root, options.favicon.source),\n outputDir: faviconOut,\n ...(options.favicon.lowResSource\n ? { lowResSource: resolve(root, options.favicon.lowResSource) }\n : {}),\n // Composited under the maskable outputs, so they match the manifest's\n // `background_color` rather than being transparent where it must not be.\n ...(options.favicon.backgroundColor\n ? { backgroundColor: options.favicon.backgroundColor }\n : {}),\n });\n if (options.favicon.name && options.favicon.themeColor) {\n await writeFaviconManifest(faviconOut, {\n name: options.favicon.name,\n themeColor: options.favicon.themeColor,\n ...(options.favicon.backgroundColor\n ? { backgroundColor: options.favicon.backgroundColor }\n : {}),\n });\n }\n }\n if (options.media) {\n const media = options.media;\n const config: MediaConfig = {\n ...(media.maxWidth != null ? { maxWidth: media.maxWidth } : {}),\n ...(media.crf != null ? { crf: media.crf } : {}),\n ...(media.maxBitrate ? { maxBitrate: media.maxBitrate } : {}),\n ...(media.audio != null ? { audio: media.audio } : {}),\n ...(media.posterTime != null ? { posterTime: media.posterTime } : {}),\n ...(media.outputs ? { outputs: media.outputs } : {}),\n };\n const result = await processMedia({\n raw: resolve(root, media.raw ?? 'raw'),\n out: resolve(root, media.out ?? 'src/assets/processed'),\n config,\n // Resolved against the project root even when defaulted. Left to\n // `processMedia` it would resolve against cwd, and in a monorepo run from\n // the workspace root that puts the cache somewhere the next build won't\n // look — so every build re-encodes, silently.\n manifest: resolve(root, media.manifest ?? MEDIA_MANIFEST_PATH),\n // An encode is minutes of silence otherwise, which in a build reads as a hang.\n onProgress: (message) => console.log(`[vitops] media: ${message}`),\n });\n if (result.written.length > 0)\n console.log(\n `[vitops] media: ${result.written.length} written, ${result.skipped} unchanged`,\n );\n }\n };\n\n // Every config `run()` reads is an input, so every one must trigger a re-run —\n // a legal document (or a colour layer) that only refreshes when the design\n // system changes is worse than one that never refreshes, because it looks\n // current. De-duplicated because `site.input` and `legal.input` are usually the\n // same file, and watching it twice would regenerate twice per edit.\n const watched = () => [...new Set([input, siteInput, legalInput].filter(Boolean))];\n\n return {\n name: '@getvitops/vite',\n // Resolve paths against Vite's project root once it's known.\n configResolved(config) {\n root = config.root;\n input = resolve(config.root, options.input ?? 'design-system.json');\n if (options.legal?.input) legalInput = resolve(config.root, options.legal.input);\n if (options.site) siteInput = resolve(config.root, options.site.input);\n },\n async buildStart() {\n for (const f of watched()) this.addWatchFile(f);\n await run();\n },\n async watchChange(id) {\n if (watched().includes(resolve(id))) await run();\n },\n configureServer(server) {\n for (const f of watched()) server.watcher.add(f);\n server.watcher.on('change', async (file) => {\n if (!watched().includes(resolve(file))) return;\n await run();\n server.ws.send({ type: 'full-reload' });\n });\n\n // Dev-only design-system write-back, for `<wc-theme-editor>`'s \"Save to\n // source\". Registered in configureServer only, so it exists in `vite dev`\n // and never in a build — on a static deploy the editor's probe 404s and the\n // button simply isn't rendered. The only writable path is `input`, already\n // resolved from config: a request supplies a patch, never a destination.\n //\n // The editor's patch is always design-system-relative (`colors.palette.…`),\n // because that is the only config it knows about. When `input` is a site\n // config the patch is therefore merged into — and validated against — the\n // design system SUBTREE, and only the surrounding file is written whole.\n // Merging it at the root instead would write a `colors` key beside\n // `organization` that nothing reads, and `validate` would reject the site\n // config wholesale, so every save would fail with errors about the wrong\n // document.\n server.middlewares.use(EDITOR_ENDPOINT, (req, res, next) => {\n const json = (status: number, body: unknown): void => {\n res.statusCode = status;\n res.setHeader('content-type', 'application/json');\n res.end(JSON.stringify(body));\n };\n\n if (req.method === 'GET') {\n try {\n const current = readJson(input);\n const at = designSystemPath(current, options.theme);\n return json(200, {\n writable: true,\n path: input,\n // The subtree, not the file: the editor's own model of a config is\n // a design system, and `at` says where in the file it lives.\n at,\n config: getAt(current, at),\n });\n } catch (err) {\n return json(500, { error: (err as Error).message });\n }\n }\n\n if (req.method !== 'POST') return next();\n\n const chunks: Buffer[] = [];\n req.on('data', (c: Buffer) => chunks.push(c));\n req.on('end', () => {\n try {\n const { patch } = JSON.parse(Buffer.concat(chunks).toString('utf8')) as {\n patch?: unknown;\n };\n if (patch == null || typeof patch !== 'object')\n return json(400, { errors: ['body must be { patch: object }'] });\n\n const current = readJson(input);\n const at = designSystemPath(current, options.theme);\n const merged = deepMerge(getAt(current, at), patch);\n // Validate the *merged* result, not the patch: a patch is a partial\n // by design, so it can't be validated on its own, and writing an\n // invalid config would break the very dev server doing the writing.\n const result = validate(merged);\n if (!result.ok)\n return json(400, {\n errors: result.errors.map((e) => `${e.path.join('.') || '(root)'}: ${e.message}`),\n });\n\n writeFileSync(input, `${formatConfig(setAt(current, at, merged))}\\n`);\n // The watcher above picks the write up and regenerates + reloads.\n return json(200, { ok: true, warnings: result.warnings });\n } catch (err) {\n return json(400, { errors: [(err as Error).message] });\n }\n });\n });\n },\n };\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@getvitops/vite",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Vite plugin that generates Vitops design-system outputs from a design-system.json during a Vite/Astro build.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"files": [
|
|
@@ -18,11 +18,12 @@
|
|
|
18
18
|
"access": "public"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@getvitops/generator": "1.0
|
|
22
|
-
"@getvitops/utils": "1.0
|
|
21
|
+
"@getvitops/generator": "2.1.0",
|
|
22
|
+
"@getvitops/utils": "2.1.0"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"vite": "^6.0.0"
|
|
25
|
+
"vite": "^6.0.0",
|
|
26
|
+
"vitest": "^4.1.9"
|
|
26
27
|
},
|
|
27
28
|
"peerDependencies": {
|
|
28
29
|
"vite": ">=5"
|