diff options
| author | Alex Pooley (@zuedev) <zuedev@gmail.com> | 2026-02-05 16:03:08 +0000 |
|---|---|---|
| committer | Alex Pooley (@zuedev) <zuedev@gmail.com> | 2026-02-05 16:03:08 +0000 |
| commit | 4775de64eb752b90267114c0c1c95235820521f3 (patch) | |
| tree | a24d73dd2ebe337a45bb6347c7be856bf72afad4 /validate.js | |
| parent | 6857b2514ac26a0731208e6f7c312cd70b606544 (diff) | |
| download | gitinfo-4775de64eb752b90267114c0c1c95235820521f3.tar gitinfo-4775de64eb752b90267114c0c1c95235820521f3.tar.gz gitinfo-4775de64eb752b90267114c0c1c95235820521f3.tar.bz2 gitinfo-4775de64eb752b90267114c0c1c95235820521f3.tar.xz gitinfo-4775de64eb752b90267114c0c1c95235820521f3.zip | |
add CLI validators in Node.js, PowerShell, and Bash
Move validate.js to validators/nodejs/ and add equivalent
validators for PowerShell and Bash. Each includes a README
with usage instructions and requirements.
Diffstat (limited to 'validate.js')
| -rw-r--r-- | validate.js | 234 |
1 files changed, 0 insertions, 234 deletions
diff --git a/validate.js b/validate.js deleted file mode 100644 index 404a6f0..0000000 --- a/validate.js +++ /dev/null @@ -1,234 +0,0 @@ -#!/usr/bin/env node - -/** - * CLI validator for .gitinfo files - * Usage: node validate.js [file] - * node validate.js # validates .gitinfo in current directory - * node validate.js path/to/.gitinfo - */ - -const fs = require("fs"); -const path = require("path"); - -// Load the schema -const SCHEMA_PATH = path.join(__dirname, "gitinfo.schema.json"); - -/** - * Strip JSONC comments (single-line // and multi-line /* *\/) - */ -function stripJsonComments(jsonc) { - let result = ""; - let i = 0; - let inString = false; - let stringChar = null; - - while (i < jsonc.length) { - const char = jsonc[i]; - const next = jsonc[i + 1]; - - // Track string state - if (!inString && (char === '"' || char === "'")) { - inString = true; - stringChar = char; - result += char; - i++; - continue; - } - - if (inString) { - if (char === "\\" && i + 1 < jsonc.length) { - // Escape sequence - result += char + jsonc[i + 1]; - i += 2; - continue; - } - if (char === stringChar) { - inString = false; - stringChar = null; - } - result += char; - i++; - continue; - } - - // Single-line comment - if (char === "/" && next === "/") { - while (i < jsonc.length && jsonc[i] !== "\n") { - i++; - } - continue; - } - - // Multi-line comment - if (char === "/" && next === "*") { - i += 2; - while ( - i < jsonc.length - 1 && - !(jsonc[i] === "*" && jsonc[i + 1] === "/") - ) { - i++; - } - i += 2; - continue; - } - - result += char; - i++; - } - - return result; -} - -/** - * Simple JSON Schema validator (subset of draft 2020-12) - */ -function validateSchema(data, schema, path = "") { - const errors = []; - - if (schema.type === "object") { - if (typeof data !== "object" || data === null || Array.isArray(data)) { - errors.push(`${path || "root"}: expected object`); - return errors; - } - - // Check additionalProperties - if (schema.additionalProperties === false && schema.properties) { - const allowed = new Set(Object.keys(schema.properties)); - for (const key of Object.keys(data)) { - if (!allowed.has(key)) { - errors.push(`${path || "root"}: unknown property "${key}"`); - } - } - } - - // Validate each property - if (schema.properties) { - for (const [key, propSchema] of Object.entries(schema.properties)) { - if (data[key] !== undefined) { - errors.push( - ...validateSchema(data[key], propSchema, `${path}.${key}`), - ); - } - } - } - } else if (schema.type === "array") { - if (!Array.isArray(data)) { - errors.push(`${path}: expected array`); - return errors; - } - - if (schema.items) { - if (Array.isArray(schema.items)) { - // Tuple validation - for (let i = 0; i < data.length; i++) { - const itemSchema = schema.items[i] || {}; - errors.push(...validateSchema(data[i], itemSchema, `${path}[${i}]`)); - } - if (schema.minItems && data.length < schema.minItems) { - errors.push(`${path}: expected at least ${schema.minItems} items`); - } - if (schema.maxItems && data.length > schema.maxItems) { - errors.push(`${path}: expected at most ${schema.maxItems} items`); - } - } else { - // Array of same type - for (let i = 0; i < data.length; i++) { - errors.push( - ...validateSchema(data[i], schema.items, `${path}[${i}]`), - ); - } - } - } - } else if (schema.type === "string") { - if (typeof data !== "string") { - errors.push(`${path}: expected string`); - return errors; - } - - if (schema.minLength && data.length < schema.minLength) { - errors.push(`${path}: string too short (min ${schema.minLength})`); - } - - if (schema.format === "uri") { - try { - new URL(data); - } catch { - errors.push(`${path}: invalid URI "${data}"`); - } - } - - if (schema.format === "email") { - if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data)) { - errors.push(`${path}: invalid email "${data}"`); - } - } - - if (schema.pattern) { - if (!new RegExp(schema.pattern).test(data)) { - errors.push(`${path}: does not match pattern ${schema.pattern}`); - } - } - } - - return errors; -} - -function main() { - const args = process.argv.slice(2); - const filePath = args[0] || ".gitinfo"; - - // Check if file exists - if (!fs.existsSync(filePath)) { - console.error(`Error: File not found: ${filePath}`); - process.exit(1); - } - - // Check if schema exists - if (!fs.existsSync(SCHEMA_PATH)) { - console.error(`Error: Schema not found: ${SCHEMA_PATH}`); - process.exit(1); - } - - // Read and parse schema - let schema; - try { - schema = JSON.parse(fs.readFileSync(SCHEMA_PATH, "utf-8")); - } catch (err) { - console.error(`Error parsing schema: ${err.message}`); - process.exit(1); - } - - // Read and parse .gitinfo file - let content; - try { - content = fs.readFileSync(filePath, "utf-8"); - } catch (err) { - console.error(`Error reading file: ${err.message}`); - process.exit(1); - } - - let data; - try { - const stripped = stripJsonComments(content); - data = JSON.parse(stripped); - } catch (err) { - console.error(`Error parsing JSONC: ${err.message}`); - process.exit(1); - } - - // Validate against schema - const errors = validateSchema(data, schema); - - if (errors.length > 0) { - console.error(`Validation failed for ${filePath}:`); - for (const error of errors) { - console.error(` - ${error}`); - } - process.exit(1); - } - - console.log(`✓ ${filePath} is valid`); - process.exit(0); -} - -main(); |
