VS Code extension that previews Archify diagrams from their JSON sources (live, as you type) and opens rendered Archify HTML in a viewer tab. Bundles the Archify 3.0.1 renderer and runs it on VS Code's Node runtime. Adds validation diagnostics, JSON schema help, source-link navigation, export saving, render-to-file and open-in-browser commands. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
99 lines
3.6 KiB
JavaScript
99 lines
3.6 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
import Ajv2020 from 'ajv/dist/2020.js';
|
|
import standaloneCode from 'ajv/dist/standalone/index.js';
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
const root = path.resolve(__dirname, '..');
|
|
const schemasDir = path.join(root, 'schemas');
|
|
const output = path.join(root, 'renderers/shared/generated-validators.mjs');
|
|
const diagramTypes = ['workflow', 'sequence', 'dataflow', 'lifecycle', 'architecture'];
|
|
|
|
const ajv = new Ajv2020({
|
|
allErrors: true,
|
|
strict: true,
|
|
code: { source: true, esm: true },
|
|
});
|
|
ajv.addSchema(JSON.parse(fs.readFileSync(path.join(schemasDir, 'common.schema.json'), 'utf8')));
|
|
|
|
const schemaIds = {};
|
|
for (const type of diagramTypes) {
|
|
const schema = JSON.parse(fs.readFileSync(path.join(schemasDir, `${type}.schema.json`), 'utf8'));
|
|
ajv.addSchema(schema);
|
|
schemaIds[type] = schema.$id;
|
|
}
|
|
|
|
const banner = '// Generated by scripts/generate-validators.mjs. Do not edit by hand.\n';
|
|
const ajvUcs2Import = 'require("ajv/dist/runtime/ucs2length").default';
|
|
const inlineUcs2Length = `function ucs2length(str) {
|
|
const len = str.length;
|
|
let length = 0;
|
|
let pos = 0;
|
|
while (pos < len) {
|
|
length += 1;
|
|
const value = str.charCodeAt(pos++);
|
|
if (value >= 0xd800 && value <= 0xdbff && pos < len
|
|
&& (str.charCodeAt(pos) & 0xfc00) === 0xdc00) pos += 1;
|
|
}
|
|
return length;
|
|
}`;
|
|
let validatorCode = standaloneCode(ajv, schemaIds);
|
|
if (!validatorCode.includes(ajvUcs2Import)) {
|
|
throw new Error('AJV standalone output no longer contains the expected ucs2length helper');
|
|
}
|
|
validatorCode = validatorCode.replaceAll(ajvUcs2Import, inlineUcs2Length);
|
|
if (validatorCode.includes('require(')) {
|
|
throw new Error('AJV standalone output contains an unexpected runtime dependency');
|
|
}
|
|
for (const type of diagramTypes) {
|
|
const exportPattern = new RegExp(`export const ${type} = (validate\\d+);`);
|
|
const match = validatorCode.match(exportPattern);
|
|
if (!match) throw new Error(`AJV standalone output no longer exports the ${type} validator as expected`);
|
|
validatorCode = validatorCode.replace(exportPattern, `const ${type}Schema = ${match[1]};`);
|
|
}
|
|
|
|
const portableOutputWrappers = diagramTypes.map((type) => `export function ${type}(data, context = undefined) {
|
|
if (!${type}Schema(data, context)) {
|
|
${type}.errors = ${type}Schema.errors;
|
|
return false;
|
|
}
|
|
const output = data?.meta?.output;
|
|
if (typeof output === 'string') {
|
|
try {
|
|
validatePortablePath(output, { profile: 'output' });
|
|
} catch (error) {
|
|
${type}.errors = [{
|
|
instancePath: \`${'${context?.instancePath || \'\'}'}/meta/output\`,
|
|
schemaPath: 'common.schema.json#/$defs/portableOutputPath',
|
|
keyword: 'portablePath',
|
|
params: { reason: error?.reason || 'invalid' },
|
|
message: 'must satisfy the portable output path contract',
|
|
}];
|
|
return false;
|
|
}
|
|
}
|
|
${type}.errors = null;
|
|
return true;
|
|
}
|
|
${type}.evaluated = ${type}Schema.evaluated;`).join('\n');
|
|
|
|
const generated = `${banner}import { validatePortablePath } from './portable-path.mjs';\n${validatorCode}\n${portableOutputWrappers}\n`;
|
|
|
|
if (process.argv.includes('--check')) {
|
|
const current = fs.existsSync(output)
|
|
? fs.readFileSync(output, 'utf8').replace(/\r\n?/g, '\n')
|
|
: '';
|
|
if (current !== generated) {
|
|
console.error('generated validators are stale — run npm run generate:validators');
|
|
process.exit(1);
|
|
}
|
|
} else {
|
|
const temporary = `${output}.${process.pid}.tmp`;
|
|
fs.writeFileSync(temporary, generated);
|
|
fs.renameSync(temporary, output);
|
|
console.log(`generated ${path.relative(root, output)}`);
|
|
}
|