@getvitops/vite 8.3.0-alpha.0 → 8.3.0-alpha.1

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 CHANGED
@@ -1,5 +1,38 @@
1
1
  # @getvitops/vite
2
2
 
3
+ ## 8.3.0-alpha.1
4
+
5
+ ### Minor Changes
6
+
7
+ - 24c568b: Escape hatches on every block, a section theme, widget areas, a badges block, Tailwind scanning of package renderers, and a sturdier `vitops audit`.
8
+
9
+ **@getvitops/emdash**
10
+
11
+ - Every `vitops.*` block and `vitops_section` gains three fields: `anchor` (rendered as the `id`, so `#contact` works), `className` (`class_name` on the section type: block-type slugs are lowercase) and `style`. `style` takes CSS custom properties only (`--p-card: 2rem`); any other declaration, and any value containing `url(`, `@`, `{`, `}` or a backslash, is dropped. With the `tailwind` format a class typed in the CMS only works if the site already uses it. They are applied to the block's own outermost element (the grid `<ul>` for `vitops.cards`), never an extra wrapper, so a block's markup is the same whether or not an editor fills them in.
12
+ - `vitops_section` gains a `theme` select: a role, or `<role>:solid`, with readable text chosen by the generator's contrast rules. It overrides `tone`/`color`/`solid`, which stay for existing content. Other site roles still go through `tone`.
13
+ - New `vitops.badges` block (a row of `.badge` or `.tag` labels, each with an optional tone, icon and link) and `vitops.widgetArea` block, plus `<VitopsWidgetArea name level />` from `@getvitops/emdash/astro`. A widget area is content defined once under Widgets and shown wherever it is placed. An area cannot hold another area, and an area containing a `core:*` component widget is rendered by EmDash's own renderer.
14
+ - Existing sites: re-run the block-type seed only on a new database; on an existing one add the `theme`, `anchor`, `className` and `style` fields to the `vitops_section` block type in the admin.
15
+
16
+ **@getvitops/create**: the emdash template seeds `announcement` and `pre-footer` widget areas, renders them in `Layout.astro`, and documents when to use a Section, a block or a widget area. Sites scaffolded earlier add the two `<VitopsWidgetArea>` lines and the `widgetAreas` seed entries by hand.
17
+
18
+ **@getvitops/generator / astro / vite** (`tailwind` format only): the Astro integration now adds `@source` lines for `@getvitops/astro` and `@getvitops/emdash`, because Tailwind v4 does not scan `node_modules`; utilities used only by their renderers (`font-display`, `font-caption`, `font-footnote`) were never emitted and the page silently rendered at body size. New `sources` option on `generate()` and the Vite plugin. A site's `src/package-classes.txt` workaround can be deleted. css and bricks: no change.
19
+
20
+ **@getvitops/cli**: `vitops emdash check-prerender` now also warns when a page uses a type-role class (`font-display`, …) the built CSS has no rule for; `--strict` makes it fail. `vitops audit` checks for its optional packages and a Chromium binary before starting the preview server (so a failed run no longer leaves port 4398 busy), stops the server on Ctrl+C and termination, and takes its page list from the built sitemap, then the menus; the guessed defaults (`/privacy`, `/contact`, …) are used only when neither exists.
21
+
22
+ **@getvitops/utils**: the audit helpers behind the above (`preflight`, `fetchSitemapPaths`, `sitemapLocs`, `pathsFromSitemapLocs`).
23
+
24
+ **@getvitops/core** (all formats): new `.block-anchor` rule gives an anchored block `scroll-margin-top: var(--header-offset, 5rem)`; set `--header-offset` to a sticky header's height. New `widget-area.css` partial. No other consumer-facing change.
25
+
26
+ The other fixed-group packages: no consumer-facing change beyond the above.
27
+
28
+ **Also fixed in the emdash template** (`site.json`): it defined only the `display`, `heading` and `body` type roles while the blocks use `title`, `eyebrow`, `lead`, `caption` and `footnote`, so those rendered at body size with no rule behind them. The roles are added with the reference design system's specs. A site scaffolded earlier adds the same five entries under `designSystem.themes.default.typography.roles`; `vitops emdash check-prerender` now names any that are missing.
29
+
30
+ ### Patch Changes
31
+
32
+ - Updated dependencies [24c568b]
33
+ - @getvitops/generator@8.3.0-alpha.1
34
+ - @getvitops/utils@8.3.0-alpha.1
35
+
3
36
  ## 8.3.0-alpha.0
4
37
 
5
38
  ### Minor Changes
package/dist/index.d.mts CHANGED
@@ -57,6 +57,8 @@ export interface VitopsPluginOptions {
57
57
  * Also the theme `<wc-theme-editor>`'s **Save to source** writes back into.
58
58
  */
59
59
  theme?: string;
60
+ /** `tailwind` format only: `@source` paths for packages whose renderers use classes (see `GenerateOptions.sources`). */
61
+ sources?: string[];
60
62
  /** Output target. Default: 'tailwind' (the EmDash/Astro case). */
61
63
  format?: Format;
62
64
  /** Directory to write generated output into. Default: 'src/styles'. */
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;iBAsDiB;;EAEf;;;;;;;EAOA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA,UAAU;;EAEV;;iBAGe;;EAEf;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;iBAGe;;;;;;;;;EASf;;;;;;;EAOA;;EAEA,SAAS;;EAET;;EAEA,UAAU;;;;;;;;;;;;;;;;;EAiBV,QAAQ;;;;;;;;EAQR;;;;;;;;EAQA;;;;;;;;;EASA,QAAQ;;;;;;;;;;EAUR,OAAO;;;;;;EAMP;;iBAGe;;EAEf;;EAEA;;iBAGe;;;;;EAKf;;EAEA;;EAEA,SAAS;;EAET;;wBAGsB,OAAO,UAAS,sBAA2B"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;iBAsDiB;;EAEf;;;;;;;EAOA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA,UAAU;;EAEV;;iBAGe;;EAEf;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;iBAGe;;;;;;;;;EASf;;;;;;;EAOA;;EAEA;;EAEA,SAAS;;EAET;;EAEA,UAAU;;;;;;;;;;;;;;;;;EAiBV,QAAQ;;;;;;;;EAQR;;;;;;;;EAQA;;;;;;;;;EASA,QAAQ;;;;;;;;;;EAUR,OAAO;;;;;;EAMP;;iBAGe;;EAEf;;EAEA;;iBAGe;;;;;EAKf;;EAEA;;EAEA,SAAS;;EAET;;wBAGsB,OAAO,UAAS,sBAA2B"}
package/dist/index.mjs CHANGED
@@ -34,6 +34,7 @@ function vitops(options = {}) {
34
34
  outDir,
35
35
  ...options.theme != null ? { theme: options.theme } : {},
36
36
  siteEnv: options.site?.siteEnv ?? "production",
37
+ ...options.sources?.length ? { sources: options.sources } : {},
37
38
  ...options.systemColorScheme != null ? { systemColorScheme: options.systemColorScheme } : {},
38
39
  ...siteInput ? { site: readJson(siteInput) } : {}
39
40
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["designSystemPath","getAt","setAt"],"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 {\n copyFileSync,\n existsSync,\n mkdirSync,\n mkdtempSync,\n readFileSync,\n rmSync,\n writeFileSync,\n} from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport { join, resolve } from 'node:path';\nimport type { Plugin } from 'vite';\nimport {\n designSystemPath,\n formatConfig,\n generate,\n generateLegal,\n getAt,\n isConfig,\n mergePatch,\n resolveConfig,\n setAt,\n validate,\n type Format,\n type LegalOutput,\n} from '@getvitops/generator';\nimport { generateFavicons, writeFaviconManifest } from '@getvitops/utils';\n// Re-exported for existing consumers/tests importing these from this package\n// directly — the implementations now live in `@getvitops/generator` (shared\n// with the EmDash design-system-editor plugin route; see that package's\n// AGENTS.md section), this module just re-uses them.\nexport { designSystemPath, getAt, setAt } from '@getvitops/generator';\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\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 — plus a standalone\n * `vitops/editor.css` for hosts that render the editor outside the site's own pages.\n * Set by the Astro integration's `editor` / `editorManifest` options. 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 = isConfig(raw);\n // `generate` reads the site config only for what the stylesheet depends on;\n // legal documents stay the separate opt-in below.\n const genArgs = {\n input: raw as Parameters<typeof generate>[0]['input'],\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 await generate({ ...genArgs, format });\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 = resolveConfig(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 dest = resolve(root, options.editorManifestDir, 'vitops');\n mkdirSync(dest, { recursive: true });\n const manifest = resolve(root, outDir, 'design-manifest.json');\n if (existsSync(manifest)) copyFileSync(manifest, join(dest, 'design-manifest.json'));\n // The editor renders in light DOM and borrows the framework's own CSS, which a host\n // outside the site's pages (the EmDash admin, in an iframe) has to load itself. That\n // needs a standalone stylesheet; only the `css` format emits one, so a tailwind site\n // gets a throwaway `css` pass whose output never reaches `outDir`.\n if (format === 'css') {\n copyFileSync(resolve(root, outDir, 'styles.css'), join(dest, 'editor.css'));\n } else {\n const scratch = mkdtempSync(join(tmpdir(), 'vitops-editor-css-'));\n try {\n await generate({ ...genArgs, format: 'css', outDir: scratch });\n copyFileSync(join(scratch, 'styles.css'), join(dest, 'editor.css'));\n } finally {\n rmSync(scratch, { recursive: true, force: true });\n }\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 = mergePatch(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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"}
1
+ {"version":3,"file":"index.mjs","names":["designSystemPath","getAt","setAt"],"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 {\n copyFileSync,\n existsSync,\n mkdirSync,\n mkdtempSync,\n readFileSync,\n rmSync,\n writeFileSync,\n} from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport { join, resolve } from 'node:path';\nimport type { Plugin } from 'vite';\nimport {\n designSystemPath,\n formatConfig,\n generate,\n generateLegal,\n getAt,\n isConfig,\n mergePatch,\n resolveConfig,\n setAt,\n validate,\n type Format,\n type LegalOutput,\n} from '@getvitops/generator';\nimport { generateFavicons, writeFaviconManifest } from '@getvitops/utils';\n// Re-exported for existing consumers/tests importing these from this package\n// directly — the implementations now live in `@getvitops/generator` (shared\n// with the EmDash design-system-editor plugin route; see that package's\n// AGENTS.md section), this module just re-uses them.\nexport { designSystemPath, getAt, setAt } from '@getvitops/generator';\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\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 /** `tailwind` format only: `@source` paths for packages whose renderers use classes (see `GenerateOptions.sources`). */\n sources?: 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 — plus a standalone\n * `vitops/editor.css` for hosts that render the editor outside the site's own pages.\n * Set by the Astro integration's `editor` / `editorManifest` options. 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 = isConfig(raw);\n // `generate` reads the site config only for what the stylesheet depends on;\n // legal documents stay the separate opt-in below.\n const genArgs = {\n input: raw as Parameters<typeof generate>[0]['input'],\n outDir,\n ...(options.theme != null ? { theme: options.theme } : {}),\n siteEnv: options.site?.siteEnv ?? 'production',\n ...(options.sources?.length ? { sources: options.sources } : {}),\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 await generate({ ...genArgs, format });\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 = resolveConfig(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 dest = resolve(root, options.editorManifestDir, 'vitops');\n mkdirSync(dest, { recursive: true });\n const manifest = resolve(root, outDir, 'design-manifest.json');\n if (existsSync(manifest)) copyFileSync(manifest, join(dest, 'design-manifest.json'));\n // The editor renders in light DOM and borrows the framework's own CSS, which a host\n // outside the site's pages (the EmDash admin, in an iframe) has to load itself. That\n // needs a standalone stylesheet; only the `css` format emits one, so a tailwind site\n // gets a throwaway `css` pass whose output never reaches `outDir`.\n if (format === 'css') {\n copyFileSync(resolve(root, outDir, 'styles.css'), join(dest, 'editor.css'));\n } else {\n const scratch = mkdtempSync(join(tmpdir(), 'vitops-editor-css-'));\n try {\n await generate({ ...genArgs, format: 'css', outDir: scratch });\n copyFileSync(join(scratch, 'styles.css'), join(dest, 'editor.css'));\n } finally {\n rmSync(scratch, { recursive: true, force: true });\n }\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 = mergePatch(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": "8.3.0-alpha.0",
3
+ "version": "8.3.0-alpha.1",
4
4
  "description": "Vite plugin that generates Vitops design-system outputs from a design-system.json during a Vite/Astro build.",
5
5
  "homepage": "https://docs.vitops.ca",
6
6
  "bugs": "https://github.com/getvitops/design-system/issues",
@@ -26,8 +26,8 @@
26
26
  "access": "public"
27
27
  },
28
28
  "dependencies": {
29
- "@getvitops/generator": "8.3.0-alpha.0",
30
- "@getvitops/utils": "8.3.0-alpha.0"
29
+ "@getvitops/generator": "8.3.0-alpha.1",
30
+ "@getvitops/utils": "8.3.0-alpha.1"
31
31
  },
32
32
  "devDependencies": {
33
33
  "vite": "npm:@voidzero-dev/vite-plus-core@1.4.0"