@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 +4 -6
- package/bin/component-project-helper.mjs +3 -3
- package/package.json +9 -6
- package/scripts/package-json.mjs +1 -1
- package/scripts/update-component-meta.mjs +60 -0
- package/scripts/update-component-skills.mjs +63 -0
- package/src/decorators/index.ts +95 -17
- package/src/discovery/index.test.ts +86 -159
- package/src/discovery/index.ts +664 -102
- package/src/index.ts +52 -10
- package/src/meta/generate-video-resource-meta.ts +198 -0
- package/src/meta/update-component-meta.test.ts +64 -0
- package/src/meta/update-component-meta.ts +47 -0
- package/src/meta/update-component-skills.test.ts +66 -0
- package/src/meta/update-component-skills.ts +306 -0
- package/src/resources/resource-descriptors.ts +47 -0
- package/src/resources/video-resource-descriptors.json +17 -0
- package/src/resources/video-resource.ts +143 -0
- package/src/schemas/index.ts +24 -2
- package/scripts/postinstall.mjs +0 -112
- package/scripts/preuninstall.mjs +0 -45
- package/scripts/render-markdown.mjs +0 -36
- package/src/lifecycle/package-json.ts +0 -63
- package/src/lifecycle/postinstall.test.ts +0 -114
- package/src/lifecycle/postinstall.ts +0 -127
- package/src/lifecycle/preuninstall.ts +0 -54
- package/src/markdown/index.test.ts +0 -20
- package/src/markdown/index.ts +0 -43
package/README.md
CHANGED
|
@@ -9,11 +9,10 @@ NPM 地址:
|
|
|
9
9
|
职责:
|
|
10
10
|
|
|
11
11
|
- 作为可发布的 npm 包承载组件工程辅助逻辑。
|
|
12
|
-
- 提供组件注解、schema
|
|
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
|
-
-
|
|
15
|
-
-
|
|
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/
|
|
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
|
|
9
|
-
const scriptPath = path.resolve(binDir, '../scripts',
|
|
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.
|
|
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
|
-
"
|
|
24
|
-
"
|
|
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": "
|
|
27
|
-
"test": "
|
|
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
|
-
"
|
|
36
|
+
"@testing-library/jest-dom": "^6.8.0",
|
|
37
|
+
"@types/node": "20.12.2",
|
|
38
|
+
"vitest": "^4.1.0"
|
|
36
39
|
}
|
|
37
40
|
}
|
package/scripts/package-json.mjs
CHANGED
|
@@ -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/
|
|
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
|
+
)
|
package/src/decorators/index.ts
CHANGED
|
@@ -1,38 +1,116 @@
|
|
|
1
|
-
import
|
|
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 =
|
|
9
|
-
|
|
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
|
-
|
|
19
|
-
|
|
37
|
+
const sceneAnnotation = createVideoResourceAnnotation(sceneResourceDescriptor)
|
|
38
|
+
const transitionAnnotation = createVideoResourceAnnotation(transitionResourceDescriptor)
|
|
39
|
+
const themeAnnotation = createVideoResourceAnnotation(themeResourceDescriptor)
|
|
20
40
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
80
|
+
const metadata = getVideoResourceMetadata<ScenePluginMetadata>(target, 'scene')
|
|
81
|
+
if (!metadata) {
|
|
31
82
|
return undefined
|
|
32
83
|
}
|
|
33
84
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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 {
|
|
7
|
+
import { discoverVideoResources } from './index'
|
|
8
8
|
|
|
9
|
-
test('discovers
|
|
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
|
|
12
|
-
|
|
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(
|
|
16
|
-
|
|
17
|
-
@
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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(
|
|
27
|
+
path.join(srcRoot, 'index.ts'),
|
|
115
28
|
`
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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
|
-
|
|
123
|
-
|
|
38
|
+
sceneFamily: 'custom',
|
|
39
|
+
rootLayout: 'absolute-fill',
|
|
124
40
|
})
|
|
125
|
-
export const DefaultSceneFrame = () => null
|
|
126
|
-
`,
|
|
127
|
-
'utf8',
|
|
128
|
-
)
|
|
129
41
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
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
|
-
|
|
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
|
|
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
|
})
|