@open-dy/vibecoding-cli 0.0.1-beta.1 → 0.0.1-beta.2
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-dy/vibecoding-cli",
|
|
3
|
-
"version": "0.0.1-beta.
|
|
3
|
+
"version": "0.0.1-beta.2",
|
|
4
4
|
"description": "Command orchestration, validation and packaging for Douyin vibecoding Taro and OpenCard projects.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
"ajv": "^8.18.0"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
|
-
"@open-dy/vibecoding-sdk-taro": "0.0.1-beta.
|
|
44
|
-
"@open-dy/vibecoding-sdk-opencard": "0.0.1-beta.
|
|
43
|
+
"@open-dy/vibecoding-sdk-taro": "0.0.1-beta.2",
|
|
44
|
+
"@open-dy/vibecoding-sdk-opencard": "0.0.1-beta.2"
|
|
45
45
|
},
|
|
46
46
|
"peerDependenciesMeta": {
|
|
47
47
|
"@open-dy/vibecoding-sdk-taro": {
|
|
@@ -54,5 +54,10 @@
|
|
|
54
54
|
"engines": {
|
|
55
55
|
"node": ">=18"
|
|
56
56
|
},
|
|
57
|
-
"license": "UNLICENSED"
|
|
57
|
+
"license": "UNLICENSED",
|
|
58
|
+
"publishConfig": {
|
|
59
|
+
"registry": "https://registry.npmjs.org/",
|
|
60
|
+
"tag": "beta",
|
|
61
|
+
"access": "public"
|
|
62
|
+
}
|
|
58
63
|
}
|
|
@@ -14,7 +14,6 @@ const __dirname = path.join(process.env.VIBECODING_OPENCARD_ROOT || process.cwd(
|
|
|
14
14
|
const ROOT = path.resolve(__dirname, '..');
|
|
15
15
|
const SRC = path.join(ROOT, 'src');
|
|
16
16
|
const PAGE_ENTRY = path.join(SRC, 'pages/index/index.tsx');
|
|
17
|
-
const require = createRequire(path.join(ROOT, 'scripts', 'package.json'));
|
|
18
17
|
|
|
19
18
|
const SHELL_CLASS = 'widget-root';
|
|
20
19
|
const REQUIRED_RADIUS_PX = 12;
|
|
@@ -332,7 +331,8 @@ function jsxName(el) {
|
|
|
332
331
|
return '';
|
|
333
332
|
}
|
|
334
333
|
|
|
335
|
-
function loadBabel() {
|
|
334
|
+
function loadBabel(root = ROOT) {
|
|
335
|
+
const require = createRequire(path.join(root, 'scripts', 'package.json'));
|
|
336
336
|
try {
|
|
337
337
|
const parser = require('@babel/parser');
|
|
338
338
|
const traverseMod = require('@babel/traverse');
|
|
@@ -384,8 +384,8 @@ function collectPageRootClassSetsRegex(source) {
|
|
|
384
384
|
return [{ type: 'View', classes }];
|
|
385
385
|
}
|
|
386
386
|
|
|
387
|
-
export function collectPageRootClassSets(source) {
|
|
388
|
-
const babel = loadBabel();
|
|
387
|
+
export function collectPageRootClassSets(source, root = ROOT) {
|
|
388
|
+
const babel = loadBabel(root);
|
|
389
389
|
if (!babel) return collectPageRootClassSetsRegex(source);
|
|
390
390
|
|
|
391
391
|
let ast;
|
|
@@ -626,9 +626,10 @@ export function lintWidgetShell({
|
|
|
626
626
|
pageSource,
|
|
627
627
|
pageFile = 'src/pages/index/index.tsx',
|
|
628
628
|
rules,
|
|
629
|
+
root = ROOT,
|
|
629
630
|
}) {
|
|
630
631
|
const findings = [];
|
|
631
|
-
const roots = collectPageRootClassSets(pageSource);
|
|
632
|
+
const roots = collectPageRootClassSets(pageSource, root);
|
|
632
633
|
if (roots.length === 0) {
|
|
633
634
|
findings.push(
|
|
634
635
|
finding(
|
|
@@ -698,9 +699,9 @@ function parseInlineCssFiles(files) {
|
|
|
698
699
|
}).flat();
|
|
699
700
|
}
|
|
700
701
|
|
|
701
|
-
export function lintWidgetShellFromSources({ pageSource, cssFiles }) {
|
|
702
|
+
export function lintWidgetShellFromSources({ pageSource, cssFiles, root = ROOT }) {
|
|
702
703
|
const rules = parseInlineCssFiles(cssFiles);
|
|
703
|
-
return lintWidgetShell({ pageSource, rules });
|
|
704
|
+
return lintWidgetShell({ pageSource, rules, root });
|
|
704
705
|
}
|
|
705
706
|
|
|
706
707
|
function assertSelfTest() {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync, readdirSync } from 'node:fs'
|
|
2
2
|
import path from 'node:path'
|
|
3
|
+
import { lintWidgetShellFromSources } from '../adapters/interest-card/opencard/validate-widget-shell.mjs'
|
|
3
4
|
|
|
4
5
|
function listCssFiles(directory, files = []) {
|
|
5
6
|
if (!existsSync(directory)) return files
|
|
@@ -14,94 +15,41 @@ function listCssFiles(directory, files = []) {
|
|
|
14
15
|
return files
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
function rootRule(source) {
|
|
18
|
-
const match = /\.widget-root\s*\{([\s\S]*?)\}/m.exec(source)
|
|
19
|
-
return match?.[1] ?? ''
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function hasDeclaration(css, property, value) {
|
|
23
|
-
const expression = new RegExp(
|
|
24
|
-
`${property.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\s*:\\s*${value}`,
|
|
25
|
-
'i',
|
|
26
|
-
)
|
|
27
|
-
return expression.test(css)
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function formatFinding(file, line, column, message) {
|
|
31
|
-
return `${file}:${line}:${column} ${message}`
|
|
32
|
-
}
|
|
33
|
-
|
|
34
18
|
export function validateWidgetShell(context) {
|
|
35
19
|
const root = context.target === 'opencard' ? context.root : context.opencardRoot
|
|
36
20
|
if (!root) return { skipped: true, findings: [] }
|
|
37
21
|
|
|
38
22
|
const pageFile = path.join(root, 'src/pages/index/index.tsx')
|
|
39
|
-
|
|
40
|
-
const findings = []
|
|
23
|
+
let findings
|
|
41
24
|
if (!existsSync(pageFile)) {
|
|
42
|
-
findings
|
|
43
|
-
file: 'src/pages/index/index.tsx',
|
|
44
|
-
line: 1,
|
|
45
|
-
column: 1,
|
|
25
|
+
findings = [{
|
|
26
|
+
file: 'src/pages/index/index.tsx', line: 1, column: 1,
|
|
46
27
|
message: '缺少 OpenCard 页面入口。',
|
|
47
|
-
}
|
|
28
|
+
}]
|
|
48
29
|
} else {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
30
|
+
// Reuse the master checker: inspect returned roots and merged CSS, including variables.
|
|
31
|
+
const cssFiles = listCssFiles(path.join(root, 'src'))
|
|
32
|
+
.map((file) => ({
|
|
33
|
+
filename: path.relative(root, file).replace(/\\/g, '/'),
|
|
34
|
+
source: readFileSync(file, 'utf8'),
|
|
35
|
+
}))
|
|
36
|
+
.sort((a, b) => {
|
|
37
|
+
if (a.filename === 'src/app.css') return -1
|
|
38
|
+
if (b.filename === 'src/app.css') return 1
|
|
39
|
+
return a.filename.localeCompare(b.filename)
|
|
56
40
|
})
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const css = cssFiles.map((file) => readFileSync(file, 'utf8')).join('\n')
|
|
62
|
-
const shell = rootRule(css)
|
|
63
|
-
if (!shell) {
|
|
64
|
-
findings.push({
|
|
65
|
-
file: 'src/pages/index/index.css',
|
|
66
|
-
line: 1,
|
|
67
|
-
column: 1,
|
|
68
|
-
message: '缺少 .widget-root CSS。',
|
|
41
|
+
findings = lintWidgetShellFromSources({
|
|
42
|
+
root,
|
|
43
|
+
pageSource: readFileSync(pageFile, 'utf8'),
|
|
44
|
+
cssFiles,
|
|
69
45
|
})
|
|
70
|
-
} else {
|
|
71
|
-
const checks = [
|
|
72
|
-
[/box-sizing\s*:\s*border-box/i, '底板必须使用 box-sizing: border-box。'],
|
|
73
|
-
[/display\s*:\s*flex/i, '底板必须使用 Flex 布局。'],
|
|
74
|
-
[/height\s*:\s*100%/i, '底板必须使用 height: 100%,不能使用 100vh。'],
|
|
75
|
-
[/background(?:-color)?\s*:\s*(?!transparent|none)[^;]+/i, '底板必须有非透明背景。'],
|
|
76
|
-
[/border\s*:\s*1px\s+(?:solid|dashed|dotted)\s+[^;]+/i, '底板必须有 1px 描边。'],
|
|
77
|
-
[/border-radius\s*:\s*(?:12px|var\(--[A-Za-z0-9_-]+\))/i, '底板四角必须使用 12px 圆角。'],
|
|
78
|
-
]
|
|
79
|
-
for (const [expression, message] of checks) {
|
|
80
|
-
if (!expression.test(shell)) {
|
|
81
|
-
findings.push({
|
|
82
|
-
file: 'src/pages/index/index.css',
|
|
83
|
-
line: 1,
|
|
84
|
-
column: 1,
|
|
85
|
-
message,
|
|
86
|
-
})
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
if (hasDeclaration(shell, 'height', '100vh')) {
|
|
90
|
-
findings.push({
|
|
91
|
-
file: 'src/pages/index/index.css',
|
|
92
|
-
line: 1,
|
|
93
|
-
column: 1,
|
|
94
|
-
message: '固定 widget 禁止使用 height: 100vh。',
|
|
95
|
-
})
|
|
96
|
-
}
|
|
97
46
|
}
|
|
98
47
|
|
|
99
48
|
if (findings.length > 0) {
|
|
100
49
|
console.warn(`WARN: OpenCard widget 底板校验未通过(${findings.length}),不阻断构建`)
|
|
101
50
|
for (const finding of findings) {
|
|
102
|
-
console.warn(
|
|
51
|
+
console.warn(`${finding.file}:${finding.line}:${finding.column} ${finding.message}`)
|
|
103
52
|
}
|
|
104
53
|
}
|
|
105
|
-
|
|
106
54
|
return { skipped: false, findings }
|
|
107
55
|
}
|