@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.1",
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.1",
44
- "@open-dy/vibecoding-sdk-opencard": "0.0.1-beta.1"
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
- const sourceDirectory = path.join(root, 'src')
40
- const findings = []
23
+ let findings
41
24
  if (!existsSync(pageFile)) {
42
- findings.push({
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
- const page = readFileSync(pageFile, 'utf8')
50
- if (!/<View\b[^>]*className\s*=\s*(?:"[^"]*\bwidget-root\b[^"]*"|'[^']*\bwidget-root\b[^']*')/m.test(page)) {
51
- findings.push({
52
- file: 'src/pages/index/index.tsx',
53
- line: 1,
54
- column: 1,
55
- message: '页面根 View 必须使用 widget-root 作为固定 widget 底板。',
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
- const cssFiles = listCssFiles(sourceDirectory)
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(formatFinding(finding.file, finding.line, finding.column, finding.message))
51
+ console.warn(`${finding.file}:${finding.line}:${finding.column} ${finding.message}`)
103
52
  }
104
53
  }
105
-
106
54
  return { skipped: false, findings }
107
55
  }