@vibecuting/component-project-helper 0.1.16 → 0.1.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,11 +9,10 @@ NPM 地址:
9
9
  职责:
10
10
 
11
11
  - 作为可发布的 npm 包承载组件工程辅助逻辑。
12
- - 提供组件注解、schema、发现器、Markdown 生成和安装期生命周期逻辑。
12
+ - 提供组件注解、schema、发现器、元数据生成、技能说明生成和安装期生命周期逻辑。
13
13
  - 安装时会优先把当前工程识别为包含 `src/components` 或 `src/video/chapters` 的独立项目,再扫描 `src/components/**/*.tsx`、`src/video/chapters/**/*.tsx` 以及 `node_modules/@vibecuting/*/src/**/*.tsx`,把组件元数据生成到 `.agents/skills/project-allow-component/components/`。
14
- - 通过 `postinstall` / `preuninstall` 维护生成文件和清理清单。
15
- - 安装时会给宿主工程的 `package.json` 补上 `update-component-skills`,它会调用同一套 `postinstall` 逻辑。
16
- - 宿主工程也可以直接运行 `pnpm update-component-skills` 手动刷新同一套生成结果。
14
+ - 宿主工程的 `update-component-skills` 会基于 `.vibecuting/video-resource-meta.generated.ts` 生成技能说明,不再执行模板源码文件本身。
15
+ - 宿主工程也可以直接运行 `pnpm run update-component-skills` 手动刷新同一套生成结果。
17
16
  - 作为主工程之外的独立 helper 包,避免把辅助逻辑重新塞回主站代码里。
18
17
 
19
18
  目录结构:
@@ -21,7 +20,7 @@ NPM 地址:
21
20
  - `src/decorators`:注解定义和元数据读写
22
21
  - `src/schemas`:zod schema
23
22
  - `src/discovery`:组件发现与解析
24
- - `src/markdown`:Markdown 渲染
23
+ - `src/meta`:元数据和技能说明生成
25
24
  - `src/runtime`:安装根与输出路径解析
26
25
  - `src/lifecycle`:postinstall / preuninstall 生命周期逻辑
27
26
 
@@ -29,7 +28,6 @@ NPM 地址:
29
28
 
30
29
  - `pnpm typecheck`
31
30
  - `pnpm test`
32
- - `node ./bin/component-project-helper.mjs postinstall`
33
31
  - `pnpm update-component-skills`
34
32
 
35
33
  发布与安装:
@@ -5,10 +5,10 @@ import path from 'node:path'
5
5
  import { fileURLToPath } from 'node:url'
6
6
 
7
7
  const binDir = path.dirname(fileURLToPath(import.meta.url))
8
- const scriptName = process.argv[2] === 'preuninstall' ? 'preuninstall.mjs' : 'postinstall.mjs'
9
- const scriptPath = path.resolve(binDir, '../scripts', scriptName)
8
+ const checkOnly = process.argv.includes('--check')
9
+ const scriptPath = path.resolve(binDir, '../scripts', 'update-component-meta.mjs')
10
10
 
11
- const result = spawnSync(process.execPath, [scriptPath], {
11
+ const result = spawnSync(process.execPath, checkOnly ? [scriptPath, '--check'] : [scriptPath], {
12
12
  stdio: 'inherit',
13
13
  env: process.env,
14
14
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibecuting/component-project-helper",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
4
4
  "type": "module",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "main": "./src/index.ts",
@@ -20,18 +20,21 @@
20
20
  "access": "public"
21
21
  },
22
22
  "scripts": {
23
- "postinstall": "node ./scripts/postinstall.mjs",
24
- "preuninstall": "node ./scripts/preuninstall.mjs",
23
+ "update-component-meta": "node ./scripts/update-component-meta.mjs",
24
+ "update-component-skills": "node ./scripts/update-component-skills.mjs",
25
25
  "lint": "node ../../../scripts/run-eslint.mjs .",
26
- "typecheck": "node ../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/bin/tsc -p tsconfig.json --noEmit",
27
- "test": "node ../../../node_modules/vitest/vitest.mjs run"
26
+ "typecheck": "pnpm exec tsc -p tsconfig.json --noEmit",
27
+ "test": "pnpm exec vitest run"
28
28
  },
29
29
  "dependencies": {
30
30
  "reflect-metadata": "^0.2.2",
31
31
  "typescript": "^5.9.3",
32
+ "tsx": "^4.19.2",
32
33
  "zod": "4.1.12"
33
34
  },
34
35
  "devDependencies": {
35
- "vitest": "^3.2.4"
36
+ "@testing-library/jest-dom": "^6.8.0",
37
+ "@types/node": "20.12.2",
38
+ "vitest": "^4.1.0"
36
39
  }
37
40
  }
@@ -3,7 +3,7 @@ import path from 'node:path'
3
3
 
4
4
  export const componentProjectSkillsScriptName = 'update-component-skills'
5
5
  export const componentProjectSkillsScriptCommand =
6
- 'node ./node_modules/@vibecuting/component-project-helper/scripts/postinstall.mjs'
6
+ 'node ./node_modules/@vibecuting/component-project-helper/scripts/update-component-skills.mjs'
7
7
 
8
8
  async function readPackageJson(projectRoot) {
9
9
  const packageJsonPath = path.join(projectRoot, 'package.json')
@@ -0,0 +1,60 @@
1
+ import { existsSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { spawnSync } from "node:child_process";
4
+ import { createRequire } from "node:module";
5
+ import { fileURLToPath, pathToFileURL } from "node:url";
6
+
7
+ const require = createRequire(import.meta.url);
8
+
9
+ function resolveLoaderPath() {
10
+ const candidates = [
11
+ path.resolve(process.cwd(), "node_modules/tsx/dist/loader.mjs"),
12
+ path.resolve(
13
+ process.cwd(),
14
+ "../../../project-templates/video-project/node_modules/tsx/dist/loader.mjs",
15
+ ),
16
+ ];
17
+
18
+ for (const candidate of candidates) {
19
+ if (existsSync(candidate)) {
20
+ return candidate;
21
+ }
22
+ }
23
+
24
+ return require.resolve("tsx");
25
+ }
26
+
27
+ const scriptPath = path.relative(process.cwd(), fileURLToPath(import.meta.url));
28
+
29
+ if (!process.env.COMPONENT_PROJECT_HELPER_UPDATE_COMPONENT_META_LOADER) {
30
+ const loaderPath = resolveLoaderPath();
31
+ const result = spawnSync(
32
+ process.execPath,
33
+ ["--import", loaderPath, scriptPath, ...process.argv.slice(2)],
34
+ {
35
+ cwd: process.cwd(),
36
+ env: {
37
+ ...process.env,
38
+ COMPONENT_PROJECT_HELPER_UPDATE_COMPONENT_META_LOADER: "1",
39
+ },
40
+ stdio: "inherit",
41
+ },
42
+ );
43
+
44
+ if (result.error) {
45
+ throw result.error;
46
+ }
47
+
48
+ process.exit(result.status ?? 1);
49
+ }
50
+
51
+ const helperModule = await import(
52
+ pathToFileURL(
53
+ path.resolve(process.cwd(), "src/meta/update-component-meta.ts"),
54
+ ).href
55
+ );
56
+
57
+ await helperModule.updateComponentMeta(
58
+ process.cwd(),
59
+ process.argv.includes("--check"),
60
+ );
@@ -0,0 +1,63 @@
1
+ import { existsSync } from 'node:fs'
2
+ import path from 'node:path'
3
+ import { spawnSync } from 'node:child_process'
4
+ import { createRequire } from 'node:module'
5
+ import { fileURLToPath, pathToFileURL } from 'node:url'
6
+
7
+ const require = createRequire(import.meta.url)
8
+
9
+ function resolveLoaderPath() {
10
+ const candidates = [
11
+ path.resolve(process.cwd(), 'node_modules/tsx/dist/loader.mjs'),
12
+ path.resolve(process.cwd(), '../../../project-templates/video-project/node_modules/tsx/dist/loader.mjs'),
13
+ path.resolve(process.cwd(), '../../../project-templates/component-project/node_modules/tsx/dist/loader.mjs'),
14
+ ]
15
+
16
+ for (const candidate of candidates) {
17
+ if (existsSync(candidate)) {
18
+ return candidate
19
+ }
20
+ }
21
+
22
+ return require.resolve('tsx')
23
+ }
24
+
25
+ const scriptPath = path.relative(process.cwd(), fileURLToPath(import.meta.url))
26
+ const helperScriptPath = path.resolve(
27
+ path.dirname(fileURLToPath(import.meta.url)),
28
+ '..',
29
+ 'src',
30
+ 'meta',
31
+ 'update-component-skills.ts',
32
+ )
33
+
34
+ if (!process.env.COMPONENT_PROJECT_HELPER_UPDATE_COMPONENT_SKILLS_LOADER) {
35
+ const loaderPath = resolveLoaderPath()
36
+ const result = spawnSync(
37
+ process.execPath,
38
+ ['--import', loaderPath, scriptPath, ...process.argv.slice(2)],
39
+ {
40
+ cwd: process.cwd(),
41
+ env: {
42
+ ...process.env,
43
+ COMPONENT_PROJECT_HELPER_UPDATE_COMPONENT_SKILLS_LOADER: '1',
44
+ },
45
+ stdio: 'inherit',
46
+ },
47
+ )
48
+
49
+ if (result.error) {
50
+ throw result.error
51
+ }
52
+
53
+ process.exit(result.status ?? 1)
54
+ }
55
+
56
+ const helperModule = await import(
57
+ pathToFileURL(helperScriptPath).href
58
+ )
59
+
60
+ await helperModule.updateComponentSkills(
61
+ process.cwd(),
62
+ process.argv.includes('--check'),
63
+ )
@@ -1,38 +1,116 @@
1
- import 'reflect-metadata'
2
-
1
+ import type {
2
+ ScenePluginMetadata,
3
+ ThemePluginMetadata,
4
+ TransitionPluginMetadata,
5
+ VideoResourceMetadata,
6
+ } from '../resources/video-resource'
7
+ import {
8
+ VIDEO_RESOURCE_METADATA_KEY,
9
+ createVideoResourceAnnotation,
10
+ defineScenePluginMetadata as defineScenePluginMetadataBase,
11
+ defineThemePluginMetadata as defineThemePluginMetadataBase,
12
+ defineTransitionPluginMetadata as defineTransitionPluginMetadataBase,
13
+ getVideoResourceMetadata,
14
+ ScenePluginMetadataSchema,
15
+ type VideoResourceDescriptor,
16
+ } from '../resources/video-resource'
17
+ import {
18
+ sceneResourceDescriptor,
19
+ themeResourceDescriptor,
20
+ transitionResourceDescriptor,
21
+ } from '../resources/resource-descriptors'
3
22
  import {
4
23
  ComponentProjectComponentMetadataSchema,
5
- type ComponentProjectComponentMetadata,
24
+ type ComponentProjectComponentMetadata as LegacyComponentProjectComponentMetadata,
6
25
  } from '../schemas'
7
26
 
8
- export const COMPONENT_PROJECT_METADATA_KEY = Symbol.for(
9
- '@vibecuting/component-project-helper/component-metadata',
10
- )
27
+ export const COMPONENT_PROJECT_METADATA_KEY = VIDEO_RESOURCE_METADATA_KEY
28
+
29
+ export type ComponentProjectComponentMetadata = LegacyComponentProjectComponentMetadata
11
30
 
12
31
  export function defineComponentProjectComponentMetadata(
13
32
  metadata: ComponentProjectComponentMetadata,
14
33
  ): ComponentProjectComponentMetadata {
15
- return ComponentProjectComponentMetadataSchema.parse(metadata)
34
+ return ComponentProjectComponentMetadataSchema.parse(metadata) as ComponentProjectComponentMetadata
16
35
  }
17
36
 
18
- export function VideoComponent(metadata: ComponentProjectComponentMetadata) {
19
- const normalized = defineComponentProjectComponentMetadata(metadata)
37
+ const sceneAnnotation = createVideoResourceAnnotation(sceneResourceDescriptor)
38
+ const transitionAnnotation = createVideoResourceAnnotation(transitionResourceDescriptor)
39
+ const themeAnnotation = createVideoResourceAnnotation(themeResourceDescriptor)
20
40
 
21
- return function componentProjectDecorator<T extends (...args: any[]) => unknown>(target: T): T {
22
- Reflect.defineMetadata(COMPONENT_PROJECT_METADATA_KEY, normalized, target)
23
- return target
41
+ function normalizeSceneMetadata(
42
+ metadata: ComponentProjectComponentMetadata | ScenePluginMetadata,
43
+ ): ScenePluginMetadata {
44
+ if ('resourceKind' in metadata) {
45
+ return ScenePluginMetadataSchema.parse(metadata)
24
46
  }
47
+
48
+ return ScenePluginMetadataSchema.parse({
49
+ resourceKind: 'scene',
50
+ name: metadata.name,
51
+ description: metadata.description,
52
+ sourceFile: metadata.sourceFile,
53
+ aspectRatio: metadata.aspectRatio,
54
+ sceneType: metadata.sceneType,
55
+ sceneFamily: 'custom',
56
+ themePreset: undefined,
57
+ slots: undefined,
58
+ propsTypeName: metadata.propsTypeName,
59
+ rootLayout: 'absolute-fill',
60
+ tags: metadata.tags,
61
+ pluginKey: metadata.name,
62
+ })
25
63
  }
26
64
 
65
+ export function VideoComponent(
66
+ metadata: ComponentProjectComponentMetadata | ScenePluginMetadata,
67
+ ) {
68
+ return sceneAnnotation.annotate(normalizeSceneMetadata(metadata))
69
+ }
70
+ export const TransitionComponent = transitionAnnotation.annotate
71
+ export const ThemeComponent = themeAnnotation.annotate
72
+
73
+ export const defineScenePluginMetadata = defineScenePluginMetadataBase
74
+ export const defineTransitionPluginMetadata = defineTransitionPluginMetadataBase
75
+ export const defineThemePluginMetadata = defineThemePluginMetadataBase
76
+
27
77
  export function getComponentProjectComponentMetadata(
28
78
  target: unknown,
29
79
  ): ComponentProjectComponentMetadata | undefined {
30
- if (!target || (typeof target !== 'object' && typeof target !== 'function')) {
80
+ const metadata = getVideoResourceMetadata<ScenePluginMetadata>(target, 'scene')
81
+ if (!metadata) {
31
82
  return undefined
32
83
  }
33
84
 
34
- return Reflect.getMetadata(
35
- COMPONENT_PROJECT_METADATA_KEY,
36
- target,
37
- ) as ComponentProjectComponentMetadata | undefined
85
+ const { resourceKind, pluginKey, rootLayout, sceneFamily, themePreset, slots, ...legacy } =
86
+ metadata
87
+ void resourceKind
88
+ void pluginKey
89
+ void rootLayout
90
+ void sceneFamily
91
+ void themePreset
92
+ void slots
93
+ return legacy as ComponentProjectComponentMetadata
94
+ }
95
+
96
+ export function getScenePluginMetadata(target: unknown): ScenePluginMetadata | undefined {
97
+ return getVideoResourceMetadata<ScenePluginMetadata>(target, 'scene')
98
+ }
99
+
100
+ export function getTransitionPluginMetadata(
101
+ target: unknown,
102
+ ): TransitionPluginMetadata | undefined {
103
+ return getVideoResourceMetadata<TransitionPluginMetadata>(target, 'transition')
104
+ }
105
+
106
+ export function getThemePluginMetadata(target: unknown): ThemePluginMetadata | undefined {
107
+ return getVideoResourceMetadata<ThemePluginMetadata>(target, 'theme')
108
+ }
109
+
110
+ export type {
111
+ ScenePluginMetadata,
112
+ ThemePluginMetadata,
113
+ TransitionPluginMetadata,
114
+ VideoResourceMetadata,
115
+ VideoResourceDescriptor,
38
116
  }
@@ -4,183 +4,110 @@ import { tmpdir } from 'node:os'
4
4
 
5
5
  import { expect, test } from 'vitest'
6
6
 
7
- import { discoverComponentProjectComponents } from './index'
7
+ import { discoverVideoResources } from './index'
8
8
 
9
- test('discovers decorated components recursively', async () => {
9
+ test('discovers scene, transition and theme resources from installed packages', async () => {
10
10
  const projectRoot = await fs.mkdtemp(path.join(tmpdir(), 'component-project-helper-discovery-'))
11
- const componentsDir = path.join(projectRoot, 'src', 'components', 'nested')
12
- await fs.mkdir(componentsDir, { recursive: true })
11
+ const packageRoot = path.join(projectRoot, 'node_modules', '@vibecuting', 'demo-pack')
12
+ const srcRoot = path.join(packageRoot, 'src')
13
13
 
14
+ await fs.mkdir(srcRoot, { recursive: true })
14
15
  await fs.writeFile(
15
- path.join(projectRoot, 'src', 'components', 'SceneFrame.tsx'),
16
- `
17
- @VideoComponent({
18
- name: 'SceneFrame',
19
- description: 'Wraps the scene content in a consistent frame',
20
- sourceFile: 'src/components/SceneFrame.tsx',
21
- aspectRatio: '16:9',
22
- sceneType: 'scene',
23
- tags: ['layout', 'frame'],
24
- propsTypeName: 'SceneFrameProps',
25
- })
26
- export const SceneFrame = () => null
27
- `,
28
- 'utf8',
29
- )
30
-
31
- await fs.writeFile(
32
- path.join(componentsDir, 'SceneTitle.tsx'),
33
- `
34
- @VideoComponent({
35
- name: 'SceneTitle',
36
- description: 'Renders the title block for scene hero content',
37
- sourceFile: 'src/components/nested/SceneTitle.tsx',
38
- aspectRatio: '9:16',
39
- sceneType: 'intro',
40
- tags: ['title', 'hero'],
41
- propsTypeName: 'SceneTitleProps',
42
- })
43
- export const SceneTitle = () => null
44
- `,
45
- 'utf8',
46
- )
47
-
48
- const discovered = await discoverComponentProjectComponents(projectRoot)
49
-
50
- expect(discovered).toEqual([
51
- {
52
- name: 'SceneFrame',
53
- description: 'Wraps the scene content in a consistent frame',
54
- sourceFile: 'src/components/SceneFrame.tsx',
55
- aspectRatio: '16:9',
56
- sceneType: 'scene',
57
- tags: ['layout', 'frame'],
58
- propsTypeName: 'SceneFrameProps',
59
- },
60
- {
61
- name: 'SceneTitle',
62
- description: 'Renders the title block for scene hero content',
63
- sourceFile: 'src/components/nested/SceneTitle.tsx',
64
- aspectRatio: '9:16',
65
- sceneType: 'intro',
66
- tags: ['title', 'hero'],
67
- propsTypeName: 'SceneTitleProps',
68
- },
69
- ])
70
- })
71
-
72
- test('discovers decorated components from installed video-project-core', async () => {
73
- const projectRoot = await fs.mkdtemp(path.join(tmpdir(), 'component-project-helper-package-'))
74
- const videoProjectCoreComponentDir = path.join(
75
- projectRoot,
76
- 'node_modules',
77
- '@vibecuting',
78
- 'video-project-core',
79
- 'src',
80
- 'core',
81
- 'intro',
82
- )
83
- await fs.mkdir(videoProjectCoreComponentDir, { recursive: true })
84
-
85
- await fs.writeFile(
86
- path.join(videoProjectCoreComponentDir, 'DefaultIntroBumper.tsx'),
87
- `
88
- @VideoComponent({
89
- name: 'DefaultIntroBumper',
90
- description: 'Default remotion intro bumper',
91
- sourceFile: 'node_modules/@vibecuting/video-project-core/src/core/intro/DefaultIntroBumper.tsx',
92
- aspectRatio: '16:9',
93
- sceneType: 'intro',
94
- tags: ['intro', 'core'],
95
- propsTypeName: 'DefaultIntroBumperProps',
96
- })
97
- export const DefaultIntroBumper = () => null
98
- `,
16
+ path.join(packageRoot, 'package.json'),
17
+ JSON.stringify({
18
+ name: '@vibecuting/demo-pack',
19
+ version: '0.1.0',
20
+ main: './src/index.ts',
21
+ types: './src/index.ts',
22
+ }),
99
23
  'utf8',
100
24
  )
101
25
 
102
- const videoProjectHelperComponentDir = path.join(
103
- projectRoot,
104
- 'node_modules',
105
- '@vibecuting',
106
- 'video-project-helper',
107
- 'src',
108
- 'core',
109
- 'scene',
110
- )
111
- await fs.mkdir(videoProjectHelperComponentDir, { recursive: true })
112
-
113
26
  await fs.writeFile(
114
- path.join(videoProjectHelperComponentDir, 'DefaultSceneFrame.tsx'),
27
+ path.join(srcRoot, 'index.ts'),
115
28
  `
116
- @VideoComponent({
117
- name: 'DefaultSceneFrame',
118
- description: 'Default helper scene frame',
119
- sourceFile: 'node_modules/@vibecuting/video-project-helper/src/core/scene/DefaultSceneFrame.tsx',
29
+ export const sceneMetadata = defineScenePluginMetadata({
30
+ resourceKind: 'scene',
31
+ name: 'DemoScene',
32
+ description: 'Demo scene resource',
33
+ sourceFile: 'src/index.ts',
34
+ pluginKey: 'demo.scene',
35
+ tags: ['demo'],
120
36
  aspectRatio: '16:9',
121
37
  sceneType: 'scene',
122
- tags: ['scene', 'helper'],
123
- propsTypeName: 'DefaultSceneFrameProps',
38
+ sceneFamily: 'custom',
39
+ rootLayout: 'absolute-fill',
124
40
  })
125
- export const DefaultSceneFrame = () => null
126
- `,
127
- 'utf8',
128
- )
129
41
 
130
- const discovered = await discoverComponentProjectComponents(projectRoot)
131
-
132
- expect(discovered).toEqual([
133
- {
134
- name: 'DefaultIntroBumper',
135
- description: 'Default remotion intro bumper',
136
- sourceFile: 'node_modules/@vibecuting/video-project-core/src/core/intro/DefaultIntroBumper.tsx',
137
- aspectRatio: '16:9',
138
- sceneType: 'intro',
139
- tags: ['intro', 'core'],
140
- propsTypeName: 'DefaultIntroBumperProps',
141
- },
142
- {
143
- name: 'DefaultSceneFrame',
144
- description: 'Default helper scene frame',
145
- sourceFile:
146
- 'node_modules/@vibecuting/video-project-helper/src/core/scene/DefaultSceneFrame.tsx',
147
- aspectRatio: '16:9',
148
- sceneType: 'scene',
149
- tags: ['scene', 'helper'],
150
- propsTypeName: 'DefaultSceneFrameProps',
151
- },
152
- ])
153
- })
154
-
155
- test('ignores test files from installed packages', async () => {
156
- const projectRoot = await fs.mkdtemp(path.join(tmpdir(), 'component-project-helper-tests-'))
157
- const packageTestDir = path.join(
158
- projectRoot,
159
- 'node_modules',
160
- '@vibecuting',
161
- 'video-project-helper',
162
- 'src',
163
- )
164
- await fs.mkdir(packageTestDir, { recursive: true })
42
+ export const transitionMetadata = defineTransitionPluginMetadata({
43
+ resourceKind: 'transition',
44
+ name: 'DemoTransition',
45
+ description: 'Demo transition resource',
46
+ sourceFile: 'src/index.ts',
47
+ pluginKey: 'demo.transition',
48
+ tags: ['demo'],
49
+ transitionKind: 'transition',
50
+ })
165
51
 
166
- await fs.writeFile(
167
- path.join(packageTestDir, 'index.test.ts'),
168
- `
169
- @VideoComponent({
170
- name: 'ShouldBeIgnored',
171
- description: 'This fixture lives in a test file and must not be discovered',
172
- sourceFile: 'node_modules/@vibecuting/video-project-helper/src/index.test.ts',
173
- aspectRatio: '16:9',
174
- sceneType: 'scene',
175
- tags: ['test'],
176
- propsTypeName: 'ShouldBeIgnoredProps',
52
+ export const themeMetadata = defineThemePluginMetadata({
53
+ resourceKind: 'theme',
54
+ name: 'DemoTheme',
55
+ description: 'Demo theme resource',
56
+ sourceFile: 'src/index.ts',
57
+ pluginKey: 'demo.theme',
58
+ tags: ['demo'],
59
+ supportedSceneFamilies: ['custom'],
60
+ cssVariables: ['--scene-bg'],
177
61
  })
178
- export const ShouldBeIgnored = () => null
62
+
63
+ export const DemoScene = VideoComponent(sceneMetadata)(
64
+ defineSceneComponent({
65
+ family: 'custom',
66
+ propsSchema: null,
67
+ component: function DemoScene() {
68
+ return null
69
+ },
70
+ }),
71
+ )
72
+
73
+ export const DemoTransition = TransitionComponent(transitionMetadata)(
74
+ defineTransitionPlugin({
75
+ key: 'demo.transition',
76
+ kind: 'transition',
77
+ metadata: transitionMetadata,
78
+ propsSchema: null,
79
+ renderBoundary: () => null,
80
+ getBoundaryDurationInFrames: () => 1,
81
+ getTimelineOverlapInFrames: () => 1,
82
+ }),
83
+ )
84
+
85
+ export const DemoTheme = ThemeComponent(themeMetadata)(
86
+ defineSceneTheme({
87
+ key: 'demo.theme',
88
+ name: 'Demo theme',
89
+ variables: {
90
+ '--scene-bg': '#000000',
91
+ },
92
+ }),
93
+ )
179
94
  `,
180
95
  'utf8',
181
96
  )
182
97
 
183
- const discovered = await discoverComponentProjectComponents(projectRoot)
98
+ const discovered = await discoverVideoResources(projectRoot)
184
99
 
185
- expect(discovered).toEqual([])
100
+ expect(discovered.map((resource) => resource.exportName)).toEqual([
101
+ 'DemoScene',
102
+ 'DemoTheme',
103
+ 'DemoTransition',
104
+ ])
105
+ expect(discovered.map((resource) => resource.metadata.resourceKind)).toEqual([
106
+ 'scene',
107
+ 'theme',
108
+ 'transition',
109
+ ])
110
+ expect(discovered[0]?.metadata.pluginKey).toBe('demo.scene')
111
+ expect(discovered[1]?.metadata.pluginKey).toBe('demo.theme')
112
+ expect(discovered[2]?.metadata.pluginKey).toBe('demo.transition')
186
113
  })