| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205 |
- #!/usr/bin/env node
- /**
- * sync-versions.js — Sync version constants across all source files
- *
- * Reads the canonical values from src/data/versions.js and updates
- * every hardcoded occurrence in JS, JSON, MD, and CJS files.
- *
- * Usage:
- * node scripts/sync-versions.js # dry-run (preview changes)
- * node scripts/sync-versions.js --apply # apply changes
- *
- * Safe to run multiple times — idempotent.
- */
- import fs from 'fs';
- import path from 'path';
- import { fileURLToPath } from 'url';
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
- const ROOT = path.resolve(__dirname, '..');
- // Import canonical versions
- const {
- VL_VERSION,
- THEME_VERSION,
- THEME_VERSION_FULL,
- STYLESPACE_VERSION,
- THEME_TAG,
- THEME_PROFILE,
- WORKFLOW_VERSION,
- PARSEVL_URL,
- } = await import('../src/data/versions.js');
- const dryRun = !process.argv.includes('--apply');
- // ── Replacement rules ──────────────────────────────────────────────
- // Each rule: [pattern (regex), replacement (string)]
- // Patterns are designed to match the OLD format regardless of old version number.
- const rules = [
- // VL_VERSION header in code/templates
- [/VL_VERSION:[\d.]+/g, `VL_VERSION:${VL_VERSION}`],
- // VL_SYNTAX_VERSION in md
- [/VL_SYNTAX_VERSION:[\d.]+/g, `VL_SYNTAX_VERSION:${VL_VERSION}`],
- // vlVersion: 'x.x' (JS object literal)
- [/vlVersion:\s*'[\d.]+'/g, `vlVersion: '${VL_VERSION}'`],
- // "vlVersion": "x.x" (JSON)
- [/"vlVersion":\s*"[\d.]+"/g, `"vlVersion": "${VL_VERSION}"`],
- // "version": "x.x" in vl-syntax-meta.json only (too generic for global)
- // handled via file-specific rule below
- // VL x.x syntax references in prompts/comments
- [/VL [\d.]+ syntax/g, `VL ${VL_VERSION} syntax`],
- [/VL [\d.]+ Syntax/g, `VL ${VL_VERSION} Syntax`],
- [/VL \(Visual Logic\) [\d.]+/g, `VL (Visual Logic) ${VL_VERSION}`],
- [/VL \(Visual Language\) v[\d.]+/g, `VL (Visual Language) v${VL_VERSION}`],
- // VL Language Complete Reference (vX.X)
- [/VL Language Complete Reference \(v[\d.]+\)/g, `VL Language Complete Reference (v${VL_VERSION})`],
- // "Follow VL x.x syntax strictly"
- [/Follow VL [\d.]+ syntax/g, `Follow VL ${VL_VERSION} syntax`],
- [/following VL [\d.]+ syntax/g, `following VL ${VL_VERSION} syntax`],
- // "VL x.x" standalone in comments (e.g. "VL 3.5 injects", "VL 3.5 PREFERRED")
- // Only match "VL X.X" followed by common suffixes to avoid false positives
- [/VL [\d.]+( injects| PREFERRED| primary| data-vl-id| live| IDs| runtime)/g,
- `VL ${VL_VERSION}$1`],
- // Theme-Enterprise-X.X tag
- [/Theme-Enterprise-[\d.]+/g, `Theme-Enterprise-${THEME_VERSION}`],
- // Theme version in theme content: version:"X.X.X"
- [/version:"[\d.]+"\n/g, `version:"${THEME_VERSION_FULL}"\n`],
- // styleSpaceVersion:"X.X"
- [/styleSpaceVersion:"[\d.]+"/g, `styleSpaceVersion:"${STYLESPACE_VERSION}"`],
- // Enterprise Theme X.X in comments
- [/Enterprise Theme [\d.]+/g, `Enterprise Theme ${THEME_VERSION}`],
- // Enterprise theme vX.X in doc tables
- [/Enterprise theme v[\d.]+/g, `Enterprise theme v${THEME_VERSION}`],
- // THEME_X.X.vth in doc tables
- [/THEME_[\d.]+\.vth/g, `THEME_${THEME_VERSION}.vth`],
- // StyleSpace X.X references
- [/StyleSpace [\d.]+/g, `StyleSpace ${STYLESPACE_VERSION}`],
- // VL x.x Syntax Rules (in workflow JSON docs)
- [/"VL [\d.]+ Syntax Rules"/g, `"VL ${VL_VERSION} Syntax Rules"`],
- // "for VL x.x syntax compliance"
- [/for VL [\d.]+ syntax compliance/g, `for VL ${VL_VERSION} syntax compliance`],
- // version: 'x.x' in meta.json context (type: 'vl-project')
- [/version: '[\d.]+',\n(\s+type: 'vl-project')/g, `version: '${VL_VERSION}',\n$1`],
- // ── VL Workflow spec version (3.15 → 3.16, etc.) ──
- // Matches workflow-builder.js, autotest-pipeline.js: version: '3.xx'
- [/version:\s*'3\.\d{2}'/g, `version: '${WORKFLOW_VERSION}'`],
- // Matches orchestrator.js template strings: version "3.xx"
- [/version "3\.\d{2}"/g, `version "${WORKFLOW_VERSION}"`],
- // Matches workflow-run.js: spec: 'v3.xx'
- [/spec:\s*'v3\.\d{2}'/g, `spec: 'v${WORKFLOW_VERSION}'`],
- // Matches AGENTS.md / docs: VL Workflow 3.xx
- [/VL Workflow [\d.]+/g, `VL Workflow ${WORKFLOW_VERSION}`],
- // Matches short-form vX.xx spec references in comments/strings
- [/\(VL Workflow spec v[\d.]+\)/g, `(VL Workflow spec v${WORKFLOW_VERSION})`],
- // ── VL version in short form (app banners, package.json description) ──
- [/\bVL v[\d.]+/g, `VL v${VL_VERSION}`],
- ];
- // ── File-specific rules ────────────────────────────────────────────
- const fileRules = {
- 'src/data/vl-syntax-meta.json': [
- [/"version":\s*"[\d.]+"/g, `"version": "${VL_VERSION}"`],
- ],
- };
- // ── Scan targets ───────────────────────────────────────────────────
- const SCAN_DIRS = ['src', 'public', '.vl-code/workflows'];
- const ROOT_FILES = ['AGENTS.md', 'MODULE-SPEC.md', 'package.json', 'CLAUDE.md'];
- const EXTENSIONS = new Set(['.js', '.cjs', '.mjs', '.json', '.md', '.html']);
- const SKIP = new Set(['node_modules', 'dist', '.git', 'versions.js']);
- function walk(dir) {
- const results = [];
- for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
- if (SKIP.has(entry.name)) continue;
- const full = path.join(dir, entry.name);
- if (entry.isDirectory()) {
- results.push(...walk(full));
- } else if (EXTENSIONS.has(path.extname(entry.name))) {
- results.push(full);
- }
- }
- return results;
- }
- // ── Main ───────────────────────────────────────────────────────────
- console.log(`\n📐 VL-Code Version Sync`);
- console.log(` VL_VERSION: ${VL_VERSION}`);
- console.log(` THEME_VERSION: ${THEME_VERSION} (${THEME_VERSION_FULL})`);
- console.log(` STYLESPACE: ${STYLESPACE_VERSION}`);
- console.log(` THEME_TAG: ${THEME_TAG}`);
- console.log(` Mode: ${dryRun ? 'DRY RUN (use --apply to write)' : 'APPLYING'}\n`);
- let totalFiles = 0;
- let changedFiles = 0;
- let totalReplacements = 0;
- const files = [
- ...SCAN_DIRS.flatMap(d => walk(path.join(ROOT, d))),
- ...ROOT_FILES.map(f => path.join(ROOT, f)).filter(f => fs.existsSync(f)),
- ];
- for (const filePath of files) {
- const relPath = path.relative(ROOT, filePath);
- const original = fs.readFileSync(filePath, 'utf-8');
- let content = original;
- let fileChanges = 0;
- // Apply global rules
- for (const [pattern, replacement] of rules) {
- const before = content;
- content = content.replace(pattern, replacement);
- if (content !== before) {
- const count = (before.match(pattern) || []).length;
- fileChanges += count;
- }
- }
- // Apply file-specific rules
- const extras = fileRules[relPath];
- if (extras) {
- for (const [pattern, replacement] of extras) {
- const before = content;
- content = content.replace(pattern, replacement);
- if (content !== before) {
- const count = (before.match(pattern) || []).length;
- fileChanges += count;
- }
- }
- }
- totalFiles++;
- if (content !== original) {
- changedFiles++;
- totalReplacements += fileChanges;
- console.log(` ✏️ ${relPath} (${fileChanges} replacements)`);
- if (!dryRun) {
- fs.writeFileSync(filePath, content, 'utf-8');
- }
- }
- }
- console.log(`\n${dryRun ? '🔍 Preview' : '✅ Done'}: ${changedFiles}/${totalFiles} files, ${totalReplacements} replacements\n`);
- if (dryRun && changedFiles > 0) {
- console.log('Run with --apply to write changes:\n node scripts/sync-versions.js --apply\n');
- }
|