@manohub/app-kit 0.2.1 → 0.2.3

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.
@@ -1,26 +1,38 @@
1
- #!/bin/sh
2
- # app-kit 护栏 pre-commit 钩子样本:只检查**已改动文件**,目标 < 3 秒(否则人会在本机把它关掉)。
3
- #
4
- # 启用(不需要改 git 配置,默认 hooks 目录即可):
5
- # cp node_modules/@manohub/app-kit/lint/pre-commit.sample .git/hooks/pre-commit
6
- # chmod +x .git/hooks/pre-commit
7
- #
8
- # 应用包不是仓库根时(monorepo,如 apps/sub-app),用环境变量指出包目录:
9
- # APPKIT_APP_DIR=apps/sub-app (可在 .git/hooks/pre-commit 里写死,或提交前 export)
10
- #
11
- # 说明:护栏以「应用包根」为 cwd 读取 appkit-guardrails.config.json;仓库根不存在该配置时
12
- # 必须给 APPKIT_APP_DIR,否则会以「未找到配置」失败(这是刻意为之:宁可报错,也不要静默跳过检查)。
13
- set -e
14
-
15
- AUDIT="node_modules/@manohub/app-kit/lint/run-all.mjs"
16
-
17
- if [ ! -f "$AUDIT" ]; then
18
- echo "[guardrails] 未安装 @manohub/app-kit(找不到 $AUDIT),跳过检查"
19
- exit 0
20
- fi
21
-
22
- if [ -n "$APPKIT_APP_DIR" ]; then
23
- exec node "$AUDIT" --changed --cwd="$APPKIT_APP_DIR"
24
- fi
25
-
26
- exec node "$AUDIT" --changed
1
+ #!/bin/sh
2
+ # app-kit 护栏 pre-commit 钩子样本:只检查**已改动文件**(含新增但未提交的文件),目标 < 3
3
+ # (否则人会在本机把它关掉)。
4
+ #
5
+ # 启用(不需要改 git 配置,默认 hooks 目录即可):
6
+ # cp node_modules/@manohub/app-kit/lint/pre-commit.sample .git/hooks/pre-commit
7
+ # chmod +x .git/hooks/pre-commit
8
+ #
9
+ # 应用包不是仓库根时(monorepo,如 apps/sub-app):**必须**用环境变量指出包目录,
10
+ # 因为钩子的 cwd 恒为仓库根,而本包通常只装在应用包自己的 node_modules 下:
11
+ # APPKIT_APP_DIR=apps/sub-app (推荐在钩子文件里写死一行,或提交前 export)
12
+ #
13
+ # 说明:护栏以「应用包根」为 cwd 读取 appkit-guardrails.config.json;找不到脚本或配置时
14
+ # 会明确告知并**跳过**本次检查(不阻塞提交)—— 静默通过的假绿比报错更危险。
15
+ set -e
16
+
17
+ # 应用包目录(缺省仓库根)
18
+ APP_DIR="${APPKIT_APP_DIR:-.}"
19
+
20
+ # 护栏脚本按「应用包自己的 node_modules → 仓库根 node_modules」依次找
21
+ if [ -f "$APP_DIR/node_modules/@manohub/app-kit/lint/run-all.mjs" ]; then
22
+ AUDIT="$APP_DIR/node_modules/@manohub/app-kit/lint/run-all.mjs"
23
+ elif [ -f "node_modules/@manohub/app-kit/lint/run-all.mjs" ]; then
24
+ AUDIT="node_modules/@manohub/app-kit/lint/run-all.mjs"
25
+ else
26
+ echo "[guardrails] 未找到护栏脚本(已查 $APP_DIR/node_modules 与 node_modules)。"
27
+ echo "[guardrails] monorepo 请在钩子里设置 APPKIT_APP_DIR=<应用包目录>(如 apps/sub-app),"
28
+ echo "[guardrails] 否则本次提交不检查(不阻塞,但请尽快修好这条路径)。"
29
+ exit 0
30
+ fi
31
+
32
+ if [ ! -f "$APP_DIR/appkit-guardrails.config.json" ]; then
33
+ echo "[guardrails] $APP_DIR 下缺少 appkit-guardrails.config.json,无法确定被审计的应用。"
34
+ echo "[guardrails] 配置片段见 node_modules/@manohub/app-kit/CONTRACT.md §8。"
35
+ exit 0
36
+ fi
37
+
38
+ exec node "$AUDIT" --changed --cwd="$APP_DIR"
package/lint/run-all.mjs CHANGED
@@ -1,59 +1,63 @@
1
- #!/usr/bin/env node
2
- /**
3
- * run-all —— 三条护栏的统一入口(接入方只挂这一条,见 CONTRACT.md §8)。
4
- *
5
- * 并发跑 style / component / structure,汇总输出与退出码;支持 `--changed`(只跑改动文件,供 pre-commit)。
6
- * 任一脚本报 error 即整体失败;`--json` 时输出聚合结果。
7
- *
8
- * 用法(接入方的 `package.json` 只挂前两条,另两条是排查单条时才用的):
9
- * node node_modules/@manohub/app-kit/lint/run-all.mjs # 全量(挂成 "lint")
10
- * node node_modules/@manohub/app-kit/lint/run-all.mjs --changed # 只跑改动文件(挂成 "lint:changed")
11
- * node node_modules/@manohub/app-kit/lint/style-audit.mjs # 单条排查:style / component / structure
12
- * node node_modules/@manohub/app-kit/lint/run-all.mjs --json
13
- */
14
- import { spawn } from 'node:child_process'
15
- import { fileURLToPath } from 'node:url'
16
- import { dirname, join } from 'node:path'
17
- import { parseArgs } from './shared.mjs'
18
-
19
- const HERE = dirname(fileURLToPath(import.meta.url))
20
- const AUDITS = ['style-audit.mjs', 'component-audit.mjs', 'structure-audit.mjs']
21
-
22
- const args = parseArgs()
23
- const passthrough = process.argv.slice(2).filter((a) => a.startsWith('--app=') || a.startsWith('--cwd='))
24
- if (args.changed) passthrough.push('--changed')
25
- if (args.json) passthrough.push('--json')
26
-
27
- const runOne = (script) =>
28
- new Promise((resolve) => {
29
- const child = spawn(process.execPath, [join(HERE, script), ...passthrough], {
30
- cwd: args.cwd,
31
- stdio: ['ignore', 'pipe', 'pipe'],
32
- })
33
- let stdout = ''
34
- let stderr = ''
35
- child.stdout.on('data', (d) => (stdout += d))
36
- child.stderr.on('data', (d) => (stderr += d))
37
- child.on('close', (code) => resolve({ script, code, stdout, stderr }))
38
- })
39
-
40
- const started = Date.now()
41
- const results = await Promise.all(AUDITS.map(runOne))
42
- const elapsed = ((Date.now() - started) / 1000).toFixed(2)
43
-
44
- if (args.json) {
45
- process.stdout.write(JSON.stringify({ audits: results.map((r) => ({ script: r.script, code: r.code })), elapsed }, null, 2) + '\n')
46
- } else {
47
- for (const r of results) {
48
- if (r.stdout) process.stdout.write(r.stdout)
49
- if (r.stderr) process.stderr.write(r.stderr)
50
- }
51
- const failed = results.filter((r) => r.code !== 0)
52
- process.stdout.write(
53
- failed.length === 0
54
- ? `✓ 护栏(三条):全绿(${elapsed}s)\n`
55
- : `✗ 护栏(三条):${failed.map((f) => f.script).join(' / ')} 未通过(${elapsed}s)\n`,
56
- )
57
- }
58
-
59
- process.exit(results.some((r) => r.code !== 0) ? 1 : 0)
1
+ #!/usr/bin/env node
2
+ /**
3
+ * run-all —— 三条护栏的统一入口(接入方只挂这一条,见 CONTRACT.md §8)。
4
+ *
5
+ * 并发跑 style / component / structure,汇总输出与退出码;支持 `--changed`(只跑改动文件,供 pre-commit)。
6
+ * 任一脚本报 error 即整体失败;`--json` 时输出聚合结果。
7
+ *
8
+ * 用法(经包的命令入口;接入方的 `package.json` 只挂前两条,其余是排查时才用的):
9
+ * pnpm exec appkit lint # 全量(挂成 "lint")
10
+ * pnpm exec appkit lint --changed # 只跑改动文件(挂成 "lint:changed")
11
+ * pnpm exec appkit lint --strict # 收口验收:存量挂起与规则豁免一律失效
12
+ * pnpm exec appkit lint:style # 单条排查:style / component / structure
13
+ * pnpm exec appkit lint --json
14
+ *
15
+ * 直接跑本脚本(`node node_modules/@manohub/app-kit/lint/run-all.mjs`)与上面完全等价 ——
16
+ * CLI 只是薄壳,已有钩子/脚本不必改。
17
+ */
18
+ import { spawn } from 'node:child_process'
19
+ import { fileURLToPath } from 'node:url'
20
+ import { dirname, join } from 'node:path'
21
+ import { parseArgs } from './shared.mjs'
22
+
23
+ const HERE = dirname(fileURLToPath(import.meta.url))
24
+ const AUDITS = ['style-audit.mjs', 'component-audit.mjs', 'structure-audit.mjs']
25
+
26
+ const args = parseArgs()
27
+ const passthrough = process.argv.slice(2).filter((a) => a.startsWith('--app=') || a.startsWith('--cwd='))
28
+ if (args.changed) passthrough.push('--changed')
29
+ if (args.json) passthrough.push('--json')
30
+
31
+ const runOne = (script) =>
32
+ new Promise((resolve) => {
33
+ const child = spawn(process.execPath, [join(HERE, script), ...passthrough], {
34
+ cwd: args.cwd,
35
+ stdio: ['ignore', 'pipe', 'pipe'],
36
+ })
37
+ let stdout = ''
38
+ let stderr = ''
39
+ child.stdout.on('data', (d) => (stdout += d))
40
+ child.stderr.on('data', (d) => (stderr += d))
41
+ child.on('close', (code) => resolve({ script, code, stdout, stderr }))
42
+ })
43
+
44
+ const started = Date.now()
45
+ const results = await Promise.all(AUDITS.map(runOne))
46
+ const elapsed = ((Date.now() - started) / 1000).toFixed(2)
47
+
48
+ if (args.json) {
49
+ process.stdout.write(JSON.stringify({ audits: results.map((r) => ({ script: r.script, code: r.code })), elapsed }, null, 2) + '\n')
50
+ } else {
51
+ for (const r of results) {
52
+ if (r.stdout) process.stdout.write(r.stdout)
53
+ if (r.stderr) process.stderr.write(r.stderr)
54
+ }
55
+ const failed = results.filter((r) => r.code !== 0)
56
+ process.stdout.write(
57
+ failed.length === 0
58
+ ? `✓ 护栏(三条):全绿(${elapsed}s)\n`
59
+ : `✗ 护栏(三条):${failed.map((f) => f.script).join(' / ')} 未通过(${elapsed}s)\n`,
60
+ )
61
+ }
62
+
63
+ process.exit(results.some((r) => r.code !== 0) ? 1 : 0)