@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.
Files changed (2) hide show
  1. package/dist/src/cli.js +58 -13
  2. 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('../package.json');
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
- console.log(`${chalk.green('✓')} ${file}`);
42
- return true;
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}: ${chalk.red(err.message)}`);
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}: ${msg}`);
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
- (await doCheck(f)) ? passed++ : failed++;
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 if (pattern.endsWith('.xvml')) {
109
- (await doCheck(pattern)) ? passed++ : failed++;
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
- console.log(`\n${chalk.bold(String(total))} file${total === 1 ? '' : 's'} checked — ` +
118
- `${chalk.green(String(passed))} passed, ${failed > 0 ? chalk.red(String(failed)) : chalk.dim('0')} failed`);
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.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",