First cut

This commit is contained in:
kadil
2026-04-17 16:08:31 -05:00
parent d10105ac00
commit 4ee4cb8e7c
58 changed files with 3243 additions and 1 deletions

26
scripts/canonicalize.js Normal file
View File

@@ -0,0 +1,26 @@
#!/usr/bin/env node
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, '..');
const { stringifyCanonical } = await import(path.join(root, 'shared/src/canonical-json.ts'));
const files = [
'site-context.json',
'content/events.json',
'config/sms-sites.json',
...fs.readdirSync(path.join(root, 'content/sections'))
.filter(f => f.endsWith('.json'))
.map(f => `content/sections/${f}`),
];
for (const rel of files) {
const abs = path.join(root, rel);
const parsed = JSON.parse(fs.readFileSync(abs, 'utf-8'));
fs.writeFileSync(abs, stringifyCanonical(parsed));
console.log(`${rel}`);
}
console.log('\nDone. All files canonicalized.');

View File

@@ -0,0 +1,41 @@
#!/usr/bin/env node
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, '..');
const { stringifyCanonical } = await import(path.join(root, 'shared/src/canonical-json.ts'));
const files = [
'site-context.json',
'content/events.json',
'config/sms-sites.json',
...fs.readdirSync(path.join(root, 'content/sections'))
.filter(f => f.endsWith('.json'))
.map(f => `content/sections/${f}`),
];
const errors = [];
console.log('Checking canonical JSON format...');
for (const rel of files) {
const abs = path.join(root, rel);
const raw = fs.readFileSync(abs, 'utf-8');
const parsed = JSON.parse(raw);
const canonical = stringifyCanonical(parsed);
if (raw === canonical) {
console.log(`${rel}`);
} else {
errors.push(rel);
}
}
if (errors.length > 0) {
console.error('\n✗ Non-canonical files (run `node scripts/canonicalize.js` to fix):');
errors.forEach(e => console.error(` ${e}`));
process.exit(1);
} else {
console.log('\n✓ All files are canonical.');
}

View File

@@ -0,0 +1,59 @@
#!/usr/bin/env node
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, '..');
// Dynamic import of shared schemas
const {
siteContextSchema,
sectionFileSchema,
eventsFileSchema,
smsSitesConfigSchema,
} = await import(path.join(root, 'shared/src/schemas/index.ts'));
const errors = [];
function validate(filePath, schema, label) {
const rel = path.relative(root, filePath);
try {
const raw = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
const result = schema.safeParse(raw);
if (!result.success) {
errors.push(`${rel}: ${result.error.issues.map(i => `${i.path.join('.')}: ${i.message}`).join('; ')}`);
} else {
console.log(`${rel}`);
}
} catch (e) {
errors.push(`${rel}: ${e.message}`);
}
}
console.log('Validating content...');
// site-context.json
validate(path.join(root, 'site-context.json'), siteContextSchema, 'site-context');
// content/events.json
validate(path.join(root, 'content/events.json'), eventsFileSchema, 'events');
// config/sms-sites.json
validate(path.join(root, 'config/sms-sites.json'), smsSitesConfigSchema, 'sms-sites');
// content/sections/*.json
const sectionsDir = path.join(root, 'content/sections');
if (fs.existsSync(sectionsDir)) {
for (const file of fs.readdirSync(sectionsDir).filter(f => f.endsWith('.json'))) {
validate(path.join(sectionsDir, file), sectionFileSchema, file);
}
}
if (errors.length > 0) {
console.error('\n✗ Validation failed:');
errors.forEach(e => console.error(` ${e}`));
process.exit(1);
} else {
console.log('\n✓ All content valid.');
}