@vibecuting/component-project-helper 0.1.11 → 0.1.13
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 +1 -1
- package/package.json +1 -1
- package/scripts/package-json.mjs +60 -0
- package/scripts/postinstall.mjs +26 -0
- package/scripts/preuninstall.mjs +3 -0
- package/scripts/runtime-root.mjs +4 -1
- package/src/lifecycle/package-json.ts +17 -1
- package/src/lifecycle/postinstall.ts +23 -0
- package/src/runtime/index.test.ts +26 -0
- package/src/runtime/index.ts +4 -1
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ NPM 地址:
|
|
|
10
10
|
|
|
11
11
|
- 作为可发布的 npm 包承载组件工程辅助逻辑。
|
|
12
12
|
- 提供组件注解、schema、发现器、Markdown 生成和安装期生命周期逻辑。
|
|
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
14
|
- 通过 `postinstall` / `preuninstall` 维护生成文件和清理清单。
|
|
15
15
|
- 安装时会给宿主工程的 `package.json` 补上 `update-component-skills`,它会调用同一套 `postinstall` 逻辑。
|
|
16
16
|
- 宿主工程也可以直接运行 `pnpm update-component-skills` 手动刷新同一套生成结果。
|
package/package.json
CHANGED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import fs from 'node:fs/promises'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
|
|
4
|
+
export const componentProjectSkillsScriptName = 'update-component-skills'
|
|
5
|
+
export const componentProjectSkillsScriptCommand =
|
|
6
|
+
'node ./node_modules/@vibecuting/component-project-helper/scripts/postinstall.mjs'
|
|
7
|
+
|
|
8
|
+
async function readPackageJson(projectRoot) {
|
|
9
|
+
const packageJsonPath = path.join(projectRoot, 'package.json')
|
|
10
|
+
try {
|
|
11
|
+
return JSON.parse(await fs.readFile(packageJsonPath, 'utf8'))
|
|
12
|
+
} catch (error) {
|
|
13
|
+
if (error?.code === 'ENOENT') {
|
|
14
|
+
return undefined
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
throw error
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function writePackageJson(projectRoot, packageJson) {
|
|
22
|
+
const packageJsonPath = path.join(projectRoot, 'package.json')
|
|
23
|
+
await fs.writeFile(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`, 'utf8')
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function ensureComponentProjectSkillsScript(projectRoot) {
|
|
27
|
+
const packageJson = await readPackageJson(projectRoot)
|
|
28
|
+
if (!packageJson) {
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const scripts = packageJson.scripts ?? {}
|
|
33
|
+
|
|
34
|
+
if (scripts[componentProjectSkillsScriptName] !== componentProjectSkillsScriptCommand) {
|
|
35
|
+
packageJson.scripts = {
|
|
36
|
+
...scripts,
|
|
37
|
+
[componentProjectSkillsScriptName]: componentProjectSkillsScriptCommand,
|
|
38
|
+
}
|
|
39
|
+
await writePackageJson(projectRoot, packageJson)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function removeComponentProjectSkillsScript(projectRoot) {
|
|
44
|
+
const packageJson = await readPackageJson(projectRoot)
|
|
45
|
+
if (!packageJson) {
|
|
46
|
+
return
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const scripts = packageJson.scripts ?? {}
|
|
50
|
+
|
|
51
|
+
if (scripts[componentProjectSkillsScriptName] !== componentProjectSkillsScriptCommand) {
|
|
52
|
+
return
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const nextScripts = { ...scripts }
|
|
56
|
+
delete nextScripts[componentProjectSkillsScriptName]
|
|
57
|
+
packageJson.scripts = nextScripts
|
|
58
|
+
|
|
59
|
+
await writePackageJson(projectRoot, packageJson)
|
|
60
|
+
}
|
package/scripts/postinstall.mjs
CHANGED
|
@@ -2,6 +2,7 @@ import fs from 'node:fs/promises'
|
|
|
2
2
|
import path from 'node:path'
|
|
3
3
|
|
|
4
4
|
import { discoverComponentProjectComponents } from './discover-components.mjs'
|
|
5
|
+
import { ensureComponentProjectSkillsScript } from './package-json.mjs'
|
|
5
6
|
import {
|
|
6
7
|
resolveComponentProjectGeneratedDocsDir,
|
|
7
8
|
resolveComponentProjectGeneratedManifestPath,
|
|
@@ -33,6 +34,16 @@ export async function runComponentProjectPostinstall(projectRoot = resolveCompon
|
|
|
33
34
|
const manifestPath = resolveComponentProjectGeneratedManifestPath(projectRoot)
|
|
34
35
|
const components = await discoverComponentProjectComponents(projectRoot)
|
|
35
36
|
|
|
37
|
+
console.log('[component-project-helper] postinstall start')
|
|
38
|
+
console.log(`[component-project-helper] project root: ${projectRoot}`)
|
|
39
|
+
console.log(`[component-project-helper] docs dir: ${docsDir}`)
|
|
40
|
+
console.log(`[component-project-helper] manifest path: ${manifestPath}`)
|
|
41
|
+
console.log(
|
|
42
|
+
`[component-project-helper] discovered ${components.length} component(s): ${
|
|
43
|
+
components.map((component) => component.name).join(', ') || '(none)'
|
|
44
|
+
}`,
|
|
45
|
+
)
|
|
46
|
+
|
|
36
47
|
await ensureDirectory(docsDir)
|
|
37
48
|
|
|
38
49
|
const files = []
|
|
@@ -40,9 +51,21 @@ export async function runComponentProjectPostinstall(projectRoot = resolveCompon
|
|
|
40
51
|
const filePath = path.join(docsDir, `${component.name}.md`)
|
|
41
52
|
await fs.writeFile(filePath, renderComponentProjectMarkdown(component), 'utf8')
|
|
42
53
|
files.push(filePath)
|
|
54
|
+
console.log(
|
|
55
|
+
`[component-project-helper] wrote ${path.relative(projectRoot, filePath)} <- ${component.sourceFile}`,
|
|
56
|
+
)
|
|
43
57
|
}
|
|
44
58
|
|
|
59
|
+
console.log(
|
|
60
|
+
`[component-project-helper] cleaning stale docs relative to manifest ${path.relative(
|
|
61
|
+
projectRoot,
|
|
62
|
+
manifestPath,
|
|
63
|
+
)}`,
|
|
64
|
+
)
|
|
45
65
|
await removeStaleGeneratedFiles(manifestPath, files)
|
|
66
|
+
await ensureComponentProjectSkillsScript(projectRoot)
|
|
67
|
+
console.log('[component-project-helper] ensured update-component-skills script')
|
|
68
|
+
console.log('[component-project-helper] ensured stale docs cleaned')
|
|
46
69
|
|
|
47
70
|
const manifest = ComponentProjectGeneratedManifestSchema.parse({
|
|
48
71
|
projectRoot,
|
|
@@ -51,6 +74,9 @@ export async function runComponentProjectPostinstall(projectRoot = resolveCompon
|
|
|
51
74
|
files,
|
|
52
75
|
})
|
|
53
76
|
await fs.writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, 'utf8')
|
|
77
|
+
console.log(
|
|
78
|
+
`[component-project-helper] postinstall complete, manifest files: ${files.length}`,
|
|
79
|
+
)
|
|
54
80
|
}
|
|
55
81
|
|
|
56
82
|
const isDirectRun = process.argv[1] && path.resolve(process.argv[1]) === path.resolve(new URL(import.meta.url).pathname)
|
package/scripts/preuninstall.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import path from 'node:path'
|
|
|
3
3
|
|
|
4
4
|
import { resolveComponentProjectGeneratedManifestPath } from './runtime-root.mjs'
|
|
5
5
|
import { ComponentProjectGeneratedManifestSchema } from './schemas.mjs'
|
|
6
|
+
import { removeComponentProjectSkillsScript } from './package-json.mjs'
|
|
6
7
|
|
|
7
8
|
export async function runComponentProjectPreuninstall(
|
|
8
9
|
projectRoot = process.env.INIT_CWD ?? process.env.npm_config_local_prefix ?? process.cwd(),
|
|
@@ -25,6 +26,8 @@ export async function runComponentProjectPreuninstall(
|
|
|
25
26
|
throw error
|
|
26
27
|
}
|
|
27
28
|
}
|
|
29
|
+
|
|
30
|
+
await removeComponentProjectSkillsScript(projectRoot)
|
|
28
31
|
}
|
|
29
32
|
|
|
30
33
|
const isDirectRun = process.argv[1] && path.resolve(process.argv[1]) === path.resolve(new URL(import.meta.url).pathname)
|
package/scripts/runtime-root.mjs
CHANGED
|
@@ -3,7 +3,10 @@ import fs from 'node:fs'
|
|
|
3
3
|
import process from 'node:process'
|
|
4
4
|
|
|
5
5
|
function hasComponentProjectSourceDir(directory) {
|
|
6
|
-
return
|
|
6
|
+
return (
|
|
7
|
+
fs.existsSync(path.join(directory, 'src', 'components')) ||
|
|
8
|
+
fs.existsSync(path.join(directory, 'src', 'video', 'chapters'))
|
|
9
|
+
)
|
|
7
10
|
}
|
|
8
11
|
|
|
9
12
|
function findComponentProjectInstallRoot(startDirectory) {
|
|
@@ -7,7 +7,15 @@ export const componentProjectSkillsScriptCommand =
|
|
|
7
7
|
|
|
8
8
|
async function readPackageJson(projectRoot: string): Promise<Record<string, unknown>> {
|
|
9
9
|
const packageJsonPath = path.join(projectRoot, 'package.json')
|
|
10
|
-
|
|
10
|
+
try {
|
|
11
|
+
return JSON.parse(await fs.readFile(packageJsonPath, 'utf8'))
|
|
12
|
+
} catch (error) {
|
|
13
|
+
if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
|
|
14
|
+
return undefined as never
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
throw error
|
|
18
|
+
}
|
|
11
19
|
}
|
|
12
20
|
|
|
13
21
|
async function writePackageJson(
|
|
@@ -20,6 +28,10 @@ async function writePackageJson(
|
|
|
20
28
|
|
|
21
29
|
export async function ensureComponentProjectSkillsScript(projectRoot: string): Promise<void> {
|
|
22
30
|
const packageJson = await readPackageJson(projectRoot)
|
|
31
|
+
if (!packageJson) {
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
|
|
23
35
|
const scripts = (packageJson.scripts as Record<string, string> | undefined) ?? {}
|
|
24
36
|
|
|
25
37
|
if (scripts[componentProjectSkillsScriptName] !== componentProjectSkillsScriptCommand) {
|
|
@@ -33,6 +45,10 @@ export async function ensureComponentProjectSkillsScript(projectRoot: string): P
|
|
|
33
45
|
|
|
34
46
|
export async function removeComponentProjectSkillsScript(projectRoot: string): Promise<void> {
|
|
35
47
|
const packageJson = await readPackageJson(projectRoot)
|
|
48
|
+
if (!packageJson) {
|
|
49
|
+
return
|
|
50
|
+
}
|
|
51
|
+
|
|
36
52
|
const scripts = (packageJson.scripts as Record<string, string> | undefined) ?? {}
|
|
37
53
|
|
|
38
54
|
if (scripts[componentProjectSkillsScriptName] !== componentProjectSkillsScriptCommand) {
|
|
@@ -40,6 +40,16 @@ export async function runComponentProjectPostinstall(
|
|
|
40
40
|
const manifestPath = resolveComponentProjectGeneratedManifestPath(projectRoot)
|
|
41
41
|
const components = await discoverComponentProjectComponents(projectRoot)
|
|
42
42
|
|
|
43
|
+
console.log('[component-project-helper] postinstall start')
|
|
44
|
+
console.log(`[component-project-helper] project root: ${projectRoot}`)
|
|
45
|
+
console.log(`[component-project-helper] docs dir: ${docsDir}`)
|
|
46
|
+
console.log(`[component-project-helper] manifest path: ${manifestPath}`)
|
|
47
|
+
console.log(
|
|
48
|
+
`[component-project-helper] discovered ${components.length} component(s): ${
|
|
49
|
+
components.map((component) => component.name).join(', ') || '(none)'
|
|
50
|
+
}`,
|
|
51
|
+
)
|
|
52
|
+
|
|
43
53
|
await ensureDirectory(docsDir)
|
|
44
54
|
|
|
45
55
|
const files: string[] = []
|
|
@@ -49,10 +59,20 @@ export async function runComponentProjectPostinstall(
|
|
|
49
59
|
const markdown = renderComponentProjectMarkdown(component)
|
|
50
60
|
await fs.writeFile(filePath, markdown, 'utf8')
|
|
51
61
|
files.push(filePath)
|
|
62
|
+
console.log(
|
|
63
|
+
`[component-project-helper] wrote ${path.relative(projectRoot, filePath)} <- ${component.sourceFile}`,
|
|
64
|
+
)
|
|
52
65
|
}
|
|
53
66
|
|
|
67
|
+
console.log(
|
|
68
|
+
`[component-project-helper] cleaning stale docs relative to manifest ${path.relative(
|
|
69
|
+
projectRoot,
|
|
70
|
+
manifestPath,
|
|
71
|
+
)}`,
|
|
72
|
+
)
|
|
54
73
|
await removeStaleGeneratedFiles(manifestPath, files)
|
|
55
74
|
await ensureComponentProjectSkillsScript(projectRoot)
|
|
75
|
+
console.log('[component-project-helper] ensured update-component-skills script')
|
|
56
76
|
|
|
57
77
|
const manifest = ComponentProjectGeneratedManifestSchema.parse({
|
|
58
78
|
projectRoot,
|
|
@@ -62,6 +82,9 @@ export async function runComponentProjectPostinstall(
|
|
|
62
82
|
})
|
|
63
83
|
|
|
64
84
|
await fs.writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, 'utf8')
|
|
85
|
+
console.log(
|
|
86
|
+
`[component-project-helper] postinstall complete, manifest files: ${files.length}`,
|
|
87
|
+
)
|
|
65
88
|
}
|
|
66
89
|
|
|
67
90
|
async function main(): Promise<void> {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import fs from 'node:fs/promises'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import { tmpdir } from 'node:os'
|
|
4
|
+
import { afterEach, expect, test, vi } from 'vitest'
|
|
5
|
+
|
|
6
|
+
import { resolveComponentProjectInstallRoot } from './index'
|
|
7
|
+
|
|
8
|
+
afterEach(() => {
|
|
9
|
+
vi.unstubAllEnvs()
|
|
10
|
+
vi.restoreAllMocks()
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
test('resolves a video project template root before a parent monorepo root', async () => {
|
|
14
|
+
const workspaceRoot = await fs.mkdtemp(path.join(tmpdir(), 'component-project-helper-runtime-'))
|
|
15
|
+
const monorepoRoot = path.join(workspaceRoot, 'repo')
|
|
16
|
+
const videoProjectRoot = path.join(monorepoRoot, 'project-templates', 'video-project')
|
|
17
|
+
|
|
18
|
+
await fs.mkdir(path.join(monorepoRoot, 'src', 'components'), { recursive: true })
|
|
19
|
+
await fs.mkdir(path.join(videoProjectRoot, 'src', 'video', 'chapters'), { recursive: true })
|
|
20
|
+
|
|
21
|
+
vi.stubEnv('INIT_CWD', path.join(videoProjectRoot, 'src', 'video'))
|
|
22
|
+
vi.stubEnv('npm_config_local_prefix', '')
|
|
23
|
+
vi.spyOn(process, 'cwd').mockReturnValue(path.join(videoProjectRoot, 'src', 'video'))
|
|
24
|
+
|
|
25
|
+
expect(resolveComponentProjectInstallRoot()).toBe(videoProjectRoot)
|
|
26
|
+
})
|
package/src/runtime/index.ts
CHANGED
|
@@ -3,7 +3,10 @@ import fs from 'node:fs'
|
|
|
3
3
|
import process from 'node:process'
|
|
4
4
|
|
|
5
5
|
function hasComponentProjectSourceDir(directory: string): boolean {
|
|
6
|
-
return
|
|
6
|
+
return (
|
|
7
|
+
fs.existsSync(path.join(directory, 'src', 'components')) ||
|
|
8
|
+
fs.existsSync(path.join(directory, 'src', 'video', 'chapters'))
|
|
9
|
+
)
|
|
7
10
|
}
|
|
8
11
|
|
|
9
12
|
function findComponentProjectInstallRoot(startDirectory: string): string | undefined {
|