@xvml/cli 0.1.1 → 0.1.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.
- package/dist/src/cli.js +58 -13
- package/package.json +3 -2
package/dist/src/cli.js
CHANGED
|
@@ -5,11 +5,12 @@ import { watch as fsWatch } from 'fs';
|
|
|
5
5
|
import path from 'path';
|
|
6
6
|
import { createRequire } from 'module';
|
|
7
7
|
import fse from 'fs-extra';
|
|
8
|
+
import { glob } from 'glob';
|
|
8
9
|
import { renderFile, outputPath } from './renderer.js';
|
|
9
10
|
import { parse, ParseError } from './parser.js';
|
|
10
11
|
import { askClaude, slugify } from './agent.js';
|
|
11
12
|
const require = createRequire(import.meta.url);
|
|
12
|
-
const { version } = require('
|
|
13
|
+
const { version } = require('../../package.json');
|
|
13
14
|
const XVMLRC_DEFAULT = JSON.stringify({ outDir: 'docs', spec: 1 }, null, 2);
|
|
14
15
|
async function findVmlFiles(dir) {
|
|
15
16
|
const SKIP = new Set(['node_modules', 'docs', 'dist', '.git']);
|
|
@@ -35,21 +36,39 @@ async function doRender(file) {
|
|
|
35
36
|
console.log(`${chalk.green('✓')} ${chalk.dim(file)} → ${chalk.cyan(out)}`);
|
|
36
37
|
}
|
|
37
38
|
async function doCheck(file) {
|
|
39
|
+
const warnings = [];
|
|
38
40
|
try {
|
|
39
41
|
const source = await fs.readFile(file, 'utf-8');
|
|
40
|
-
parse(source);
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
const doc = parse(source);
|
|
43
|
+
// Spec-level checks
|
|
44
|
+
if (doc.specVersion === 1 && !source.includes('@spec')) {
|
|
45
|
+
warnings.push('missing @spec directive (defaulting to spec 1)');
|
|
46
|
+
}
|
|
47
|
+
if (!doc.page) {
|
|
48
|
+
warnings.push('missing @page directive');
|
|
49
|
+
}
|
|
50
|
+
if (warnings.length > 0) {
|
|
51
|
+
console.log(`${chalk.yellow('⚠')} ${file}`);
|
|
52
|
+
for (const w of warnings) {
|
|
53
|
+
console.log(` ${chalk.yellow('warn')} ${w}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
console.log(`${chalk.green('✓')} ${file}`);
|
|
58
|
+
}
|
|
59
|
+
return { ok: true, warnings };
|
|
43
60
|
}
|
|
44
61
|
catch (err) {
|
|
45
62
|
if (err instanceof ParseError) {
|
|
46
|
-
console.error(`${chalk.red('✕')} ${file}
|
|
63
|
+
console.error(`${chalk.red('✕')} ${file}`);
|
|
64
|
+
console.error(` ${chalk.red('error')} ${err.message}`);
|
|
47
65
|
}
|
|
48
66
|
else {
|
|
49
67
|
const msg = err instanceof Error ? err.message : String(err);
|
|
50
|
-
console.error(`${chalk.red('✕')} ${file}
|
|
68
|
+
console.error(`${chalk.red('✕')} ${file}`);
|
|
69
|
+
console.error(` ${chalk.red('error')} ${msg}`);
|
|
51
70
|
}
|
|
52
|
-
return false;
|
|
71
|
+
return { ok: false, warnings };
|
|
53
72
|
}
|
|
54
73
|
}
|
|
55
74
|
export function buildCli() {
|
|
@@ -96,17 +115,38 @@ export function buildCli() {
|
|
|
96
115
|
.description('Check .xvml files for spec compliance')
|
|
97
116
|
.action(async (patterns) => {
|
|
98
117
|
let passed = 0;
|
|
118
|
+
let warned = 0;
|
|
99
119
|
let failed = 0;
|
|
120
|
+
async function checkFile(f) {
|
|
121
|
+
const result = await doCheck(f);
|
|
122
|
+
if (!result.ok)
|
|
123
|
+
failed++;
|
|
124
|
+
else if (result.warnings.length > 0) {
|
|
125
|
+
warned++;
|
|
126
|
+
passed++;
|
|
127
|
+
}
|
|
128
|
+
else
|
|
129
|
+
passed++;
|
|
130
|
+
}
|
|
100
131
|
for (const pattern of patterns) {
|
|
101
132
|
const stat = await fs.stat(pattern).catch(() => null);
|
|
102
133
|
if (stat?.isDirectory()) {
|
|
103
134
|
const files = await findVmlFiles(pattern);
|
|
104
|
-
for (const f of files)
|
|
105
|
-
|
|
135
|
+
for (const f of files)
|
|
136
|
+
await checkFile(f);
|
|
137
|
+
}
|
|
138
|
+
else if (pattern.includes('*') || pattern.includes('{')) {
|
|
139
|
+
// Glob pattern
|
|
140
|
+
const matches = await glob(pattern);
|
|
141
|
+
const files = matches.filter(f => f.endsWith('.xvml'));
|
|
142
|
+
if (files.length === 0) {
|
|
143
|
+
console.log(chalk.yellow(`No .xvml files matched: ${pattern}`));
|
|
106
144
|
}
|
|
145
|
+
for (const f of files)
|
|
146
|
+
await checkFile(f);
|
|
107
147
|
}
|
|
108
|
-
else
|
|
109
|
-
|
|
148
|
+
else {
|
|
149
|
+
await checkFile(pattern);
|
|
110
150
|
}
|
|
111
151
|
}
|
|
112
152
|
const total = passed + failed;
|
|
@@ -114,8 +154,13 @@ export function buildCli() {
|
|
|
114
154
|
console.log(chalk.yellow('No .xvml files found.'));
|
|
115
155
|
return;
|
|
116
156
|
}
|
|
117
|
-
|
|
118
|
-
|
|
157
|
+
const summary = [
|
|
158
|
+
`\n${chalk.bold(String(total))} file${total === 1 ? '' : 's'} checked —`,
|
|
159
|
+
`${chalk.green(String(passed))} passed`,
|
|
160
|
+
warned > 0 ? `(${chalk.yellow(String(warned))} with warnings)` : '',
|
|
161
|
+
`${failed > 0 ? chalk.red(String(failed)) : chalk.dim('0')} failed`,
|
|
162
|
+
].filter(Boolean).join(' ');
|
|
163
|
+
console.log(summary);
|
|
119
164
|
if (failed > 0)
|
|
120
165
|
process.exit(1);
|
|
121
166
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xvml/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "XVML — Visual Markup Language CLI renderer",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -39,7 +39,8 @@
|
|
|
39
39
|
"@anthropic-ai/sdk": "^0.104.1",
|
|
40
40
|
"chalk": "^5.6.2",
|
|
41
41
|
"commander": "^15.0.0",
|
|
42
|
-
"fs-extra": "^11.3.5"
|
|
42
|
+
"fs-extra": "^11.3.5",
|
|
43
|
+
"glob": "^13.0.6"
|
|
43
44
|
},
|
|
44
45
|
"devDependencies": {
|
|
45
46
|
"@types/fs-extra": "^11.0.4",
|