@millstone/synapse-cli 0.1.0
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.
- package/README.md +135 -0
- package/bin/synapse.js +3 -0
- package/dist/commands/eject.d.ts +19 -0
- package/dist/commands/eject.d.ts.map +1 -0
- package/dist/commands/eject.js +146 -0
- package/dist/commands/eject.js.map +1 -0
- package/dist/commands/fetch-reference.d.ts +19 -0
- package/dist/commands/fetch-reference.d.ts.map +1 -0
- package/dist/commands/fetch-reference.js +93 -0
- package/dist/commands/fetch-reference.js.map +1 -0
- package/dist/commands/format.d.ts +26 -0
- package/dist/commands/format.d.ts.map +1 -0
- package/dist/commands/format.js +126 -0
- package/dist/commands/format.js.map +1 -0
- package/dist/commands/generate-pdf.d.ts +19 -0
- package/dist/commands/generate-pdf.d.ts.map +1 -0
- package/dist/commands/generate-pdf.js +140 -0
- package/dist/commands/generate-pdf.js.map +1 -0
- package/dist/commands/index.d.ts +17 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +26 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/init.d.ts +58 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +234 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/migrate.d.ts +29 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +297 -0
- package/dist/commands/migrate.js.map +1 -0
- package/dist/commands/scaffold.d.ts +24 -0
- package/dist/commands/scaffold.d.ts.map +1 -0
- package/dist/commands/scaffold.js +244 -0
- package/dist/commands/scaffold.js.map +1 -0
- package/dist/commands/update.d.ts +25 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +253 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/validate.d.ts +37 -0
- package/dist/commands/validate.d.ts.map +1 -0
- package/dist/commands/validate.js +526 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +277 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/bodyRules.d.ts +70 -0
- package/dist/lib/bodyRules.d.ts.map +1 -0
- package/dist/lib/bodyRules.js +711 -0
- package/dist/lib/bodyRules.js.map +1 -0
- package/dist/lib/config.d.ts +49 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +91 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/git.d.ts +99 -0
- package/dist/lib/git.d.ts.map +1 -0
- package/dist/lib/git.js +266 -0
- package/dist/lib/git.js.map +1 -0
- package/dist/lib/graph.d.ts +6 -0
- package/dist/lib/graph.d.ts.map +1 -0
- package/dist/lib/graph.js +6 -0
- package/dist/lib/graph.js.map +1 -0
- package/dist/lib/homepage.d.ts +10 -0
- package/dist/lib/homepage.d.ts.map +1 -0
- package/dist/lib/homepage.js +172 -0
- package/dist/lib/homepage.js.map +1 -0
- package/dist/lib/markdown.d.ts +107 -0
- package/dist/lib/markdown.d.ts.map +1 -0
- package/dist/lib/markdown.js +318 -0
- package/dist/lib/markdown.js.map +1 -0
- package/dist/lib/mode-detection.d.ts +10 -0
- package/dist/lib/mode-detection.d.ts.map +1 -0
- package/dist/lib/mode-detection.js +29 -0
- package/dist/lib/mode-detection.js.map +1 -0
- package/dist/lib/naming.d.ts +47 -0
- package/dist/lib/naming.d.ts.map +1 -0
- package/dist/lib/naming.js +403 -0
- package/dist/lib/naming.js.map +1 -0
- package/dist/lib/schemas.d.ts +38 -0
- package/dist/lib/schemas.d.ts.map +1 -0
- package/dist/lib/schemas.js +248 -0
- package/dist/lib/schemas.js.map +1 -0
- package/dist/lib/templateLint.d.ts +21 -0
- package/dist/lib/templateLint.d.ts.map +1 -0
- package/dist/lib/templateLint.js +243 -0
- package/dist/lib/templateLint.js.map +1 -0
- package/dist/lib/templates.d.ts +53 -0
- package/dist/lib/templates.d.ts.map +1 -0
- package/dist/lib/templates.js +128 -0
- package/dist/lib/templates.js.map +1 -0
- package/dist/lib/tracking.d.ts +52 -0
- package/dist/lib/tracking.d.ts.map +1 -0
- package/dist/lib/tracking.js +135 -0
- package/dist/lib/tracking.js.map +1 -0
- package/dist/lib/types.generated.d.ts +54 -0
- package/dist/lib/types.generated.d.ts.map +1 -0
- package/dist/lib/types.generated.js +144 -0
- package/dist/lib/types.generated.js.map +1 -0
- package/dist/lib/validate-plugins.d.ts +22 -0
- package/dist/lib/validate-plugins.d.ts.map +1 -0
- package/dist/lib/validate-plugins.js +851 -0
- package/dist/lib/validate-plugins.js.map +1 -0
- package/package.json +85 -0
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import Ajv from 'ajv';
|
|
2
|
+
import addFormats from 'ajv-formats';
|
|
3
|
+
import fsExtra from 'fs-extra';
|
|
4
|
+
const fs = fsExtra;
|
|
5
|
+
import * as path from 'path';
|
|
6
|
+
import { createRequire } from 'module';
|
|
7
|
+
import { DOC_TYPES } from './types.generated.js';
|
|
8
|
+
export { isDocType, DOC_TYPES } from './types.generated.js';
|
|
9
|
+
// Cache for compiled schemas to improve performance
|
|
10
|
+
const schemaCache = new Map();
|
|
11
|
+
/**
|
|
12
|
+
* Resolve the frontmatter schema directory using a cascade:
|
|
13
|
+
* 1. Local project override: {projectRoot}/schemas/frontmatter/
|
|
14
|
+
* 2. @synapse/schemas npm package
|
|
15
|
+
* 3. Error with helpful message
|
|
16
|
+
*/
|
|
17
|
+
function getDefaultSchemaDir() {
|
|
18
|
+
// Priority 1: Local project override
|
|
19
|
+
const localPaths = [
|
|
20
|
+
path.resolve(process.cwd(), 'schemas/frontmatter'),
|
|
21
|
+
path.resolve(process.cwd(), '../schemas/frontmatter'),
|
|
22
|
+
path.resolve(process.cwd(), '../../schemas/frontmatter'),
|
|
23
|
+
];
|
|
24
|
+
for (const schemaPath of localPaths) {
|
|
25
|
+
if (fs.existsSync(schemaPath)) {
|
|
26
|
+
try {
|
|
27
|
+
const files = fs.readdirSync(schemaPath);
|
|
28
|
+
const hasSchemas = files.some(f => f.endsWith('.schema.json'));
|
|
29
|
+
if (hasSchemas) {
|
|
30
|
+
return schemaPath;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// Priority 2: @synapse/schemas package
|
|
39
|
+
try {
|
|
40
|
+
const require = createRequire(import.meta.url);
|
|
41
|
+
const pkgPath = require.resolve('@synapse/schemas/package.json');
|
|
42
|
+
const pkgDir = path.dirname(pkgPath);
|
|
43
|
+
const frontmatterDir = path.join(pkgDir, 'frontmatter');
|
|
44
|
+
if (fs.existsSync(frontmatterDir)) {
|
|
45
|
+
return frontmatterDir;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// Package not installed
|
|
50
|
+
}
|
|
51
|
+
// Priority 3: Error with helpful message
|
|
52
|
+
throw new Error('Could not find frontmatter schemas. Either place schemas in {projectRoot}/schemas/frontmatter/ ' +
|
|
53
|
+
'or install the @synapse/schemas package: npm install @synapse/schemas');
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Load a schema for a specific document type
|
|
57
|
+
* Resolves $ref references to base.schema.json automatically
|
|
58
|
+
*/
|
|
59
|
+
export async function loadSchema(type, dir) {
|
|
60
|
+
const schemaDir = dir || getDefaultSchemaDir();
|
|
61
|
+
const schemaPath = path.join(schemaDir, `${type}.schema.json`);
|
|
62
|
+
const cacheKey = `${schemaDir}:${type}`;
|
|
63
|
+
// Return cached schema if available
|
|
64
|
+
if (schemaCache.has(cacheKey)) {
|
|
65
|
+
return schemaCache.get(cacheKey);
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
// Read the schema file
|
|
69
|
+
const schemaContent = await fs.readFile(schemaPath, 'utf-8');
|
|
70
|
+
const schema = JSON.parse(schemaContent);
|
|
71
|
+
// If schema has allOf with $ref to base.schema.json, resolve it
|
|
72
|
+
if (schema.allOf && Array.isArray(schema.allOf)) {
|
|
73
|
+
const resolvedAllOf = await Promise.all(schema.allOf.map(async (item) => {
|
|
74
|
+
if (item.$ref === 'base.schema.json') {
|
|
75
|
+
const basePath = path.join(schemaDir, 'base.schema.json');
|
|
76
|
+
const baseContent = await fs.readFile(basePath, 'utf-8');
|
|
77
|
+
return JSON.parse(baseContent);
|
|
78
|
+
}
|
|
79
|
+
return item;
|
|
80
|
+
}));
|
|
81
|
+
// Merge base schema properties with type-specific schema
|
|
82
|
+
const mergedSchema = {
|
|
83
|
+
...schema,
|
|
84
|
+
properties: {
|
|
85
|
+
...resolvedAllOf[0]?.properties || {},
|
|
86
|
+
...schema.properties || {}
|
|
87
|
+
},
|
|
88
|
+
required: [
|
|
89
|
+
...(resolvedAllOf[0]?.required || []),
|
|
90
|
+
...(schema.required || [])
|
|
91
|
+
].filter((v, i, a) => a.indexOf(v) === i) // Remove duplicates
|
|
92
|
+
};
|
|
93
|
+
// Remove allOf since we've resolved it
|
|
94
|
+
delete mergedSchema.allOf;
|
|
95
|
+
schemaCache.set(cacheKey, mergedSchema);
|
|
96
|
+
return mergedSchema;
|
|
97
|
+
}
|
|
98
|
+
// Cache and return schema as-is if no allOf
|
|
99
|
+
schemaCache.set(cacheKey, schema);
|
|
100
|
+
return schema;
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
if (error.code === 'ENOENT') {
|
|
104
|
+
throw new Error(`Schema file not found for type '${type}' at path: ${schemaPath}`);
|
|
105
|
+
}
|
|
106
|
+
throw new Error(`Failed to load schema for type '${type}': ${error.message}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* List all available schema types in the schema directory
|
|
111
|
+
*/
|
|
112
|
+
export async function listSchemas(dir) {
|
|
113
|
+
const schemaDir = dir || getDefaultSchemaDir();
|
|
114
|
+
try {
|
|
115
|
+
const files = await fs.readdir(schemaDir);
|
|
116
|
+
// Filter for .schema.json files and extract type names
|
|
117
|
+
const schemaTypes = files
|
|
118
|
+
.filter(file => file.endsWith('.schema.json') && file !== 'base.schema.json')
|
|
119
|
+
.map(file => file.replace('.schema.json', ''))
|
|
120
|
+
.sort();
|
|
121
|
+
return schemaTypes;
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
if (error.code === 'ENOENT') {
|
|
125
|
+
throw new Error(`Schema directory not found: ${schemaDir}`);
|
|
126
|
+
}
|
|
127
|
+
throw new Error(`Failed to list schemas: ${error.message}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Validate frontmatter data against a JSON schema
|
|
132
|
+
*/
|
|
133
|
+
export function validateFrontmatter(data, schema) {
|
|
134
|
+
// Create a temporary Ajv instance for validation
|
|
135
|
+
// Remove the $schema property to avoid draft version conflicts
|
|
136
|
+
const cleanSchema = { ...schema };
|
|
137
|
+
delete cleanSchema.$schema;
|
|
138
|
+
delete cleanSchema.$id;
|
|
139
|
+
const ajv = new Ajv({
|
|
140
|
+
allErrors: true,
|
|
141
|
+
strict: false,
|
|
142
|
+
validateSchema: false // Don't validate the schema itself
|
|
143
|
+
});
|
|
144
|
+
// Add format validation support
|
|
145
|
+
addFormats(ajv);
|
|
146
|
+
try {
|
|
147
|
+
const validate = ajv.compile(cleanSchema);
|
|
148
|
+
const valid = validate(data);
|
|
149
|
+
if (valid) {
|
|
150
|
+
return { valid: true };
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
// Format errors to be more human-readable
|
|
154
|
+
const formattedErrors = (validate.errors || []).map(error => {
|
|
155
|
+
const path = error.instancePath || '/';
|
|
156
|
+
let message = error.message || 'validation error';
|
|
157
|
+
// Add more context for specific error types
|
|
158
|
+
if (error.keyword === 'required') {
|
|
159
|
+
const missingProp = error.params.missingProperty;
|
|
160
|
+
message = `missing required property '${missingProp}'`;
|
|
161
|
+
}
|
|
162
|
+
else if (error.keyword === 'type') {
|
|
163
|
+
const expectedType = error.params.type;
|
|
164
|
+
message = `must be ${expectedType}`;
|
|
165
|
+
}
|
|
166
|
+
else if (error.keyword === 'const') {
|
|
167
|
+
const expectedValue = error.params.allowedValue;
|
|
168
|
+
message = `must be equal to constant '${expectedValue}'`;
|
|
169
|
+
}
|
|
170
|
+
return { path, message };
|
|
171
|
+
});
|
|
172
|
+
return {
|
|
173
|
+
valid: false,
|
|
174
|
+
errors: formattedErrors
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
// If compilation fails, return as validation error
|
|
180
|
+
return {
|
|
181
|
+
valid: false,
|
|
182
|
+
errors: [{
|
|
183
|
+
path: '/',
|
|
184
|
+
message: `Schema compilation failed: ${error.message}`
|
|
185
|
+
}]
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Validate that all expected document types have schemas
|
|
191
|
+
* Returns an object with validation results
|
|
192
|
+
*/
|
|
193
|
+
export async function validateSchemasCoverage(dir) {
|
|
194
|
+
// Use generated DOC_TYPES instead of hardcoded list
|
|
195
|
+
const expectedTypes = [...DOC_TYPES];
|
|
196
|
+
const errors = [];
|
|
197
|
+
try {
|
|
198
|
+
const foundTypes = await listSchemas(dir);
|
|
199
|
+
// Check for missing schemas
|
|
200
|
+
const missing = expectedTypes.filter(type => !foundTypes.includes(type));
|
|
201
|
+
// Check for extra schemas (not in expected list)
|
|
202
|
+
const extra = foundTypes.filter(type => !expectedTypes.includes(type));
|
|
203
|
+
// Validate each schema can be loaded and compiled
|
|
204
|
+
for (const type of expectedTypes) {
|
|
205
|
+
if (foundTypes.includes(type)) {
|
|
206
|
+
try {
|
|
207
|
+
const schema = await loadSchema(type, dir);
|
|
208
|
+
// Basic validation that schema has required structure
|
|
209
|
+
if (!schema.properties) {
|
|
210
|
+
errors.push(`Schema for '${type}' is missing 'properties' field`);
|
|
211
|
+
}
|
|
212
|
+
if (!schema.properties.type) {
|
|
213
|
+
errors.push(`Schema for '${type}' is missing 'type' property definition`);
|
|
214
|
+
}
|
|
215
|
+
else if (schema.properties.type.const !== type) {
|
|
216
|
+
errors.push(`Schema for '${type}' has mismatched type const: expected '${type}', got '${schema.properties.type.const}'`);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
catch (error) {
|
|
220
|
+
errors.push(`Failed to load schema for '${type}': ${error.message}`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return {
|
|
225
|
+
success: missing.length === 0 && errors.length === 0,
|
|
226
|
+
coverage: {
|
|
227
|
+
expected: expectedTypes,
|
|
228
|
+
found: foundTypes,
|
|
229
|
+
missing,
|
|
230
|
+
extra
|
|
231
|
+
},
|
|
232
|
+
errors
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
catch (error) {
|
|
236
|
+
return {
|
|
237
|
+
success: false,
|
|
238
|
+
coverage: {
|
|
239
|
+
expected: expectedTypes,
|
|
240
|
+
found: [],
|
|
241
|
+
missing: expectedTypes,
|
|
242
|
+
extra: []
|
|
243
|
+
},
|
|
244
|
+
errors: [`Failed to validate schema coverage: ${error.message}`]
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
//# sourceMappingURL=schemas.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemas.js","sourceRoot":"","sources":["../../src/lib/schemas.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type JSONSchema } from './schemas.js';
|
|
2
|
+
export interface TemplateLintIssue {
|
|
3
|
+
type: 'error' | 'warning';
|
|
4
|
+
message: string;
|
|
5
|
+
field?: string;
|
|
6
|
+
line?: number;
|
|
7
|
+
}
|
|
8
|
+
export interface TemplateLintResult {
|
|
9
|
+
templatePath: string;
|
|
10
|
+
errors: TemplateLintIssue[];
|
|
11
|
+
warnings: TemplateLintIssue[];
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Analyzes a template file and checks it against its schema
|
|
15
|
+
*/
|
|
16
|
+
export declare function analyzeTemplate(templatePath: string, schema?: JSONSchema, schemaDir?: string): Promise<TemplateLintResult>;
|
|
17
|
+
/**
|
|
18
|
+
* Lint all templates in a directory
|
|
19
|
+
*/
|
|
20
|
+
export declare function lintTemplatesDirectory(templatesDir: string, schemaDir?: string): Promise<TemplateLintResult[]>;
|
|
21
|
+
//# sourceMappingURL=templateLint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"templateLint.d.ts","sourceRoot":"","sources":["../../src/lib/templateLint.ts"],"names":[],"mappings":"AAGA,OAAO,EAAc,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAO3D,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,OAAO,GAAG,SAAS,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAC5B,QAAQ,EAAE,iBAAiB,EAAE,CAAC;CAC/B;AA0ID;;GAEG;AACH,wBAAsB,eAAe,CACnC,YAAY,EAAE,MAAM,EACpB,MAAM,CAAC,EAAE,UAAU,EACnB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,kBAAkB,CAAC,CA8G7B;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CAC1C,YAAY,EAAE,MAAM,EACpB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAkB/B"}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import fsExtra from 'fs-extra';
|
|
2
|
+
const fs = fsExtra;
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import { loadSchema } from './schemas.js';
|
|
5
|
+
import { isDocType } from './types.generated.js';
|
|
6
|
+
import { extractFrontmatter, extractPlaceholders, parseFrontmatterKeys } from './markdown.js';
|
|
7
|
+
// Default Handlebars helpers that are always available
|
|
8
|
+
const DEFAULT_HELPERS = ['now', 'each', 'unless', 'if', 'with'];
|
|
9
|
+
// Default Handlebars helpers that are always available
|
|
10
|
+
/**
|
|
11
|
+
* Gets the document type from the template path or frontmatter
|
|
12
|
+
*/
|
|
13
|
+
function getDocTypeFromTemplate(templatePath, frontmatter) {
|
|
14
|
+
// Try to extract from frontmatter first
|
|
15
|
+
const typeMatch = frontmatter.match(/^type:\s*([a-zA-Z_]+)/m);
|
|
16
|
+
if (typeMatch && isDocType(typeMatch[1])) {
|
|
17
|
+
return typeMatch[1];
|
|
18
|
+
}
|
|
19
|
+
// Try to infer from filename
|
|
20
|
+
const basename = path.basename(templatePath, '.hbs').toLowerCase();
|
|
21
|
+
if (isDocType(basename)) {
|
|
22
|
+
return basename;
|
|
23
|
+
}
|
|
24
|
+
// Map common variations to doc types
|
|
25
|
+
const typeMap = {
|
|
26
|
+
'adr': 'adr',
|
|
27
|
+
'capability': 'capability',
|
|
28
|
+
'policy': 'policy',
|
|
29
|
+
'prd': 'prd',
|
|
30
|
+
'process': 'process',
|
|
31
|
+
'runbook': 'runbook',
|
|
32
|
+
'sop': 'sop',
|
|
33
|
+
'standard': 'standard',
|
|
34
|
+
'system': 'system',
|
|
35
|
+
'tdd': 'tdd'
|
|
36
|
+
};
|
|
37
|
+
return typeMap[basename] || null;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Gets required fields from the merged schema
|
|
41
|
+
*/
|
|
42
|
+
async function getRequiredFields(type, schemaDir) {
|
|
43
|
+
const requiredFields = new Set();
|
|
44
|
+
// For custom schema directories (like in tests), bypass loadSchema to avoid caching issues
|
|
45
|
+
if (schemaDir && !schemaDir.includes('content/schemas')) {
|
|
46
|
+
try {
|
|
47
|
+
// Load base schema
|
|
48
|
+
const baseSchemaPath = path.join(schemaDir, 'base.schema.json');
|
|
49
|
+
if (await fs.pathExists(baseSchemaPath)) {
|
|
50
|
+
const baseSchema = await fs.readJSON(baseSchemaPath);
|
|
51
|
+
if (baseSchema.required && Array.isArray(baseSchema.required)) {
|
|
52
|
+
baseSchema.required.forEach((field) => requiredFields.add(field));
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// Load type-specific schema
|
|
56
|
+
const typeSchemaPath = path.join(schemaDir, `${type}.schema.json`);
|
|
57
|
+
if (await fs.pathExists(typeSchemaPath)) {
|
|
58
|
+
const typeSchema = await fs.readJSON(typeSchemaPath);
|
|
59
|
+
if (typeSchema.required && Array.isArray(typeSchema.required)) {
|
|
60
|
+
typeSchema.required.forEach((field) => requiredFields.add(field));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
// Schema loading failed
|
|
66
|
+
}
|
|
67
|
+
return requiredFields;
|
|
68
|
+
}
|
|
69
|
+
// For production schemas, use the cached loadSchema function
|
|
70
|
+
try {
|
|
71
|
+
const schema = await loadSchema(type, schemaDir);
|
|
72
|
+
if (schema.required && Array.isArray(schema.required)) {
|
|
73
|
+
schema.required.forEach((field) => requiredFields.add(field));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
// Schema might not exist yet
|
|
78
|
+
}
|
|
79
|
+
return requiredFields;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Gets all valid properties from the merged schema
|
|
83
|
+
*/
|
|
84
|
+
async function getAllSchemaProperties(type, schemaDir) {
|
|
85
|
+
const allProperties = new Set();
|
|
86
|
+
// For custom schema directories (like in tests), bypass loadSchema to avoid caching issues
|
|
87
|
+
if (schemaDir && !schemaDir.includes('content/schemas')) {
|
|
88
|
+
try {
|
|
89
|
+
// Load base schema properties
|
|
90
|
+
const baseSchemaPath = path.join(schemaDir, 'base.schema.json');
|
|
91
|
+
if (await fs.pathExists(baseSchemaPath)) {
|
|
92
|
+
const baseSchema = await fs.readJSON(baseSchemaPath);
|
|
93
|
+
if (baseSchema.properties) {
|
|
94
|
+
Object.keys(baseSchema.properties).forEach(prop => allProperties.add(prop));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// Load type-specific schema properties
|
|
98
|
+
const typeSchemaPath = path.join(schemaDir, `${type}.schema.json`);
|
|
99
|
+
if (await fs.pathExists(typeSchemaPath)) {
|
|
100
|
+
const typeSchema = await fs.readJSON(typeSchemaPath);
|
|
101
|
+
if (typeSchema.properties) {
|
|
102
|
+
Object.keys(typeSchema.properties).forEach(prop => allProperties.add(prop));
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
// Schema loading failed
|
|
108
|
+
}
|
|
109
|
+
return allProperties;
|
|
110
|
+
}
|
|
111
|
+
// For production schemas, use the cached loadSchema function
|
|
112
|
+
try {
|
|
113
|
+
const schema = await loadSchema(type, schemaDir);
|
|
114
|
+
if (schema.properties) {
|
|
115
|
+
Object.keys(schema.properties).forEach(prop => allProperties.add(prop));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
// Schema might not exist yet
|
|
120
|
+
}
|
|
121
|
+
return allProperties;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Analyzes a template file and checks it against its schema
|
|
125
|
+
*/
|
|
126
|
+
export async function analyzeTemplate(templatePath, schema, schemaDir) {
|
|
127
|
+
const errors = [];
|
|
128
|
+
const warnings = [];
|
|
129
|
+
try {
|
|
130
|
+
// Read template content
|
|
131
|
+
const templateContent = await fs.readFile(templatePath, 'utf-8');
|
|
132
|
+
// Extract frontmatter
|
|
133
|
+
const { frontmatter, body, startLine } = extractFrontmatter(templateContent);
|
|
134
|
+
if (!frontmatter) {
|
|
135
|
+
errors.push({
|
|
136
|
+
type: 'error',
|
|
137
|
+
message: 'Template missing frontmatter block'
|
|
138
|
+
});
|
|
139
|
+
return { templatePath, errors, warnings };
|
|
140
|
+
}
|
|
141
|
+
// Get document type
|
|
142
|
+
const docType = getDocTypeFromTemplate(templatePath, frontmatter);
|
|
143
|
+
if (!docType) {
|
|
144
|
+
errors.push({
|
|
145
|
+
type: 'error',
|
|
146
|
+
message: 'Unable to determine document type from template'
|
|
147
|
+
});
|
|
148
|
+
return { templatePath, errors, warnings };
|
|
149
|
+
}
|
|
150
|
+
// Check for type const in frontmatter
|
|
151
|
+
const typePattern = new RegExp(`^type:\\s*${docType}\\s*$`, 'm');
|
|
152
|
+
if (!typePattern.test(frontmatter)) {
|
|
153
|
+
errors.push({
|
|
154
|
+
type: 'error',
|
|
155
|
+
message: `Frontmatter missing type constant field with value '${docType}'`,
|
|
156
|
+
field: 'type',
|
|
157
|
+
line: startLine
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
// Parse frontmatter keys
|
|
161
|
+
const frontmatterKeys = parseFrontmatterKeys(frontmatter);
|
|
162
|
+
// Extract all placeholders from entire template
|
|
163
|
+
const allPlaceholders = new Set([
|
|
164
|
+
...extractPlaceholders(frontmatter),
|
|
165
|
+
...extractPlaceholders(body)
|
|
166
|
+
]);
|
|
167
|
+
// Get required fields from schema
|
|
168
|
+
const requiredFields = await getRequiredFields(docType, schemaDir);
|
|
169
|
+
// Check for missing required fields
|
|
170
|
+
for (const requiredField of requiredFields) {
|
|
171
|
+
// Skip 'type' as it should be a constant
|
|
172
|
+
if (requiredField === 'type')
|
|
173
|
+
continue;
|
|
174
|
+
// Check if field is in frontmatter or referenced as placeholder
|
|
175
|
+
if (!frontmatterKeys.has(requiredField) && !allPlaceholders.has(requiredField)) {
|
|
176
|
+
// Special case: 'now' helper can satisfy created/updated fields
|
|
177
|
+
if ((requiredField === 'created' || requiredField === 'updated') &&
|
|
178
|
+
allPlaceholders.has('now')) {
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
warnings.push({
|
|
182
|
+
type: 'warning',
|
|
183
|
+
message: `Required schema property '${requiredField}' is neither set nor referenced in template`,
|
|
184
|
+
field: requiredField
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
// Get all valid schema properties
|
|
189
|
+
const allSchemaProperties = await getAllSchemaProperties(docType, schemaDir);
|
|
190
|
+
// Check for unknown placeholders
|
|
191
|
+
for (const placeholder of allPlaceholders) {
|
|
192
|
+
// Skip if it's a known helper
|
|
193
|
+
if (DEFAULT_HELPERS.includes(placeholder))
|
|
194
|
+
continue;
|
|
195
|
+
// Skip @-prefixed variables (Handlebars built-ins like @last, @index)
|
|
196
|
+
if (placeholder.startsWith('@'))
|
|
197
|
+
continue;
|
|
198
|
+
// Skip 'this' keyword
|
|
199
|
+
if (placeholder === 'this')
|
|
200
|
+
continue;
|
|
201
|
+
// Check if it's a valid schema property
|
|
202
|
+
if (!allSchemaProperties.has(placeholder)) {
|
|
203
|
+
warnings.push({
|
|
204
|
+
type: 'warning',
|
|
205
|
+
message: `Unknown placeholder '${placeholder}' not found in schema properties or registered helpers`,
|
|
206
|
+
field: placeholder
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
errors.push({
|
|
213
|
+
type: 'error',
|
|
214
|
+
message: `Failed to analyze template: ${error instanceof Error ? error.message : String(error)}`
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
return {
|
|
218
|
+
templatePath,
|
|
219
|
+
errors,
|
|
220
|
+
warnings
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Lint all templates in a directory
|
|
225
|
+
*/
|
|
226
|
+
export async function lintTemplatesDirectory(templatesDir, schemaDir) {
|
|
227
|
+
const results = [];
|
|
228
|
+
try {
|
|
229
|
+
const templateFiles = await fs.readdir(templatesDir);
|
|
230
|
+
for (const file of templateFiles) {
|
|
231
|
+
if (file.endsWith('.hbs')) {
|
|
232
|
+
const templatePath = path.join(templatesDir, file);
|
|
233
|
+
const result = await analyzeTemplate(templatePath, undefined, schemaDir);
|
|
234
|
+
results.push(result);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
catch (error) {
|
|
239
|
+
// Directory might not exist
|
|
240
|
+
}
|
|
241
|
+
return results;
|
|
242
|
+
}
|
|
243
|
+
//# sourceMappingURL=templateLint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"templateLint.js","sourceRoot":"","sources":["../../src/lib/templateLint.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import Handlebars from 'handlebars';
|
|
2
|
+
/**
|
|
3
|
+
* Register custom Handlebars helpers
|
|
4
|
+
*/
|
|
5
|
+
export declare function registerHelpers(helpers: Record<string, Handlebars.HelperDelegate>): void;
|
|
6
|
+
/**
|
|
7
|
+
* Register default helpers for Synapse templates
|
|
8
|
+
*/
|
|
9
|
+
export declare function registerDefaultHelpers(): void;
|
|
10
|
+
/**
|
|
11
|
+
* Compile a single Handlebars template from file path
|
|
12
|
+
*/
|
|
13
|
+
export declare function compileTemplate(templatePath: string): Promise<HandlebarsTemplateDelegate>;
|
|
14
|
+
/**
|
|
15
|
+
* Render a template with provided data
|
|
16
|
+
*/
|
|
17
|
+
export declare function renderTemplate(templatePath: string, data: Record<string, any>): Promise<string>;
|
|
18
|
+
/**
|
|
19
|
+
* Discover all .hbs template files in a directory
|
|
20
|
+
*/
|
|
21
|
+
export declare function discoverTemplates(baseDir: string): Promise<string[]>;
|
|
22
|
+
/**
|
|
23
|
+
* Compile and validate all templates, returning a report
|
|
24
|
+
*/
|
|
25
|
+
export declare function compileAllTemplates(templateDir: string): Promise<{
|
|
26
|
+
success: boolean;
|
|
27
|
+
templates: Array<{
|
|
28
|
+
path: string;
|
|
29
|
+
name: string;
|
|
30
|
+
compiled: boolean;
|
|
31
|
+
error?: string;
|
|
32
|
+
}>;
|
|
33
|
+
}>;
|
|
34
|
+
/**
|
|
35
|
+
* Get template metadata including required fields from analysis
|
|
36
|
+
*/
|
|
37
|
+
export declare function getTemplateMetadata(templatePath: string): Promise<{
|
|
38
|
+
name: string;
|
|
39
|
+
path: string;
|
|
40
|
+
variables: string[];
|
|
41
|
+
helpers: string[];
|
|
42
|
+
}>;
|
|
43
|
+
declare const _default: {
|
|
44
|
+
compileTemplate: typeof compileTemplate;
|
|
45
|
+
renderTemplate: typeof renderTemplate;
|
|
46
|
+
registerHelpers: typeof registerHelpers;
|
|
47
|
+
registerDefaultHelpers: typeof registerDefaultHelpers;
|
|
48
|
+
discoverTemplates: typeof discoverTemplates;
|
|
49
|
+
compileAllTemplates: typeof compileAllTemplates;
|
|
50
|
+
getTemplateMetadata: typeof getTemplateMetadata;
|
|
51
|
+
};
|
|
52
|
+
export default _default;
|
|
53
|
+
//# sourceMappingURL=templates.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../../src/lib/templates.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,YAAY,CAAC;AASpC;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,cAAc,CAAC,GAAG,IAAI,CAIxF;AAED;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,IAAI,CAoB7C;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,0BAA0B,CAAC,CAO/F;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GACxB,OAAO,CAAC,MAAM,CAAC,CAGjB;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAO1E;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC;IACT,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,KAAK,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,OAAO,CAAC;QAClB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC,CAAC;CACJ,CAAC,CA4BD;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC;IACvE,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,CAAC,CAwBD;;;;;;;;;;AAED,wBAQE"}
|