From f1f2129739d1fd817eab0f6354a49eff5a91cade Mon Sep 17 00:00:00 2001 From: Darius Cepulis Date: Thu, 25 Jun 2026 11:20:45 -0700 Subject: [PATCH] feat: add a CDN media-availability manifest and read it across install surfaces (#1737) Co-authored-by: Claude --- .gitignore | 1 + packages/cli/src/commands/docs.ts | 16 +++- packages/cli/src/site-modules.d.ts | 12 +++ packages/cli/src/utils/prompts.ts | 19 +++- .../src/utils/tests/fixtures/cdn-media.json | 9 ++ packages/cli/src/utils/tests/prompts.test.ts | 19 ++++ packages/cli/tsdown.config.ts | 1 + packages/cli/vitest.config.ts | 5 ++ site/package.json | 3 +- site/scripts/build-cdn-manifest.ts | 65 ++++++++++++++ .../installation/HTMLInstallTabs.astro | 10 +++ .../installation/HTMLInstallTabs.tsx | 64 -------------- .../installation/HTMLInstallTabsClient.tsx | 86 +++++++++++++++++++ site/src/components/typography/P.astro | 3 +- site/src/components/typography/styles.ts | 2 + site/src/content.config.ts | 11 +++ site/src/content/docs/how-to/installation.mdx | 4 +- .../installation/__tests__/cdn-code.test.ts | 20 ++++- site/src/utils/installation/cdn-code.ts | 19 +++- site/turbo.json | 8 +- 20 files changed, 300 insertions(+), 77 deletions(-) create mode 100644 packages/cli/src/utils/tests/fixtures/cdn-media.json create mode 100644 packages/cli/src/utils/tests/prompts.test.ts create mode 100644 site/scripts/build-cdn-manifest.ts create mode 100644 site/src/components/installation/HTMLInstallTabs.astro delete mode 100644 site/src/components/installation/HTMLInstallTabs.tsx create mode 100644 site/src/components/installation/HTMLInstallTabsClient.tsx diff --git a/.gitignore b/.gitignore index 092d6f87..e05b3822 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,7 @@ site/src/content/generated-feature-reference/ site/src/content/generated-media-reference/ site/src/content/generated-preset-reference/ site/src/content/ejected-skins.json +site/src/content/cdn-media.json packages/cli/docs/ packages/html/docs/ packages/react/docs/ diff --git a/packages/cli/src/commands/docs.ts b/packages/cli/src/commands/docs.ts index 2eedc04a..79d9a077 100644 --- a/packages/cli/src/commands/docs.ts +++ b/packages/cli/src/commands/docs.ts @@ -5,7 +5,13 @@ import type { Framework } from '../utils/config.js'; import { getConfigValue } from '../utils/config.js'; import { docExistsInAnyFramework, readBundledDoc, readLlmsTxt } from '../utils/docs.js'; import { formatInstallationCode } from '../utils/format.js'; -import { mapRawSkin, type PartialInstallFlags, promptFramework, promptInstallOptions } from '../utils/prompts.js'; +import { + mapRawSkin, + type PartialInstallFlags, + promptFramework, + promptInstallOptions, + supportsCdnInstall, +} from '../utils/prompts.js'; import { replaceMarker, stripOmitMarkers } from '../utils/replace.js'; interface ParsedFlags { @@ -177,6 +183,14 @@ export async function handleDocs(flags: ParsedFlags, positionals: string[]): Pro process.exit(1); } + // The interactive prompt hides CDN for renderers without a CDN build; guard + // the non-interactive flag path so a `--install-method cdn` request for one + // can't emit a broken snippet. + if (opts.installMethod === 'cdn' && !supportsCdnInstall(opts.renderer)) { + console.error('Error: this source type has no CDN build. Install it with npm, pnpm, yarn, or bun.'); + process.exit(1); + } + const generated = formatInstallationCode(opts); const output = stripOmitMarkers(replaceMarker(markdown, 'installation', generated)); printVersionHeader(); diff --git a/packages/cli/src/site-modules.d.ts b/packages/cli/src/site-modules.d.ts index 0f644faf..592148f5 100644 --- a/packages/cli/src/site-modules.d.ts +++ b/packages/cli/src/site-modules.d.ts @@ -59,3 +59,15 @@ declare module '@/utils/installation/detect-renderer' { export function detectRenderer(url: string, useCase: UseCase): DetectionResult | null; } + +declare module '@/utils/installation/cdn-code' { + import type { Renderer, Skin, UseCase } from '@/utils/installation/types'; + + export function generateCdnCode(useCase: UseCase, skin: Skin, renderer: Renderer): string; + export function rendererSupportsCdn(renderer: Renderer, cdnMediaSubpaths: readonly string[]): boolean; +} + +declare module '@/content/cdn-media.json' { + const entries: Array<{ id: string }>; + export default entries; +} diff --git a/packages/cli/src/utils/prompts.ts b/packages/cli/src/utils/prompts.ts index ca9d1b59..c0d49de8 100644 --- a/packages/cli/src/utils/prompts.ts +++ b/packages/cli/src/utils/prompts.ts @@ -1,10 +1,18 @@ import * as p from '@clack/prompts'; +import cdnMedia from '@/content/cdn-media.json'; +import { rendererSupportsCdn } from '@/utils/installation/cdn-code'; import type { InstallationOptions } from '@/utils/installation/codegen'; import { detectRenderer } from '@/utils/installation/detect-renderer'; import type { InstallMethod, Renderer, Skin, UseCase } from '@/utils/installation/types'; import { VALID_RENDERERS } from '@/utils/installation/types'; import type { Framework } from './config.js'; +const CDN_MEDIA_SUBPATHS = cdnMedia.map((entry) => entry.id); + +export function supportsCdnInstall(renderer: Renderer): boolean { + return rendererSupportsCdn(renderer, CDN_MEDIA_SUBPATHS); +} + export async function promptFramework(): Promise { const value = await p.select({ message: 'Which framework?', @@ -50,14 +58,19 @@ function skinOptionsForUseCase(useCase: UseCase): Array<{ value: Skin; label: st ]; } -function installMethodOptions(framework: Framework): Array<{ value: InstallMethod; label: string }> { +function installMethodOptions( + framework: Framework, + renderer: Renderer +): Array<{ value: InstallMethod; label: string }> { const options: Array<{ value: InstallMethod; label: string }> = [ { value: 'npm', label: 'npm' }, { value: 'pnpm', label: 'pnpm' }, { value: 'yarn', label: 'yarn' }, { value: 'bun', label: 'bun' }, ]; - if (framework === 'html') { + // CDN is HTML-only, and only when the renderer ships a CDN build — matching + // the install page, which hides the CDN tab for renderers without one. + if (framework === 'html' && supportsCdnInstall(renderer)) { options.unshift({ value: 'cdn', label: 'CDN' }); } return options; @@ -154,7 +167,7 @@ export async function promptInstallOptions( (await (async () => { const value = await p.select({ message: 'Install method', - options: installMethodOptions(framework), + options: installMethodOptions(framework, media), }); if (p.isCancel(value)) process.exit(0); return value; diff --git a/packages/cli/src/utils/tests/fixtures/cdn-media.json b/packages/cli/src/utils/tests/fixtures/cdn-media.json new file mode 100644 index 00000000..7d154757 --- /dev/null +++ b/packages/cli/src/utils/tests/fixtures/cdn-media.json @@ -0,0 +1,9 @@ +[ + { "id": "dash-video" }, + { "id": "hls-video" }, + { "id": "mux-audio" }, + { "id": "mux-video" }, + { "id": "native-hls-video" }, + { "id": "simple-hls-audio-only" }, + { "id": "simple-hls-video" } +] diff --git a/packages/cli/src/utils/tests/prompts.test.ts b/packages/cli/src/utils/tests/prompts.test.ts new file mode 100644 index 00000000..ff23486d --- /dev/null +++ b/packages/cli/src/utils/tests/prompts.test.ts @@ -0,0 +1,19 @@ +import { describe, expect, it } from 'vitest'; +import { supportsCdnInstall } from '../prompts.js'; + +// Wires the cdn-media manifest into the CLI the same way the install page reads +// the cdnMedia collection. Every current renderer ships (or is covered by) a +// CDN build, so all resolve true; the no-CDN path (e.g. Vimeo) arrives with the +// new rendering engines. `rendererSupportsCdn`'s false branch is unit-tested in +// cdn-code.test.ts. +describe('supportsCdnInstall', () => { + it('returns true for preset renderers (covered by the preset bundle)', () => { + expect(supportsCdnInstall('html5-video')).toBe(true); + expect(supportsCdnInstall('html5-audio')).toBe(true); + expect(supportsCdnInstall('background-video')).toBe(true); + }); + + it('returns true for hls, whose media bundle ships a CDN build', () => { + expect(supportsCdnInstall('hls')).toBe(true); + }); +}); diff --git a/packages/cli/tsdown.config.ts b/packages/cli/tsdown.config.ts index 523c6752..5a9a3a68 100644 --- a/packages/cli/tsdown.config.ts +++ b/packages/cli/tsdown.config.ts @@ -23,6 +23,7 @@ export default defineConfig({ '@/utils/installation/types': resolve(__dirname, '../../site/src/utils/installation/types.ts'), '@/utils/installation/cdn-code': resolve(__dirname, '../../site/src/utils/installation/cdn-code.ts'), '@/utils/installation/detect-renderer': resolve(__dirname, '../../site/src/utils/installation/detect-renderer.ts'), + '@/content/cdn-media.json': resolve(__dirname, '../../site/src/content/cdn-media.json'), '@/consts': resolve(__dirname, '../../site/src/consts.ts'), }, }); diff --git a/packages/cli/vitest.config.ts b/packages/cli/vitest.config.ts index 2d74f45c..c5286b1e 100644 --- a/packages/cli/vitest.config.ts +++ b/packages/cli/vitest.config.ts @@ -17,6 +17,11 @@ export default defineConfig({ __dirname, '../../site/src/utils/installation/detect-renderer.ts' ), + // The real manifest is generated at build time (gitignored) and bundled + // by tsdown. CLI tests are intentionally hermetic (`test` has no turbo + // build dependency), so they resolve a committed fixture that mirrors the + // manifest's shape and contents instead of forcing a CDN build. + '@/content/cdn-media.json': resolve(__dirname, 'src/utils/tests/fixtures/cdn-media.json'), '@/consts': resolve(__dirname, '../../site/src/consts.ts'), }, }, diff --git a/site/package.json b/site/package.json index ffceef4d..a1cb6d96 100644 --- a/site/package.json +++ b/site/package.json @@ -5,9 +5,10 @@ "scripts": { "api-docs": "tsx scripts/api-docs-builder/src/index.ts", "ejected-skins": "tsx scripts/build-ejected-skins.ts", + "cdn-manifest": "tsx scripts/build-cdn-manifest.ts", "dev": "NETLIFY_DEV=1 astro dev", "build": "astro build", - "clean": "rm -rf dist .netlify src/content/generated-component-reference src/content/generated-util-reference src/content/ejected-skins.json", + "clean": "rm -rf dist .netlify src/content/generated-component-reference src/content/generated-util-reference src/content/ejected-skins.json src/content/cdn-media.json", "astro": "astro", "test": "vitest run", "test:watch": "vitest", diff --git a/site/scripts/build-cdn-manifest.ts b/site/scripts/build-cdn-manifest.ts new file mode 100644 index 00000000..d04bea98 --- /dev/null +++ b/site/scripts/build-cdn-manifest.ts @@ -0,0 +1,65 @@ +/** + * Build the CDN media manifest for the installation guide. + * + * Scans the built `@videojs/html` CDN media bundles and records which media + * subpaths actually ship a CDN build. The installation page uses this to hide + * the CDN install option for renderers that have no CDN bundle (e.g. Vimeo). + * + * Produces `site/src/content/cdn-media.json` as an array of `{ id }` entries + * (one per media subpath), consumed via the `cdnMedia` content collection. + * + * Source of truth: the built output of `@videojs/html`'s `build:cdn` task + * (configured in `packages/html/tsdown.cdn.config.ts`). Reading the build + * output — rather than a hand-maintained list — means a renderer that fails to + * ship a CDN bundle correctly shows as no-CDN. + * + * Prerequisites: `@videojs/html`'s `build:cdn` (wired as a turbo dependency). + */ + +import { existsSync, mkdirSync, readdirSync, writeFileSync } from 'node:fs'; +import { dirname, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { z } from 'astro/zod'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const ROOT = resolve(__dirname, '../..'); +const CDN_MEDIA_DIR = resolve(ROOT, 'packages/html/cdn/media'); +const OUTPUT = resolve(ROOT, 'site/src/content/cdn-media.json'); + +const PREFIX = '\x1b[35m[cdn-manifest]\x1b[0m'; +const log = { + info: (...args: unknown[]) => console.log(PREFIX, ...args), + warn: (...args: unknown[]) => console.warn(PREFIX, '\x1b[33mwarn:\x1b[0m', ...args), + error: (...args: unknown[]) => console.error(PREFIX, '\x1b[31merror:\x1b[0m', ...args), +}; + +const ManifestSchema = z.array(z.object({ id: z.string() })); + +function main() { + if (!existsSync(CDN_MEDIA_DIR)) { + log.error(`CDN media build not found at ${CDN_MEDIA_DIR}.`); + log.error("Run @videojs/html's build:cdn first (it's wired as a turbo dependency)."); + process.exit(1); + } + + // Production bundles are `.js`; skip dev (`.dev.js`), sourcemaps, + // and type stubs. + const subpaths = readdirSync(CDN_MEDIA_DIR) + .filter((file) => file.endsWith('.js') && !file.endsWith('.dev.js')) + .map((file) => file.replace(/\.js$/, '')) + .sort(); + + if (subpaths.length === 0) { + log.error(`No CDN media bundles found in ${CDN_MEDIA_DIR}.`); + process.exit(1); + } + + const entries = ManifestSchema.parse(subpaths.map((id) => ({ id }))); + + mkdirSync(dirname(OUTPUT), { recursive: true }); + writeFileSync(OUTPUT, `${JSON.stringify(entries, null, 2)}\n`); + + log.info(`✅ Wrote ${entries.length} CDN media entries to ${OUTPUT}`); +} + +main(); diff --git a/site/src/components/installation/HTMLInstallTabs.astro b/site/src/components/installation/HTMLInstallTabs.astro new file mode 100644 index 00000000..7d9ccd57 --- /dev/null +++ b/site/src/components/installation/HTMLInstallTabs.astro @@ -0,0 +1,10 @@ +--- +import { getCollection } from 'astro:content'; +import HTMLInstallTabsClient from './HTMLInstallTabsClient'; + +// Read the generated CDN media manifest server-side and hand the list to the +// client island, which decides per-renderer whether to offer the CDN option. +const cdnMedia = (await getCollection('cdnMedia')).map((entry) => entry.id); +--- + + diff --git a/site/src/components/installation/HTMLInstallTabs.tsx b/site/src/components/installation/HTMLInstallTabs.tsx deleted file mode 100644 index 56ea1615..00000000 --- a/site/src/components/installation/HTMLInstallTabs.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import { useEffect, useRef } from 'react'; -import ClientCode from '@/components/Code/ClientCode'; -import { Tab, TabsList, TabsPanel, TabsRoot } from '@/components/Tabs'; -import { installMethod } from '@/stores/installation'; -import type { InstallMethod } from '@/utils/installation/types'; -import HTMLCdnCodeBlock from './HTMLCdnCodeBlock'; - -export default function HTMLInstallTabs() { - const ref = useRef(null); - - // Observe tab changes and sync to installMethod store - useEffect(() => { - const root = ref.current?.querySelector('[data-tabs-root]'); - if (!root) return; - - const observer = new MutationObserver(() => { - const activeTab = root.querySelector('[role="tab"][data-tab-active="true"]'); - if (activeTab) { - const value = activeTab.getAttribute('data-value'); - if (value) { - installMethod.set(value as InstallMethod); - } - } - }); - - const tabs = root.querySelectorAll('[role="tab"]'); - tabs.forEach((tab) => { - observer.observe(tab, { attributes: true, attributeFilter: ['data-tab-active'] }); - }); - - return () => observer.disconnect(); - }, []); - - return ( -
- - - - cdn - - npm - pnpm - yarn - bun - - - - - - - - - - - - - - - - - -
- ); -} diff --git a/site/src/components/installation/HTMLInstallTabsClient.tsx b/site/src/components/installation/HTMLInstallTabsClient.tsx new file mode 100644 index 00000000..b58e3e7d --- /dev/null +++ b/site/src/components/installation/HTMLInstallTabsClient.tsx @@ -0,0 +1,86 @@ +import { useStore } from '@nanostores/react'; +import clsx from 'clsx'; +import { useEffect, useRef } from 'react'; +import ClientCode from '@/components/Code/ClientCode'; +import { Tab, TabsList, TabsPanel, TabsRoot } from '@/components/Tabs'; +import { shared } from '@/components/typography/styles'; +import { installMethod, renderer } from '@/stores/installation'; +import { rendererSupportsCdn } from '@/utils/installation/cdn-code'; +import type { InstallMethod } from '@/utils/installation/types'; +import HTMLCdnCodeBlock from './HTMLCdnCodeBlock'; + +interface HTMLInstallTabsProps { + /** Media subpaths that ship a CDN build, from the cdn-media manifest. */ + cdnMedia: string[]; +} + +export default function HTMLInstallTabs({ cdnMedia }: HTMLInstallTabsProps) { + const ref = useRef(null); + const $renderer = useStore(renderer); + + const supportsCdn = rendererSupportsCdn($renderer, cdnMedia); + + // Mirror the active install-method tab into the store so the usage code block + // can react (e.g. CDN omits the JS imports). Observing from the stable + // wrapper rather than the tabs root means the observer survives the keyed + // remount below, so it never needs to re-attach. + useEffect(() => { + const el = ref.current; + if (!el) return; + + const observer = new MutationObserver(() => { + const value = el.querySelector('[role="tab"][data-tab-active="true"]')?.getAttribute('data-value'); + if (value) installMethod.set(value as InstallMethod); + }); + + observer.observe(el, { subtree: true, attributes: true, attributeFilter: ['data-tab-active'] }); + + return () => observer.disconnect(); + }, []); + + return ( +
+ {/* Remount the tab set when CDN availability changes so the active tab + resets cleanly to its initial. Without this, flipping the npm tab's + `initial` while the CDN tab mounts/unmounts can leave two tabs active + at once and desync installMethod from the visible tab. */} + + + {supportsCdn && ( + + cdn + + )} + + npm + + pnpm + yarn + bun + + {supportsCdn && ( + + + + )} + + + + + + + + + + + + + + {!supportsCdn && ( +

+ This source type isn't available via CDN — install it with a package manager (npm, pnpm, yarn, or bun). +

+ )} +
+ ); +} diff --git a/site/src/components/typography/P.astro b/site/src/components/typography/P.astro index 4485bbdc..844d92ac 100644 --- a/site/src/components/typography/P.astro +++ b/site/src/components/typography/P.astro @@ -3,6 +3,7 @@ import type { HTMLTag, Polymorphic } from 'astro/types'; import { clsx } from 'clsx'; import { twMerge } from '@/utils/twMerge'; +import { shared } from './styles'; type Props = Polymorphic<{ as: Tag }> & { maxWidth?: boolean; @@ -13,7 +14,7 @@ const { as: Tag = 'p', maxWidth = true, class: className, ...props } = Astro.pro --- diff --git a/site/src/components/typography/styles.ts b/site/src/components/typography/styles.ts index 7695490d..0a092291 100644 --- a/site/src/components/typography/styles.ts +++ b/site/src/components/typography/styles.ts @@ -13,6 +13,8 @@ export const shared = { em: 'italic', li: '', ol: 'list-decimal list-outside pl-4 space-y-1', + p: 'my-4', + prose: 'mx-auto max-w-3xl', strong: 'font-bold', ul: 'list-disc list-outside pl-4 space-y-1', } as const; diff --git a/site/src/content.config.ts b/site/src/content.config.ts index 4a49b2ca..de9f754f 100644 --- a/site/src/content.config.ts +++ b/site/src/content.config.ts @@ -170,6 +170,16 @@ const ejectedSkins = defineCollection({ }), }); +// Media subpaths that ship a CDN build, generated by scripts/build-cdn-manifest.ts. +// Each entry's id is a media subpath (e.g. `hls-video`). Used by the installation +// guide to hide the CDN install option for renderers with no CDN bundle. +const cdnMedia = defineCollection({ + loader: file('./src/content/cdn-media.json'), + schema: z.object({ + id: z.string(), + }), +}); + export const collections = { blog, docs, @@ -180,4 +190,5 @@ export const collections = { mediaReference, presetReference, ejectedSkins, + cdnMedia, }; diff --git a/site/src/content/docs/how-to/installation.mdx b/site/src/content/docs/how-to/installation.mdx index 93763cef..299a96c2 100644 --- a/site/src/content/docs/how-to/installation.mdx +++ b/site/src/content/docs/how-to/installation.mdx @@ -7,7 +7,7 @@ import JSPicker from '@/components/installation/JSPicker.astro'; import RendererPicker from '@/components/installation/RendererPicker'; import SkinPickerSection from '@/components/installation/SkinPickerSection.astro'; import UseCasePicker from '@/components/installation/UseCasePicker'; -import HTMLInstallTabs from '@/components/installation/HTMLInstallTabs'; +import HTMLInstallTabs from '@/components/installation/HTMLInstallTabs.astro'; import HTMLUsageCodeBlock from '@/components/installation/HTMLUsageCodeBlock'; import ReactCreateCodeBlock from '@/components/installation/ReactCreateCodeBlock'; import ReactUsageCodeBlock from '@/components/installation/ReactUsageCodeBlock'; @@ -112,7 +112,7 @@ Video.js supports a wide range of file types and hosting services. It's easy to ## Install Video.js - + diff --git a/site/src/utils/installation/__tests__/cdn-code.test.ts b/site/src/utils/installation/__tests__/cdn-code.test.ts index edf2cf35..e35aee4f 100644 --- a/site/src/utils/installation/__tests__/cdn-code.test.ts +++ b/site/src/utils/installation/__tests__/cdn-code.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import { generateCdnCode } from '../cdn-code'; +import { generateCdnCode, rendererSupportsCdn } from '../cdn-code'; describe('generateCdnCode', () => { it('generates video preset CDN tags for html5-video', () => { @@ -33,3 +33,21 @@ describe('generateCdnCode', () => { ); }); }); + +describe('rendererSupportsCdn', () => { + const manifest = ['hls-video']; + + it('returns true for preset renderers (covered by the preset bundle, no media subpath)', () => { + expect(rendererSupportsCdn('html5-video', manifest)).toBe(true); + expect(rendererSupportsCdn('html5-audio', manifest)).toBe(true); + expect(rendererSupportsCdn('background-video', manifest)).toBe(true); + }); + + it('returns true for a media renderer whose subpath is in the manifest', () => { + expect(rendererSupportsCdn('hls', manifest)).toBe(true); + }); + + it('returns false for a media renderer whose subpath is absent from the manifest', () => { + expect(rendererSupportsCdn('hls', [])).toBe(false); + }); +}); diff --git a/site/src/utils/installation/cdn-code.ts b/site/src/utils/installation/cdn-code.ts index 05ec4bdf..f236f7e2 100644 --- a/site/src/utils/installation/cdn-code.ts +++ b/site/src/utils/installation/cdn-code.ts @@ -10,14 +10,29 @@ function getCdnFileName(useCase: UseCase, skin: Skin): string { return skin; } -function getCdnMediaSubpath(renderer: Renderer): string | null { +// Renderer → media subpath name, independent of whether a CDN build exists. +// Preset renderers (html5-video/audio, background-video) are covered by the +// preset bundle and have no separate media script, so they map to null. +function getMediaSubpath(renderer: Renderer): string | null { const map: Partial> = { hls: 'hls-video', }; - return map[renderer] ?? null; } +// Whether a renderer can be installed via CDN, given the set of media subpaths +// that ship a CDN build (from the cdn-media manifest). Preset renderers always +// can (no separate media script); a media renderer can only if its subpath is +// in the manifest. +export function rendererSupportsCdn(renderer: Renderer, cdnMediaSubpaths: readonly string[]): boolean { + const subpath = getMediaSubpath(renderer); + return subpath === null || cdnMediaSubpaths.includes(subpath); +} + +function getCdnMediaSubpath(renderer: Renderer): string | null { + return getMediaSubpath(renderer); +} + export function generateCdnCode(useCase: UseCase, skin: Skin, renderer: Renderer): string { const name = getCdnFileName(useCase, skin); const mediaSubpath = getCdnMediaSubpath(renderer); diff --git a/site/turbo.json b/site/turbo.json index 64e9c1cd..a5f42188 100644 --- a/site/turbo.json +++ b/site/turbo.json @@ -10,8 +10,12 @@ "dependsOn": ["^build"], "outputs": ["src/content/ejected-skins.json"] }, + "cdn-manifest": { + "dependsOn": ["^build:cdn"], + "outputs": ["src/content/cdn-media.json"] + }, "build": { - "dependsOn": ["$TURBO_EXTENDS$", "api-docs", "ejected-skins"], + "dependsOn": ["$TURBO_EXTENDS$", "api-docs", "ejected-skins", "cdn-manifest"], "env": [ "OAUTH_CLIENT_ID", "OAUTH_CLIENT_SECRET", @@ -23,7 +27,7 @@ ] }, "dev": { - "dependsOn": ["$TURBO_EXTENDS$", "api-docs", "ejected-skins"], + "dependsOn": ["$TURBO_EXTENDS$", "api-docs", "ejected-skins", "cdn-manifest"], "env": [ "OAUTH_CLIENT_ID", "OAUTH_CLIENT_SECRET",