@vibecuting/component-project-helper 0.1.8 → 0.1.10

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
@@ -10,7 +10,7 @@ NPM 地址:
10
10
 
11
11
  - 作为可发布的 npm 包承载组件工程辅助逻辑。
12
12
  - 提供组件注解、schema、发现器、Markdown 生成和安装期生命周期逻辑。
13
- - 安装时扫描 `src/components/**/*.tsx`,把组件元数据生成到 `.agents/skills/project-allow-component/components/`。
13
+ - 安装时扫描 `src/components/**/*.tsx`、`src/video/chapters/**/*.tsx` 以及 `node_modules/@vibecuting/*/src/**/*.tsx`,把组件元数据生成到 `.agents/skills/project-allow-component/components/`。
14
14
  - 通过 `postinstall` / `preuninstall` 维护生成文件和清理清单。
15
15
  - 安装时会给宿主工程的 `package.json` 补上 `update-component-skills`,它会调用同一套 `postinstall` 逻辑。
16
16
  - 宿主工程也可以直接运行 `pnpm update-component-skills` 手动刷新同一套生成结果。
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@vibecuting/component-project-helper",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "type": "module",
5
+ "license": "SEE LICENSE IN LICENSE",
5
6
  "main": "./src/index.ts",
6
7
  "types": "./src/index.ts",
7
8
  "exports": {
@@ -97,6 +97,38 @@ async function collectComponentSourceFiles(directory) {
97
97
  return files
98
98
  }
99
99
 
100
+ async function collectNamespacedPackageSourceFiles(projectRoot) {
101
+ const namespaceRoot = path.join(projectRoot, 'node_modules', '@vibecuting')
102
+ let entries = []
103
+
104
+ try {
105
+ entries = await fs.readdir(namespaceRoot, { withFileTypes: true })
106
+ } catch {
107
+ return []
108
+ }
109
+
110
+ const packageRoots = entries
111
+ .filter((entry) => entry.isDirectory() || entry.isSymbolicLink())
112
+ .map((entry) => path.join(namespaceRoot, entry.name, 'src'))
113
+
114
+ const files = await Promise.all(packageRoots.map((directory) => collectComponentSourceFiles(directory)))
115
+ return [...new Set(files.flat())]
116
+ }
117
+
118
+ async function collectDiscoverableSourceFiles(projectRoot) {
119
+ const sourceRoots = [
120
+ path.join(projectRoot, 'src', 'components'),
121
+ path.join(projectRoot, 'src', 'video', 'chapters'),
122
+ ]
123
+
124
+ const localFiles = await Promise.all(
125
+ sourceRoots.map((directory) => collectComponentSourceFiles(directory)),
126
+ )
127
+ const packageFiles = await collectNamespacedPackageSourceFiles(projectRoot)
128
+
129
+ return [...new Set([...localFiles.flat(), ...packageFiles])]
130
+ }
131
+
100
132
  function readMetadataFromSource(source) {
101
133
  const decoratorMatch = /@VideoComponent\s*\((\{[\s\S]*?\})\)/m.exec(source)
102
134
  if (decoratorMatch) {
@@ -135,9 +167,8 @@ function readMetadataFromSource(source) {
135
167
  }
136
168
 
137
169
  export async function discoverComponentProjectComponents(projectRoot) {
138
- const componentsDir = path.join(projectRoot, 'src', 'components')
139
170
  const discovered = []
140
- const sourceFiles = await collectComponentSourceFiles(componentsDir)
171
+ const sourceFiles = await collectDiscoverableSourceFiles(projectRoot)
141
172
 
142
173
  for (const absolutePath of sourceFiles) {
143
174
  const source = await fs.readFile(absolutePath, 'utf8')
@@ -68,3 +68,86 @@ test('discovers decorated components recursively', async () => {
68
68
  },
69
69
  ])
70
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
+ `,
99
+ 'utf8',
100
+ )
101
+
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
+ await fs.writeFile(
114
+ path.join(videoProjectHelperComponentDir, 'DefaultSceneFrame.tsx'),
115
+ `
116
+ @VideoComponent({
117
+ name: 'DefaultSceneFrame',
118
+ description: 'Default helper scene frame',
119
+ sourceFile: 'node_modules/@vibecuting/video-project-helper/src/core/scene/DefaultSceneFrame.tsx',
120
+ aspectRatio: '16:9',
121
+ sceneType: 'scene',
122
+ tags: ['scene', 'helper'],
123
+ propsTypeName: 'DefaultSceneFrameProps',
124
+ })
125
+ export const DefaultSceneFrame = () => null
126
+ `,
127
+ 'utf8',
128
+ )
129
+
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
+ })
@@ -44,6 +44,38 @@ async function collectComponentSourceFiles(directory: string): Promise<string[]>
44
44
  return files
45
45
  }
46
46
 
47
+ async function collectNamespacedPackageSourceFiles(projectRoot: string): Promise<string[]> {
48
+ const namespaceRoot = path.join(projectRoot, 'node_modules', '@vibecuting')
49
+ let entries: Dirent[] = []
50
+
51
+ try {
52
+ entries = await fs.readdir(namespaceRoot, { withFileTypes: true })
53
+ } catch {
54
+ return []
55
+ }
56
+
57
+ const packageRoots = entries
58
+ .filter((entry) => entry.isDirectory() || entry.isSymbolicLink())
59
+ .map((entry) => path.join(namespaceRoot, entry.name, 'src'))
60
+
61
+ const files = await Promise.all(packageRoots.map((directory) => collectComponentSourceFiles(directory)))
62
+ return [...new Set(files.flat())]
63
+ }
64
+
65
+ async function collectDiscoverableSourceFiles(projectRoot: string): Promise<string[]> {
66
+ const sourceRoots = [
67
+ path.join(projectRoot, 'src', 'components'),
68
+ path.join(projectRoot, 'src', 'video', 'chapters'),
69
+ ]
70
+
71
+ const localFiles = await Promise.all(
72
+ sourceRoots.map((directory) => collectComponentSourceFiles(directory)),
73
+ )
74
+ const packageFiles = await collectNamespacedPackageSourceFiles(projectRoot)
75
+
76
+ return [...new Set([...localFiles.flat(), ...packageFiles])]
77
+ }
78
+
47
79
  function extractBalancedObjectLiteral(source: string, startIndex: number): string | undefined {
48
80
  let depth = 0
49
81
  let inString = false
@@ -143,9 +175,8 @@ function readMetadataFromSource(source: string): ComponentProjectComponentMetada
143
175
  export async function discoverComponentProjectComponents(
144
176
  projectRoot: string,
145
177
  ): Promise<ComponentProjectComponentDiscovery[]> {
146
- const componentsDir = path.join(projectRoot, 'src', 'components')
147
178
  const discovered: ComponentProjectComponentDiscovery[] = []
148
- const sourceFiles = await collectComponentSourceFiles(componentsDir)
179
+ const sourceFiles = await collectDiscoverableSourceFiles(projectRoot)
149
180
 
150
181
  for (const absolutePath of sourceFiles) {
151
182
  const source = await fs.readFile(absolutePath, 'utf8')