egaki 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +188 -0
- package/dist/cli/cache-utils.d.ts +33 -0
- package/dist/cli/cache-utils.d.ts.map +1 -0
- package/dist/cli/cache-utils.js +157 -0
- package/dist/cli/cached-generate.d.ts +105 -0
- package/dist/cli/cached-generate.d.ts.map +1 -0
- package/dist/cli/cached-generate.js +208 -0
- package/dist/cli/cached-generate.test.d.ts +11 -0
- package/dist/cli/cached-generate.test.d.ts.map +1 -0
- package/dist/cli/cached-generate.test.js +431 -0
- package/dist/cli/cartesia-provider.d.ts +16 -0
- package/dist/cli/cartesia-provider.d.ts.map +1 -0
- package/dist/cli/cartesia-provider.js +184 -0
- package/dist/cli/chatgpt-auth.d.ts.map +1 -0
- package/dist/cli/cli.d.ts +4 -0
- package/dist/cli/cli.d.ts.map +1 -0
- package/dist/cli/cli.js +1329 -0
- package/dist/cli/clone-generate.d.ts +26 -0
- package/dist/cli/clone-generate.d.ts.map +1 -0
- package/dist/cli/clone-generate.js +148 -0
- package/dist/{credentials.d.ts → cli/credentials.d.ts} +11 -2
- package/dist/cli/credentials.d.ts.map +1 -0
- package/dist/{credentials.js → cli/credentials.js} +88 -5
- package/dist/cli/elevenlabs-speech-provider.d.ts +26 -0
- package/dist/cli/elevenlabs-speech-provider.d.ts.map +1 -0
- package/dist/cli/elevenlabs-speech-provider.js +199 -0
- package/dist/cli/generate.d.ts +140 -0
- package/dist/cli/generate.d.ts.map +1 -0
- package/dist/cli/generate.js +604 -0
- package/dist/cli/generate.test.d.ts +2 -0
- package/dist/cli/generate.test.d.ts.map +1 -0
- package/dist/cli/generate.test.js +131 -0
- package/dist/cli/login.d.ts.map +1 -0
- package/dist/{login.js → cli/login.js} +45 -4
- package/dist/cli/main.d.ts +3 -0
- package/dist/cli/main.d.ts.map +1 -0
- package/dist/cli/main.js +7 -0
- package/dist/cli/model-catalog.d.ts +141 -0
- package/dist/cli/model-catalog.d.ts.map +1 -0
- package/dist/cli/model-catalog.js +2135 -0
- package/dist/{models.d.ts → cli/models.d.ts} +16 -9
- package/dist/cli/models.d.ts.map +1 -0
- package/dist/cli/models.js +313 -0
- package/dist/cli/open-browser.d.ts.map +1 -0
- package/dist/cli/openai-speech-provider.d.ts +3 -0
- package/dist/cli/openai-speech-provider.d.ts.map +1 -0
- package/dist/cli/openai-speech-provider.js +28 -0
- package/dist/cli/separation-generate.d.ts +34 -0
- package/dist/cli/separation-generate.d.ts.map +1 -0
- package/dist/cli/separation-generate.js +117 -0
- package/dist/cli/speech-catalog.d.ts +35 -0
- package/dist/cli/speech-catalog.d.ts.map +1 -0
- package/dist/cli/speech-catalog.js +195 -0
- package/dist/cli/speech-generate.d.ts +86 -0
- package/dist/cli/speech-generate.d.ts.map +1 -0
- package/dist/cli/speech-generate.js +156 -0
- package/dist/cli/speech-models.d.ts +8 -0
- package/dist/cli/speech-models.d.ts.map +1 -0
- package/dist/cli/speech-models.js +109 -0
- package/dist/cli/speech-provider.test.d.ts +2 -0
- package/dist/cli/speech-provider.test.d.ts.map +1 -0
- package/dist/cli/speech-provider.test.js +197 -0
- package/dist/cli/subscription.d.ts.map +1 -0
- package/dist/cli/transcription-catalog.d.ts +35 -0
- package/dist/cli/transcription-catalog.d.ts.map +1 -0
- package/dist/cli/transcription-catalog.js +192 -0
- package/dist/cli/transcription-generate.d.ts +62 -0
- package/dist/cli/transcription-generate.d.ts.map +1 -0
- package/dist/cli/transcription-generate.js +144 -0
- package/dist/cli/transcription-models.d.ts +9 -0
- package/dist/cli/transcription-models.d.ts.map +1 -0
- package/dist/cli/transcription-models.js +100 -0
- package/dist/cli/transcription-providers.d.ts +22 -0
- package/dist/cli/transcription-providers.d.ts.map +1 -0
- package/dist/cli/transcription-providers.js +290 -0
- package/dist/cli/video-model-catalog.d.ts +2 -0
- package/dist/cli/video-model-catalog.d.ts.map +1 -0
- package/dist/cli/video-model-catalog.js +5 -0
- package/dist/cli/xai-auth.d.ts +29 -0
- package/dist/cli/xai-auth.d.ts.map +1 -0
- package/dist/cli/xai-auth.js +325 -0
- package/dist/vite/angled-screen.d.ts +57 -0
- package/dist/vite/angled-screen.d.ts.map +1 -0
- package/dist/vite/angled-screen.js +115 -0
- package/dist/vite/app.d.ts +51 -0
- package/dist/vite/app.d.ts.map +1 -0
- package/dist/vite/app.js +150 -0
- package/dist/vite/bands-shader.d.ts +47 -0
- package/dist/vite/bands-shader.d.ts.map +1 -0
- package/dist/vite/bands-shader.js +267 -0
- package/dist/vite/code-block.d.ts +107 -0
- package/dist/vite/code-block.d.ts.map +1 -0
- package/dist/vite/code-block.js +641 -0
- package/dist/vite/components.d.ts +36 -0
- package/dist/vite/components.d.ts.map +1 -0
- package/dist/vite/components.js +113 -0
- package/dist/vite/dispersion-rings-shader.d.ts +44 -0
- package/dist/vite/dispersion-rings-shader.d.ts.map +1 -0
- package/dist/vite/dispersion-rings-shader.js +282 -0
- package/dist/vite/easing-curves.d.ts +129 -0
- package/dist/vite/easing-curves.d.ts.map +1 -0
- package/dist/vite/easing-curves.js +584 -0
- package/dist/vite/easing-curves.test.d.ts +2 -0
- package/dist/vite/easing-curves.test.d.ts.map +1 -0
- package/dist/vite/easing-curves.test.js +200 -0
- package/dist/vite/generated-media-client.d.ts +35 -0
- package/dist/vite/generated-media-client.d.ts.map +1 -0
- package/dist/vite/generated-media-client.js +76 -0
- package/dist/vite/keyframes.d.ts +129 -0
- package/dist/vite/keyframes.d.ts.map +1 -0
- package/dist/vite/keyframes.js +194 -0
- package/dist/vite/layout-editor.d.ts +45 -0
- package/dist/vite/layout-editor.d.ts.map +1 -0
- package/dist/vite/layout-editor.js +587 -0
- package/dist/vite/layout-transition.d.ts +136 -0
- package/dist/vite/layout-transition.d.ts.map +1 -0
- package/dist/vite/layout-transition.js +581 -0
- package/dist/vite/liquid-gradient-shader.d.ts +62 -0
- package/dist/vite/liquid-gradient-shader.d.ts.map +1 -0
- package/dist/vite/liquid-gradient-shader.js +413 -0
- package/dist/vite/mdx-client.d.ts +15 -0
- package/dist/vite/mdx-client.d.ts.map +1 -0
- package/dist/vite/mdx-client.js +265 -0
- package/dist/vite/mdx-parse.d.ts +102 -0
- package/dist/vite/mdx-parse.d.ts.map +1 -0
- package/dist/vite/mdx-parse.js +251 -0
- package/dist/vite/mdx-provided-components.d.ts +18 -0
- package/dist/vite/mdx-provided-components.d.ts.map +1 -0
- package/dist/vite/mdx-provided-components.js +14 -0
- package/dist/vite/mdx-video.d.ts +327 -0
- package/dist/vite/mdx-video.d.ts.map +1 -0
- package/dist/vite/mdx-video.js +596 -0
- package/dist/vite/mdx-video.test.d.ts +9 -0
- package/dist/vite/mdx-video.test.d.ts.map +1 -0
- package/dist/vite/mdx-video.test.js +1945 -0
- package/dist/vite/media-components.d.ts +45 -0
- package/dist/vite/media-components.d.ts.map +1 -0
- package/dist/vite/media-components.js +355 -0
- package/dist/vite/media-duration-store.d.ts +66 -0
- package/dist/vite/media-duration-store.d.ts.map +1 -0
- package/dist/vite/media-duration-store.js +204 -0
- package/dist/vite/player-page.d.ts +35 -0
- package/dist/vite/player-page.d.ts.map +1 -0
- package/dist/vite/player-page.js +720 -0
- package/dist/vite/render-client.d.ts +18 -0
- package/dist/vite/render-client.d.ts.map +1 -0
- package/dist/vite/render-client.js +67 -0
- package/dist/vite/sdk.d.ts +221 -0
- package/dist/vite/sdk.d.ts.map +1 -0
- package/dist/vite/sdk.js +355 -0
- package/dist/vite/server-components.d.ts +55 -0
- package/dist/vite/server-components.d.ts.map +1 -0
- package/dist/vite/server-components.js +156 -0
- package/dist/vite/server-mdx.d.ts +74 -0
- package/dist/vite/server-mdx.d.ts.map +1 -0
- package/dist/vite/server-mdx.js +259 -0
- package/dist/vite/shader-renderer.d.ts +48 -0
- package/dist/vite/shader-renderer.d.ts.map +1 -0
- package/dist/vite/shader-renderer.js +411 -0
- package/dist/vite/store-hooks.d.ts +12 -0
- package/dist/vite/store-hooks.d.ts.map +1 -0
- package/dist/vite/store-hooks.js +93 -0
- package/dist/vite/store.d.ts +75 -0
- package/dist/vite/store.d.ts.map +1 -0
- package/dist/vite/store.js +52 -0
- package/dist/vite/store.test.d.ts +6 -0
- package/dist/vite/store.test.d.ts.map +1 -0
- package/dist/vite/store.test.js +172 -0
- package/dist/vite/tweakpane-hook.d.ts +94 -0
- package/dist/vite/tweakpane-hook.d.ts.map +1 -0
- package/dist/vite/tweakpane-hook.js +450 -0
- package/dist/vite/vite-plugin.d.ts +28 -0
- package/dist/vite/vite-plugin.d.ts.map +1 -0
- package/dist/vite/vite-plugin.js +538 -0
- package/dist/vite/wave-gradient-shader.d.ts +41 -0
- package/dist/vite/wave-gradient-shader.d.ts.map +1 -0
- package/dist/vite/wave-gradient-shader.js +207 -0
- package/package.json +103 -25
- package/skills/egaki/SKILL.md +162 -1
- package/src/cli/cache-utils.ts +169 -0
- package/src/cli/cached-generate.test.ts +531 -0
- package/src/cli/cached-generate.ts +348 -0
- package/src/cli/cartesia-provider.ts +249 -0
- package/src/cli/cli.ts +1818 -0
- package/src/cli/clone-generate.ts +202 -0
- package/src/{credentials.ts → cli/credentials.ts} +90 -7
- package/src/cli/elevenlabs-speech-provider.ts +243 -0
- package/src/cli/generate.test.ts +166 -0
- package/src/cli/generate.ts +912 -0
- package/src/{login.ts → cli/login.ts} +48 -3
- package/src/cli/main.ts +8 -0
- package/src/cli/model-catalog.ts +2336 -0
- package/src/cli/models.ts +408 -0
- package/src/cli/openai-speech-provider.ts +34 -0
- package/src/cli/separation-generate.ts +187 -0
- package/src/cli/speech-catalog.ts +253 -0
- package/src/cli/speech-generate.ts +275 -0
- package/src/cli/speech-models.ts +146 -0
- package/src/cli/speech-provider.test.ts +226 -0
- package/src/cli/transcription-catalog.ts +250 -0
- package/src/cli/transcription-generate.ts +214 -0
- package/src/cli/transcription-models.ts +136 -0
- package/src/cli/transcription-providers.ts +427 -0
- package/src/cli/video-model-catalog.ts +14 -0
- package/src/cli/xai-auth.ts +396 -0
- package/src/vite/angled-screen.tsx +214 -0
- package/src/vite/app.tsx +197 -0
- package/src/vite/bands-shader.tsx +319 -0
- package/src/vite/code-block.tsx +939 -0
- package/src/vite/components.tsx +278 -0
- package/src/vite/dispersion-rings-shader.tsx +331 -0
- package/src/vite/easing-curves.test.ts +232 -0
- package/src/vite/easing-curves.ts +743 -0
- package/src/vite/generated-media-client.tsx +121 -0
- package/src/vite/keyframes.tsx +347 -0
- package/src/vite/layout-editor.tsx +709 -0
- package/src/vite/layout-transition.tsx +706 -0
- package/src/vite/liquid-gradient-shader.tsx +474 -0
- package/src/vite/mdx-client.tsx +420 -0
- package/src/vite/mdx-parse.ts +343 -0
- package/src/vite/mdx-provided-components.ts +19 -0
- package/src/vite/mdx-video.test.tsx +2219 -0
- package/src/vite/mdx-video.tsx +976 -0
- package/src/vite/media-components.tsx +473 -0
- package/src/vite/media-duration-store.ts +225 -0
- package/src/vite/motion-timing.ts +194 -0
- package/src/vite/player-page.tsx +1107 -0
- package/src/vite/render-client.ts +84 -0
- package/src/vite/sdk.ts +598 -0
- package/src/vite/server-components.tsx +241 -0
- package/src/vite/server-mdx.ts +285 -0
- package/src/vite/shader-renderer.tsx +535 -0
- package/src/vite/store-hooks.ts +117 -0
- package/src/vite/store.test.ts +202 -0
- package/src/vite/store.ts +115 -0
- package/src/vite/styles.css +21 -0
- package/src/vite/tweakpane-hook.tsx +593 -0
- package/src/vite/virtual-modules.d.ts +30 -0
- package/src/vite/vite-plugin.ts +584 -0
- package/src/vite/wave-gradient-shader.tsx +253 -0
- package/README.md +0 -266
- package/dist/antigravity-auth.d.ts +0 -15
- package/dist/antigravity-auth.d.ts.map +0 -1
- package/dist/antigravity-auth.js +0 -544
- package/dist/antigravity-image.d.ts +0 -11
- package/dist/antigravity-image.d.ts.map +0 -1
- package/dist/antigravity-image.js +0 -285
- package/dist/chatgpt-auth.d.ts.map +0 -1
- package/dist/cli.d.ts +0 -3
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js +0 -968
- package/dist/credentials.d.ts.map +0 -1
- package/dist/gateway-model-candidates.generated.d.ts +0 -11
- package/dist/gateway-model-candidates.generated.d.ts.map +0 -1
- package/dist/gateway-model-candidates.generated.js +0 -64
- package/dist/login.d.ts.map +0 -1
- package/dist/model-catalog.d.ts +0 -42
- package/dist/model-catalog.d.ts.map +0 -1
- package/dist/model-catalog.js +0 -1001
- package/dist/models.d.ts.map +0 -1
- package/dist/models.js +0 -246
- package/dist/open-browser.d.ts.map +0 -1
- package/dist/subscription.d.ts.map +0 -1
- package/dist/video-model-catalog.d.ts +0 -52
- package/dist/video-model-catalog.d.ts.map +0 -1
- package/dist/video-model-catalog.js +0 -306
- package/src/cli.ts +0 -1378
- package/src/model-catalog.ts +0 -1077
- package/src/models.ts +0 -309
- package/src/video-model-catalog.ts +0 -363
- /package/dist/{chatgpt-auth.d.ts → cli/chatgpt-auth.d.ts} +0 -0
- /package/dist/{chatgpt-auth.js → cli/chatgpt-auth.js} +0 -0
- /package/dist/{login.d.ts → cli/login.d.ts} +0 -0
- /package/dist/{open-browser.d.ts → cli/open-browser.d.ts} +0 -0
- /package/dist/{open-browser.js → cli/open-browser.js} +0 -0
- /package/dist/{subscription.d.ts → cli/subscription.d.ts} +0 -0
- /package/dist/{subscription.js → cli/subscription.js} +0 -0
- /package/src/{chatgpt-auth.ts → cli/chatgpt-auth.ts} +0 -0
- /package/src/{open-browser.ts → cli/open-browser.ts} +0 -0
- /package/src/{subscription.ts → cli/subscription.ts} +0 -0
|
@@ -0,0 +1,584 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vite plugin for the video framework.
|
|
3
|
+
*
|
|
4
|
+
* Discovers MDX files in the project root and serves each as a page route.
|
|
5
|
+
* The default entry (video.mdx or index.mdx) is served at /; others at
|
|
6
|
+
* /<relative-path-without-extension>. Auto-injects spiceflow + react plugins.
|
|
7
|
+
*
|
|
8
|
+
* Usage in vite.config.ts:
|
|
9
|
+
* import { video } from 'egaki/vite'
|
|
10
|
+
* export default defineConfig({ plugins: [video()] })
|
|
11
|
+
* // or with explicit default entry:
|
|
12
|
+
* export default defineConfig({ plugins: [video({ entry: './video.mdx' })] })
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import path from 'node:path'
|
|
16
|
+
import fs from 'node:fs'
|
|
17
|
+
import { createRequire } from 'node:module'
|
|
18
|
+
import { fileURLToPath } from 'node:url'
|
|
19
|
+
import type { Plugin, PluginOption } from 'vite'
|
|
20
|
+
import { spiceflowPlugin } from 'spiceflow/vite'
|
|
21
|
+
import react from '@vitejs/plugin-react'
|
|
22
|
+
import tailwindcss from '@tailwindcss/vite'
|
|
23
|
+
import { mdxParse } from 'safe-mdx/parse'
|
|
24
|
+
import { collectServerImportSources } from './server-mdx.ts'
|
|
25
|
+
import { parseFrontmatter } from './mdx-parse.ts'
|
|
26
|
+
|
|
27
|
+
// Resolve the package src/ directory from this file's location.
|
|
28
|
+
// Used for resolve.alias so the RSC module runner can resolve relative
|
|
29
|
+
// imports from app.tsx (same pattern as egaki/vite).
|
|
30
|
+
const __srcDir = fileURLToPath(new URL('.', import.meta.url))
|
|
31
|
+
const APP_SRC_PATH = path.join(__srcDir, 'app.tsx')
|
|
32
|
+
|
|
33
|
+
const VIRTUAL_APP = 'virtual:egaki-app'
|
|
34
|
+
const RESOLVED_APP = '\0' + VIRTUAL_APP
|
|
35
|
+
|
|
36
|
+
const VIRTUAL_MDX = 'virtual:egaki-mdx'
|
|
37
|
+
const RESOLVED_MDX = '\0' + VIRTUAL_MDX
|
|
38
|
+
|
|
39
|
+
const VIRTUAL_MODULES = 'virtual:egaki-modules'
|
|
40
|
+
const RESOLVED_MODULES = '\0' + VIRTUAL_MODULES
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
const PKG_NAME = 'egaki'
|
|
45
|
+
|
|
46
|
+
export interface VideoPluginOptions {
|
|
47
|
+
/** Path to the default MDX entry file (relative to vite root or absolute).
|
|
48
|
+
* When omitted, auto-discovers: video.mdx > index.mdx > first .mdx found.
|
|
49
|
+
* All other .mdx files in the project become additional routes. */
|
|
50
|
+
entry?: string
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Resolve a relative MDX import source against the project root,
|
|
54
|
+
* probing common extensions. Returns the absolute path or undefined. */
|
|
55
|
+
function resolveSourceToFile(root: string, source: string): string | undefined {
|
|
56
|
+
const base = path.resolve(root, source)
|
|
57
|
+
for (const ext of ['', '.tsx', '.ts', '.jsx', '.js', '.mdx', '.md']) {
|
|
58
|
+
const candidate = base + ext
|
|
59
|
+
if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {
|
|
60
|
+
return candidate.replace(/\\/g, '/')
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return undefined
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Extract raw source text ranges per section directly from the mdast.
|
|
68
|
+
* Works with heading nodes' positions to capture the FULL raw heading
|
|
69
|
+
* text including props like `duration=3s` and `transition=20` that
|
|
70
|
+
* `splitIntoSections` strips during parsing. Each section spans from
|
|
71
|
+
* its heading's start offset to the next heading's start (or EOF).
|
|
72
|
+
*/
|
|
73
|
+
function sectionRawTexts(source: string, mdast: any): string[] {
|
|
74
|
+
// Collect the start offsets of each heading node
|
|
75
|
+
const headingStarts: number[] = []
|
|
76
|
+
for (const node of mdast.children) {
|
|
77
|
+
if (node.type === 'heading' && node.position?.start?.offset != null) {
|
|
78
|
+
headingStarts.push(node.position.start.offset)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// Slice source between consecutive headings, trimming trailing whitespace
|
|
82
|
+
// so boundary changes (adding/removing a following section) don't cause
|
|
83
|
+
// false positives on the preceding section.
|
|
84
|
+
return headingStarts.map((start, i) => {
|
|
85
|
+
const end = i + 1 < headingStarts.length ? headingStarts[i + 1]! : source.length
|
|
86
|
+
return source.slice(start, end).trimEnd()
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Compare old and new MDX sources section-by-section. Returns the index
|
|
92
|
+
* of the first added or edited section, or null if nothing changed (or
|
|
93
|
+
* only deletions occurred, which we skip per design).
|
|
94
|
+
*/
|
|
95
|
+
export function findChangedSectionIndex(oldSource: string, newSource: string): number | null {
|
|
96
|
+
try {
|
|
97
|
+
const oldTexts = sectionRawTexts(oldSource, mdxParse(oldSource))
|
|
98
|
+
const newTexts = sectionRawTexts(newSource, mdxParse(newSource))
|
|
99
|
+
|
|
100
|
+
const maxLen = Math.max(oldTexts.length, newTexts.length)
|
|
101
|
+
for (let i = 0; i < maxLen; i++) {
|
|
102
|
+
if (i >= oldTexts.length) return i // new section added
|
|
103
|
+
if (i >= newTexts.length) return null // section deleted, skip
|
|
104
|
+
if (oldTexts[i] !== newTexts[i]) {
|
|
105
|
+
// Distinguish edit from deletion: if the next old section matches
|
|
106
|
+
// the current new section, a section was deleted (shifted down).
|
|
107
|
+
// Look ahead to detect deletions at any position, not just the tail.
|
|
108
|
+
if (newTexts.length < oldTexts.length && oldTexts[i + 1] === newTexts[i]) {
|
|
109
|
+
return null // deletion, skip
|
|
110
|
+
}
|
|
111
|
+
return i // content changed
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return null // no scene-level change (frontmatter/preamble only)
|
|
115
|
+
} catch {
|
|
116
|
+
return null // parse error, don't seek
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** Discover .mdx files in the project root directory (non-recursive).
|
|
121
|
+
* Only root-level files become entries to avoid import resolution issues
|
|
122
|
+
* with nested paths. Returns a map of routePath → absolutePath. */
|
|
123
|
+
function discoverMdxEntries(root: string): Map<string, string> {
|
|
124
|
+
const entries = new Map<string, string>()
|
|
125
|
+
if (!fs.existsSync(root)) return entries
|
|
126
|
+
for (const entry of fs.readdirSync(root, { withFileTypes: true })) {
|
|
127
|
+
if (entry.isDirectory()) continue
|
|
128
|
+
if (!/\.mdx$/.test(entry.name)) continue
|
|
129
|
+
const fullPath = path.join(root, entry.name)
|
|
130
|
+
const routePath = entry.name.replace(/\.mdx$/, '')
|
|
131
|
+
entries.set(routePath, fullPath.replace(/\\/g, '/'))
|
|
132
|
+
}
|
|
133
|
+
return entries
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** Pick the default entry route from discovered entries.
|
|
137
|
+
* Priority: explicit option > video.mdx > index.mdx > first alphabetically. */
|
|
138
|
+
function resolveDefaultRoute(entries: Map<string, string>, explicitEntry?: string, root?: string): string {
|
|
139
|
+
if (explicitEntry && root) {
|
|
140
|
+
const absEntry = path.isAbsolute(explicitEntry)
|
|
141
|
+
? explicitEntry.replace(/\\/g, '/')
|
|
142
|
+
: path.resolve(root, explicitEntry).replace(/\\/g, '/')
|
|
143
|
+
for (const [route, absPath] of entries) {
|
|
144
|
+
if (absPath === absEntry) return route
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (entries.has('video')) return 'video'
|
|
148
|
+
if (entries.has('index')) return 'index'
|
|
149
|
+
// First alphabetically
|
|
150
|
+
const sorted = [...entries.keys()].sort()
|
|
151
|
+
return sorted[0] ?? 'video'
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export function video(options?: VideoPluginOptions): PluginOption[] {
|
|
155
|
+
let root: string
|
|
156
|
+
/** All discovered MDX entry files: routePath → absolutePath */
|
|
157
|
+
let mdxEntries: Map<string, string> = new Map()
|
|
158
|
+
/** Route path of the default entry (served at /) */
|
|
159
|
+
let defaultRoute: string = ''
|
|
160
|
+
/** Whether the user's project has `motion` (framer-motion) installed. */
|
|
161
|
+
let hasMotion = false
|
|
162
|
+
/** Cached previous MDX sources for section-level diff detection, keyed by abs path. */
|
|
163
|
+
const previousMdxSources: Map<string, string> = new Map()
|
|
164
|
+
/** Set of absolute paths of all entry MDX files for quick lookup. */
|
|
165
|
+
let entryPathSet: Set<string> = new Set()
|
|
166
|
+
|
|
167
|
+
/** Is this file referenced inside a <Server> block of any entry MDX?
|
|
168
|
+
* Parsed on demand (no cache — file changes are rare and parsing is
|
|
169
|
+
* milliseconds). Used to decide which edits need an rsc:update. */
|
|
170
|
+
const isServerImportedFile = (file: string): boolean => {
|
|
171
|
+
try {
|
|
172
|
+
for (const absPath of entryPathSet) {
|
|
173
|
+
const sources = collectServerImportSources(mdxParse(fs.readFileSync(absPath, 'utf-8')))
|
|
174
|
+
if (sources.some((source) => resolveSourceToFile(root, source) === file)) return true
|
|
175
|
+
}
|
|
176
|
+
return false
|
|
177
|
+
} catch {
|
|
178
|
+
return false
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const videoPlugin: Plugin = {
|
|
183
|
+
name: 'egaki:core',
|
|
184
|
+
|
|
185
|
+
configResolved(config) {
|
|
186
|
+
root = config.root
|
|
187
|
+
|
|
188
|
+
// Discover all MDX entries in the project root.
|
|
189
|
+
mdxEntries = discoverMdxEntries(root)
|
|
190
|
+
|
|
191
|
+
// If explicit entry is provided but not found, create a minimal entry
|
|
192
|
+
// in the map (will error below if file doesn't exist).
|
|
193
|
+
if (options?.entry) {
|
|
194
|
+
const absEntry = (path.isAbsolute(options.entry)
|
|
195
|
+
? options.entry
|
|
196
|
+
: path.resolve(root, options.entry)).replace(/\\/g, '/')
|
|
197
|
+
if (!fs.existsSync(absEntry)) {
|
|
198
|
+
throw new Error(
|
|
199
|
+
`[egaki] entry file not found: ${absEntry}\n` +
|
|
200
|
+
`Set entry to a path relative to the vite root.`,
|
|
201
|
+
)
|
|
202
|
+
}
|
|
203
|
+
// Ensure explicit entry is in the map
|
|
204
|
+
const relPath = path.relative(root, absEntry).replace(/\\/g, '/')
|
|
205
|
+
const routePath = relPath.replace(/\.mdx$/, '')
|
|
206
|
+
mdxEntries.set(routePath, absEntry)
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (mdxEntries.size === 0) {
|
|
210
|
+
throw new Error(
|
|
211
|
+
`[egaki] no .mdx files found in ${root}\n` +
|
|
212
|
+
`Create a video.mdx file or set entry explicitly.`,
|
|
213
|
+
)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
defaultRoute = resolveDefaultRoute(mdxEntries, options?.entry, root)
|
|
217
|
+
entryPathSet = new Set(mdxEntries.values())
|
|
218
|
+
|
|
219
|
+
// Seed previous sources for section-level diff detection.
|
|
220
|
+
for (const [, absPath] of mdxEntries) {
|
|
221
|
+
try {
|
|
222
|
+
previousMdxSources.set(absPath, fs.readFileSync(absPath, 'utf-8'))
|
|
223
|
+
} catch { /* ignore read errors */ }
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Auto-generate egaki-env.d.ts so MDX LSP knows about built-in
|
|
227
|
+
// components via the global MDXProvidedComponents type. Same
|
|
228
|
+
// pattern Vite uses for vite-env.d.ts.
|
|
229
|
+
const envDtsPath = path.join(root, 'egaki-env.d.ts')
|
|
230
|
+
const envDtsContent = 'import \'egaki/mdx-components\'\n'
|
|
231
|
+
try {
|
|
232
|
+
const existing = fs.existsSync(envDtsPath) ? fs.readFileSync(envDtsPath, 'utf-8') : ''
|
|
233
|
+
if (existing !== envDtsContent) {
|
|
234
|
+
fs.writeFileSync(envDtsPath, envDtsContent)
|
|
235
|
+
}
|
|
236
|
+
} catch {
|
|
237
|
+
// Non-fatal: LSP autocomplete just won't work
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Detect if the user has `motion` (framer-motion) installed.
|
|
241
|
+
// When present, we inject timing patches so motion.div animations
|
|
242
|
+
// sync with Remotion's frame-based rendering.
|
|
243
|
+
try {
|
|
244
|
+
createRequire(root + '/').resolve('motion-dom')
|
|
245
|
+
hasMotion = true
|
|
246
|
+
} catch {
|
|
247
|
+
hasMotion = false
|
|
248
|
+
}
|
|
249
|
+
},
|
|
250
|
+
|
|
251
|
+
resolveId(id) {
|
|
252
|
+
if (id === VIRTUAL_APP) return RESOLVED_APP
|
|
253
|
+
if (id === VIRTUAL_MDX) return RESOLVED_MDX
|
|
254
|
+
if (id === VIRTUAL_MODULES) return RESOLVED_MODULES
|
|
255
|
+
},
|
|
256
|
+
|
|
257
|
+
load(id) {
|
|
258
|
+
if (id === RESOLVED_MDX) {
|
|
259
|
+
// Import all entry MDX files as raw strings (?raw for HMR tracking).
|
|
260
|
+
// Export an entries map so app.tsx can serve each at its route.
|
|
261
|
+
const defaultAbsPath = mdxEntries.get(defaultRoute)!
|
|
262
|
+
// Parse frontmatter from the default entry for composition dimensions.
|
|
263
|
+
let fm: ReturnType<typeof parseFrontmatter>
|
|
264
|
+
try {
|
|
265
|
+
const mdxContent = fs.readFileSync(defaultAbsPath, 'utf-8')
|
|
266
|
+
fm = parseFrontmatter(mdxParse(mdxContent))
|
|
267
|
+
} catch (e) {
|
|
268
|
+
console.error('[egaki] frontmatter parse error:', e)
|
|
269
|
+
fm = { fps: 30, bpm: 120, width: 1920, height: 1080, scale: 1 }
|
|
270
|
+
}
|
|
271
|
+
const folderName = path.basename(root)
|
|
272
|
+
|
|
273
|
+
const imports: string[] = []
|
|
274
|
+
const entriesObj: string[] = []
|
|
275
|
+
const pathsObj: string[] = []
|
|
276
|
+
let i = 0
|
|
277
|
+
for (const [routePath, absPath] of mdxEntries) {
|
|
278
|
+
const varName = `__entry${i++}`
|
|
279
|
+
imports.push(`import ${varName} from ${JSON.stringify(absPath + '?raw')}`)
|
|
280
|
+
entriesObj.push(` ${JSON.stringify(routePath)}: ${varName}`)
|
|
281
|
+
pathsObj.push(` ${JSON.stringify(routePath)}: ${JSON.stringify(absPath)}`)
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return [
|
|
285
|
+
...imports,
|
|
286
|
+
`export const entries = {`,
|
|
287
|
+
entriesObj.join(',\n'),
|
|
288
|
+
`}`,
|
|
289
|
+
`export const entryPaths = {`,
|
|
290
|
+
pathsObj.join(',\n'),
|
|
291
|
+
`}`,
|
|
292
|
+
`export const defaultRoute = ${JSON.stringify(defaultRoute)}`,
|
|
293
|
+
`export const projectRoot = ${JSON.stringify(root.replace(/\\/g, '/'))}`,
|
|
294
|
+
`export const compositionWidth = ${fm.width}`,
|
|
295
|
+
`export const compositionHeight = ${fm.height}`,
|
|
296
|
+
`export const folderName = ${JSON.stringify(folderName)}`,
|
|
297
|
+
// Backward compat: default export is the default entry source
|
|
298
|
+
`export default entries[${JSON.stringify(defaultRoute)}]`,
|
|
299
|
+
`export const entryPath = entryPaths[${JSON.stringify(defaultRoute)}]`,
|
|
300
|
+
].join('\n')
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (id === RESOLVED_MODULES) {
|
|
304
|
+
// Build an eager module map for all user files in the project
|
|
305
|
+
// root. Each file is imported statically so modules are available
|
|
306
|
+
// synchronously — no async resolution, no loading state.
|
|
307
|
+
//
|
|
308
|
+
// This map is only imported by the client (and ssr) — the rsc env
|
|
309
|
+
// resolves <Server> slot modules via dynamic imports in app.tsx.
|
|
310
|
+
// *.server.{ts,tsx} files are excluded: that postfix is the hard
|
|
311
|
+
// "never bundle to the browser" guarantee for files with API keys
|
|
312
|
+
// or node-only imports.
|
|
313
|
+
const imports: string[] = []
|
|
314
|
+
const entries: string[] = []
|
|
315
|
+
let i = 0
|
|
316
|
+
const walkDir = (dir: string) => {
|
|
317
|
+
if (!fs.existsSync(dir)) return
|
|
318
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
319
|
+
if (entry.name === 'node_modules' || entry.name === 'dist' || entry.name === 'e2e' || entry.name === 'test-results' || entry.name.startsWith('.')) continue
|
|
320
|
+
const fullPath = path.join(dir, entry.name)
|
|
321
|
+
if (entry.isDirectory()) {
|
|
322
|
+
walkDir(fullPath)
|
|
323
|
+
} else if (/\.(tsx?|jsx?|mdx?)$/.test(entry.name) && !/\.(test|spec|config)\./.test(entry.name)) {
|
|
324
|
+
// Entry MDX files are NOT skipped: they may be imported as
|
|
325
|
+
// partials by other MDX files (e.g. `import Intro from './intro.mdx'`).
|
|
326
|
+
// Self-import is a user error and harmless (safe-mdx handles it).
|
|
327
|
+
if (/\.server\.[jt]sx?$/.test(entry.name)) continue
|
|
328
|
+
const isMdx = /\.mdx?$/.test(entry.name)
|
|
329
|
+
const relPath = './' + path.relative(root, fullPath).replace(/\\/g, '/')
|
|
330
|
+
const absPath = fullPath.replace(/\\/g, '/')
|
|
331
|
+
const varName = `__mod${i++}`
|
|
332
|
+
if (isMdx) {
|
|
333
|
+
// MDX/MD files loaded as raw strings for client rendering
|
|
334
|
+
imports.push(`import ${varName} from ${JSON.stringify(absPath + '?raw')}`)
|
|
335
|
+
entries.push(` ${JSON.stringify(relPath)}: { default: ${varName} }`)
|
|
336
|
+
} else {
|
|
337
|
+
imports.push(`import * as ${varName} from ${JSON.stringify(absPath)}`)
|
|
338
|
+
entries.push(` ${JSON.stringify(relPath)}: ${varName}`)
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
walkDir(root)
|
|
344
|
+
|
|
345
|
+
// No self-accept here: mdx-client.tsx accepts updates of this
|
|
346
|
+
// module via import.meta.hot.accept('virtual:egaki-modules', cb).
|
|
347
|
+
// When a user file changes, HMR propagates through this module to
|
|
348
|
+
// that boundary, re-executing this module with fresh imports and
|
|
349
|
+
// handing the new map to the callback. Self-accepting here would
|
|
350
|
+
// make THIS module the boundary and the importer callback would
|
|
351
|
+
// never fire.
|
|
352
|
+
// When motion is installed, import motion-timing.ts as a
|
|
353
|
+
// side-effect so JSAnimation patches run before any user component.
|
|
354
|
+
// resolve.dedupe (set in configEnvironment) ensures motion-dom
|
|
355
|
+
// resolves to the same instance that motion/react uses.
|
|
356
|
+
const motionTimingPath = path.join(__srcDir, 'motion-timing.ts').replace(/\\/g, '/')
|
|
357
|
+
const motionImport = hasMotion ? [`import ${JSON.stringify(motionTimingPath)}`] : []
|
|
358
|
+
|
|
359
|
+
return [
|
|
360
|
+
...motionImport,
|
|
361
|
+
...imports,
|
|
362
|
+
`export const eagerModules = {`,
|
|
363
|
+
entries.join(',\n'),
|
|
364
|
+
`}`,
|
|
365
|
+
].join('\n')
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
if (id === RESOLVED_APP) {
|
|
369
|
+
// Spiceflow entry: import the framework's app from its absolute
|
|
370
|
+
// source path so the RSC module runner resolves relative imports
|
|
371
|
+
// (./mdx-parse.ts etc.) from the correct filesystem directory.
|
|
372
|
+
return [
|
|
373
|
+
`import { app } from ${JSON.stringify(APP_SRC_PATH)}`,
|
|
374
|
+
`export { app }`,
|
|
375
|
+
].join('\n')
|
|
376
|
+
}
|
|
377
|
+
},
|
|
378
|
+
|
|
379
|
+
// HMR for file changes in the project.
|
|
380
|
+
//
|
|
381
|
+
// Entry MDX: the source string flows server → client through the RSC
|
|
382
|
+
// flight payload, so invalidate the virtual modules in all envs and
|
|
383
|
+
// send rsc:update to re-fetch the flight.
|
|
384
|
+
//
|
|
385
|
+
// User .tsx/.ts/.mdx/.css files: handled in the client module graph
|
|
386
|
+
// (Fast Refresh for components, dep-accept in mdx-client.tsx for the
|
|
387
|
+
// rest) AND via rsc:update, because <Server> slots are rendered in the
|
|
388
|
+
// rsc env from the same files — the flight refetch delivers fresh
|
|
389
|
+
// slots. On rsc/ssr envs we invalidate the changed modules manually
|
|
390
|
+
// and return [] to suppress default HMR, which would trigger an SSR
|
|
391
|
+
// "program reload" → full page reload.
|
|
392
|
+
//
|
|
393
|
+
// File create/delete: the generated module list changed and no accept
|
|
394
|
+
// chain exists for new files, so invalidate everything + full reload.
|
|
395
|
+
hotUpdate(ctx) {
|
|
396
|
+
const normalizedFile = ctx.file.replace(/\\/g, '/')
|
|
397
|
+
const isEntryMdx = entryPathSet.has(normalizedFile)
|
|
398
|
+
const isImportedMdx = /\.mdx?$/.test(ctx.file)
|
|
399
|
+
&& !isEntryMdx
|
|
400
|
+
&& !ctx.file.includes('node_modules')
|
|
401
|
+
&& ctx.file.startsWith(root)
|
|
402
|
+
const isUserFile = /\.[jt]sx?$/.test(ctx.file)
|
|
403
|
+
&& !ctx.file.includes('node_modules')
|
|
404
|
+
&& ctx.file.startsWith(root)
|
|
405
|
+
const isCss = /\.css$/.test(ctx.file)
|
|
406
|
+
&& !ctx.file.includes('node_modules')
|
|
407
|
+
&& ctx.file.startsWith(root)
|
|
408
|
+
|
|
409
|
+
if (!isEntryMdx && !isImportedMdx && !isUserFile && !isCss) return
|
|
410
|
+
|
|
411
|
+
const invalidateVirtual = (ids: string[]) => {
|
|
412
|
+
for (const env of Object.values(ctx.server.environments)) {
|
|
413
|
+
for (const resolvedId of ids) {
|
|
414
|
+
const mod = env.moduleGraph.getModuleById(resolvedId)
|
|
415
|
+
if (mod) {
|
|
416
|
+
env.moduleGraph.invalidateModule(mod)
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// Create/delete: regenerate module list and entry map, full reload.
|
|
423
|
+
if (ctx.type !== 'update') {
|
|
424
|
+
// Re-discover entries in case a new MDX file was added or removed.
|
|
425
|
+
mdxEntries = discoverMdxEntries(root)
|
|
426
|
+
if (options?.entry) {
|
|
427
|
+
const absEntry = (path.isAbsolute(options.entry)
|
|
428
|
+
? options.entry
|
|
429
|
+
: path.resolve(root, options.entry)).replace(/\\/g, '/')
|
|
430
|
+
const relPath = path.relative(root, absEntry).replace(/\\/g, '/')
|
|
431
|
+
mdxEntries.set(relPath.replace(/\.mdx$/, ''), absEntry)
|
|
432
|
+
}
|
|
433
|
+
defaultRoute = resolveDefaultRoute(mdxEntries, options?.entry, root)
|
|
434
|
+
const nextEntryPathSet = new Set(mdxEntries.values())
|
|
435
|
+
|
|
436
|
+
// Sync previousMdxSources: seed new entries, prune removed ones.
|
|
437
|
+
for (const file of previousMdxSources.keys()) {
|
|
438
|
+
if (!nextEntryPathSet.has(file)) previousMdxSources.delete(file)
|
|
439
|
+
}
|
|
440
|
+
for (const file of nextEntryPathSet) {
|
|
441
|
+
if (!previousMdxSources.has(file)) {
|
|
442
|
+
try { previousMdxSources.set(file, fs.readFileSync(file, 'utf-8')) }
|
|
443
|
+
catch { /* file may be mid-write */ }
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
entryPathSet = nextEntryPathSet
|
|
447
|
+
|
|
448
|
+
invalidateVirtual([RESOLVED_APP, RESOLVED_MDX, RESOLVED_MODULES])
|
|
449
|
+
if (this.environment.name === 'client') {
|
|
450
|
+
ctx.server.environments.client?.hot.send({ type: 'full-reload' })
|
|
451
|
+
}
|
|
452
|
+
return []
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
if (isEntryMdx) {
|
|
456
|
+
invalidateVirtual([RESOLVED_APP, RESOLVED_MDX])
|
|
457
|
+
|
|
458
|
+
if (this.environment.name === 'client') {
|
|
459
|
+
// Section-level diff detection for the changed entry.
|
|
460
|
+
const newMdxSource = fs.readFileSync(normalizedFile, 'utf-8')
|
|
461
|
+
const prevSource = previousMdxSources.get(normalizedFile)
|
|
462
|
+
const changedSection = prevSource != null
|
|
463
|
+
? findChangedSectionIndex(prevSource, newMdxSource)
|
|
464
|
+
: null
|
|
465
|
+
previousMdxSources.set(normalizedFile, newMdxSource)
|
|
466
|
+
|
|
467
|
+
ctx.server.environments.client?.hot.send({
|
|
468
|
+
type: 'custom',
|
|
469
|
+
event: 'rsc:update',
|
|
470
|
+
data: { file: ctx.file },
|
|
471
|
+
})
|
|
472
|
+
if (changedSection != null) {
|
|
473
|
+
ctx.server.environments.client?.hot.send({
|
|
474
|
+
type: 'custom',
|
|
475
|
+
event: 'egaki:scene-changed',
|
|
476
|
+
data: { sectionIndex: changedSection, file: normalizedFile },
|
|
477
|
+
})
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
return []
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// User file / imported MDX / CSS updates.
|
|
484
|
+
// Client env: let default HMR run (Fast Refresh for components,
|
|
485
|
+
// dep-accept propagation through virtual:egaki-modules for the rest).
|
|
486
|
+
if (this.environment.name === 'client') {
|
|
487
|
+
return
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// rsc/ssr envs: keep graphs fresh for the next render, but suppress
|
|
491
|
+
// default HMR (would cause a full program reload).
|
|
492
|
+
invalidateVirtual([RESOLVED_APP, RESOLVED_MODULES])
|
|
493
|
+
for (const mod of ctx.modules) {
|
|
494
|
+
this.environment.moduleGraph.invalidateModule(mod)
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// Edits to files referenced inside <Server> (or *.server.* postfix)
|
|
498
|
+
// send rsc:update: <Server> slots render in the rsc env, so the
|
|
499
|
+
// flight must be refetched for fresh slot content. Sent from the
|
|
500
|
+
// rsc branch AFTER invalidation so the browser's refetch cannot
|
|
501
|
+
// race a stale rsc module graph. The refetch remounts the client
|
|
502
|
+
// tree (spiceflow payload swap resets the Player to frame 0), so it
|
|
503
|
+
// must NOT fire for regular files — those are covered by
|
|
504
|
+
// client-graph HMR which preserves player state.
|
|
505
|
+
if (this.environment.name === 'rsc') {
|
|
506
|
+
const file = ctx.file.replace(/\\/g, '/')
|
|
507
|
+
if (/\.server\.[jt]sx?$/.test(file) || isServerImportedFile(file)) {
|
|
508
|
+
ctx.server.environments.client?.hot.send({
|
|
509
|
+
type: 'custom',
|
|
510
|
+
event: 'rsc:update',
|
|
511
|
+
data: { file: ctx.file },
|
|
512
|
+
})
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
return []
|
|
516
|
+
},
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// Keep the video package inside the RSC/SSR transform pipeline
|
|
520
|
+
const rscPackagePlugin: Plugin = {
|
|
521
|
+
name: 'egaki:rsc-package',
|
|
522
|
+
configEnvironment(name, config) {
|
|
523
|
+
// noExternal: keep package in transform pipeline for all environments
|
|
524
|
+
config.resolve ??= {}
|
|
525
|
+
const existing = config.resolve.noExternal
|
|
526
|
+
if (existing === true) return
|
|
527
|
+
const arr = Array.isArray(existing) ? existing : existing ? [existing] : []
|
|
528
|
+
arr.push(new RegExp(`^${PKG_NAME}`))
|
|
529
|
+
arr.push(/^tweakpane/)
|
|
530
|
+
config.resolve.noExternal = arr
|
|
531
|
+
|
|
532
|
+
// Deduplicate motion packages so egaki's motion-timing virtual module
|
|
533
|
+
// patches the same JSAnimation class that motion/react uses. Without
|
|
534
|
+
// this, Vite can resolve motion-dom to different instances and
|
|
535
|
+
// prototype patches won't affect the user's animations.
|
|
536
|
+
// Always applied (not gated by hasMotion) because configEnvironment
|
|
537
|
+
// runs before configResolved where hasMotion is set. Harmless when
|
|
538
|
+
// motion isn't installed — dedupe on missing packages is a no-op.
|
|
539
|
+
config.resolve.dedupe = mergeUnique(
|
|
540
|
+
config.resolve.dedupe as string[] | undefined,
|
|
541
|
+
['motion', 'motion-dom', 'motion-utils'],
|
|
542
|
+
)
|
|
543
|
+
|
|
544
|
+
if (name === 'client') {
|
|
545
|
+
config.optimizeDeps ??= {}
|
|
546
|
+
config.optimizeDeps.exclude = mergeUnique(
|
|
547
|
+
config.optimizeDeps.exclude,
|
|
548
|
+
[PKG_NAME],
|
|
549
|
+
)
|
|
550
|
+
config.optimizeDeps.include = mergeUnique(
|
|
551
|
+
config.optimizeDeps.include,
|
|
552
|
+
[
|
|
553
|
+
`${PKG_NAME} > spiceflow > @vitejs/plugin-rsc/vendor/react-server-dom/client.browser`,
|
|
554
|
+
`${PKG_NAME} > remotion`,
|
|
555
|
+
`${PKG_NAME} > @remotion/player`,
|
|
556
|
+
`${PKG_NAME} > safe-mdx`,
|
|
557
|
+
],
|
|
558
|
+
)
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
if (name === 'rsc' || name === 'ssr') {
|
|
562
|
+
config.optimizeDeps ??= {}
|
|
563
|
+
config.optimizeDeps.exclude = mergeUnique(
|
|
564
|
+
config.optimizeDeps.exclude,
|
|
565
|
+
['spiceflow'],
|
|
566
|
+
)
|
|
567
|
+
}
|
|
568
|
+
},
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
return [
|
|
572
|
+
videoPlugin,
|
|
573
|
+
rscPackagePlugin,
|
|
574
|
+
tailwindcss(),
|
|
575
|
+
spiceflowPlugin({ entry: VIRTUAL_APP }),
|
|
576
|
+
react(),
|
|
577
|
+
]
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
function mergeUnique(existing: string[] | undefined, items: string[]): string[] {
|
|
581
|
+
const set = new Set(existing ?? [])
|
|
582
|
+
for (const item of items) set.add(item)
|
|
583
|
+
return [...set]
|
|
584
|
+
}
|