@unrdf/kgn 5.0.1
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/LICENSE +21 -0
- package/README.md +210 -0
- package/package.json +90 -0
- package/src/MIGRATION_COMPLETE.md +186 -0
- package/src/PORT-MAP.md +302 -0
- package/src/base/filter-templates.js +479 -0
- package/src/base/index.js +92 -0
- package/src/base/injection-targets.js +583 -0
- package/src/base/macro-templates.js +298 -0
- package/src/base/macro-templates.js.bak +461 -0
- package/src/base/shacl-templates.js +617 -0
- package/src/base/template-base.js +388 -0
- package/src/core/attestor.js +381 -0
- package/src/core/filters.js +518 -0
- package/src/core/index.js +21 -0
- package/src/core/kgen-engine.js +372 -0
- package/src/core/parser.js +447 -0
- package/src/core/post-processor.js +313 -0
- package/src/core/renderer.js +469 -0
- package/src/doc-generator/cli.mjs +122 -0
- package/src/doc-generator/index.mjs +28 -0
- package/src/doc-generator/mdx-generator.mjs +71 -0
- package/src/doc-generator/nav-generator.mjs +136 -0
- package/src/doc-generator/parser.mjs +291 -0
- package/src/doc-generator/rdf-builder.mjs +306 -0
- package/src/doc-generator/scanner.mjs +189 -0
- package/src/engine/index.js +42 -0
- package/src/engine/pipeline.js +448 -0
- package/src/engine/renderer.js +604 -0
- package/src/engine/template-engine.js +566 -0
- package/src/filters/array.js +436 -0
- package/src/filters/data.js +479 -0
- package/src/filters/index.js +270 -0
- package/src/filters/rdf.js +264 -0
- package/src/filters/text.js +369 -0
- package/src/index.js +109 -0
- package/src/inheritance/index.js +40 -0
- package/src/injection/api.js +260 -0
- package/src/injection/atomic-writer.js +327 -0
- package/src/injection/constants.js +136 -0
- package/src/injection/idempotency-manager.js +295 -0
- package/src/injection/index.js +28 -0
- package/src/injection/injection-engine.js +378 -0
- package/src/injection/integration.js +339 -0
- package/src/injection/modes/index.js +341 -0
- package/src/injection/rollback-manager.js +373 -0
- package/src/injection/target-resolver.js +323 -0
- package/src/injection/tests/atomic-writer.test.js +382 -0
- package/src/injection/tests/injection-engine.test.js +611 -0
- package/src/injection/tests/integration.test.js +392 -0
- package/src/injection/tests/run-tests.js +283 -0
- package/src/injection/validation-engine.js +547 -0
- package/src/linter/determinism-linter.js +473 -0
- package/src/linter/determinism.js +410 -0
- package/src/linter/index.js +6 -0
- package/src/linter/test-doubles.js +475 -0
- package/src/parser/frontmatter.js +228 -0
- package/src/parser/variables.js +344 -0
- package/src/renderer/deterministic.js +245 -0
- package/src/renderer/index.js +6 -0
- package/src/templates/latex/academic-paper.njk +186 -0
- package/src/templates/latex/index.js +104 -0
- package/src/templates/nextjs/app-page.njk +66 -0
- package/src/templates/nextjs/index.js +80 -0
- package/src/templates/office/docx/document.njk +368 -0
- package/src/templates/office/index.js +79 -0
- package/src/templates/office/word-report.njk +129 -0
- package/src/utils/template-utils.js +426 -0
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template Utilities - High-level convenience functions
|
|
3
|
+
* Migrated from ~/unjucks with enhanced error handling and validation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { TemplateEngine } from '../engine/template-engine.js';
|
|
7
|
+
import { TemplateLinter } from '../linter/determinism.js';
|
|
8
|
+
import { VariableExtractor } from '../parser/variables.js';
|
|
9
|
+
import { FrontmatterParser } from '../parser/frontmatter.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Quick template rendering with default options
|
|
13
|
+
*/
|
|
14
|
+
export async function renderTemplate(templatePath, context = {}, options = {}) {
|
|
15
|
+
const engine = new TemplateEngine({
|
|
16
|
+
templatesDir: options.templatesDir,
|
|
17
|
+
deterministicMode: options.deterministicMode !== false,
|
|
18
|
+
strictMode: options.strictMode !== false,
|
|
19
|
+
...options.engineOptions
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
return await engine.render(templatePath, context, options);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Render template from string content
|
|
27
|
+
*/
|
|
28
|
+
export function renderString(templateString, context = {}, options = {}) {
|
|
29
|
+
const engine = new TemplateEngine({
|
|
30
|
+
deterministicMode: options.deterministicMode !== false,
|
|
31
|
+
strictMode: options.strictMode !== false,
|
|
32
|
+
...options.engineOptions
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
return engine.renderString(templateString, context, options);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Validate template for determinism and common issues
|
|
40
|
+
*/
|
|
41
|
+
export async function validateTemplate(templatePath, options = {}) {
|
|
42
|
+
try {
|
|
43
|
+
// Read template content
|
|
44
|
+
const fs = await import('fs/promises');
|
|
45
|
+
const path = await import('path');
|
|
46
|
+
|
|
47
|
+
const fullPath = options.templatesDir
|
|
48
|
+
? path.resolve(options.templatesDir, templatePath)
|
|
49
|
+
: path.resolve(templatePath);
|
|
50
|
+
|
|
51
|
+
const content = await fs.readFile(fullPath, 'utf8');
|
|
52
|
+
|
|
53
|
+
// Parse frontmatter
|
|
54
|
+
const parser = new FrontmatterParser();
|
|
55
|
+
const { frontmatter } = parser.parse(content);
|
|
56
|
+
|
|
57
|
+
// Lint template
|
|
58
|
+
const linter = new TemplateLinter({
|
|
59
|
+
strict: options.strict !== false,
|
|
60
|
+
customRules: options.customRules
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const lintResult = linter.lint(content, frontmatter);
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
success: true,
|
|
67
|
+
templatePath: fullPath,
|
|
68
|
+
...lintResult
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
} catch (error) {
|
|
72
|
+
return {
|
|
73
|
+
success: false,
|
|
74
|
+
error: error.message,
|
|
75
|
+
templatePath
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Extract variables from template
|
|
82
|
+
*/
|
|
83
|
+
export async function extractVariables(templatePath, options = {}) {
|
|
84
|
+
try {
|
|
85
|
+
// Read template content
|
|
86
|
+
const fs = await import('fs/promises');
|
|
87
|
+
const path = await import('path');
|
|
88
|
+
|
|
89
|
+
const fullPath = options.templatesDir
|
|
90
|
+
? path.resolve(options.templatesDir, templatePath)
|
|
91
|
+
: path.resolve(templatePath);
|
|
92
|
+
|
|
93
|
+
const content = await fs.readFile(fullPath, 'utf8');
|
|
94
|
+
|
|
95
|
+
// Parse frontmatter to get content without frontmatter
|
|
96
|
+
const parser = new FrontmatterParser();
|
|
97
|
+
const { frontmatter, content: templateContent } = parser.parse(content);
|
|
98
|
+
|
|
99
|
+
// Extract variables
|
|
100
|
+
const extractor = new VariableExtractor({
|
|
101
|
+
includeFilters: options.includeFilters !== false,
|
|
102
|
+
includeFunctions: options.includeFunctions !== false
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const extracted = extractor.extract(templateContent);
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
success: true,
|
|
109
|
+
templatePath: fullPath,
|
|
110
|
+
frontmatter,
|
|
111
|
+
...extracted
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
} catch (error) {
|
|
115
|
+
return {
|
|
116
|
+
success: false,
|
|
117
|
+
error: error.message,
|
|
118
|
+
templatePath
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Lint template for determinism issues
|
|
125
|
+
*/
|
|
126
|
+
export async function lintTemplate(templatePath, options = {}) {
|
|
127
|
+
try {
|
|
128
|
+
// Read template content
|
|
129
|
+
const fs = await import('fs/promises');
|
|
130
|
+
const path = await import('path');
|
|
131
|
+
|
|
132
|
+
const fullPath = options.templatesDir
|
|
133
|
+
? path.resolve(options.templatesDir, templatePath)
|
|
134
|
+
: path.resolve(templatePath);
|
|
135
|
+
|
|
136
|
+
const content = await fs.readFile(fullPath, 'utf8');
|
|
137
|
+
|
|
138
|
+
// Parse frontmatter
|
|
139
|
+
const parser = new FrontmatterParser();
|
|
140
|
+
const { frontmatter } = parser.parse(content);
|
|
141
|
+
|
|
142
|
+
// Lint template
|
|
143
|
+
const linter = new TemplateLinter({
|
|
144
|
+
strict: options.strict !== false,
|
|
145
|
+
warnOnly: options.warnOnly === true,
|
|
146
|
+
customRules: options.customRules || []
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
const result = linter.lint(content, frontmatter);
|
|
150
|
+
|
|
151
|
+
// Auto-fix if requested
|
|
152
|
+
if (options.autoFix) {
|
|
153
|
+
const fixResult = linter.autoFix(content);
|
|
154
|
+
result.autoFix = fixResult;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
success: true,
|
|
159
|
+
templatePath: fullPath,
|
|
160
|
+
...result
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
} catch (error) {
|
|
164
|
+
return {
|
|
165
|
+
success: false,
|
|
166
|
+
error: error.message,
|
|
167
|
+
templatePath
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Analyze template comprehensively
|
|
174
|
+
*/
|
|
175
|
+
export async function analyzeTemplate(templatePath, options = {}) {
|
|
176
|
+
const results = await Promise.all([
|
|
177
|
+
extractVariables(templatePath, options),
|
|
178
|
+
validateTemplate(templatePath, options),
|
|
179
|
+
lintTemplate(templatePath, options)
|
|
180
|
+
]);
|
|
181
|
+
|
|
182
|
+
const [variables, validation, linting] = results;
|
|
183
|
+
|
|
184
|
+
if (!variables.success || !validation.success || !linting.success) {
|
|
185
|
+
const errors = [
|
|
186
|
+
variables.error,
|
|
187
|
+
validation.error,
|
|
188
|
+
linting.error
|
|
189
|
+
].filter(Boolean);
|
|
190
|
+
|
|
191
|
+
return {
|
|
192
|
+
success: false,
|
|
193
|
+
error: `Analysis failed: ${errors.join(', ')}`,
|
|
194
|
+
templatePath
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return {
|
|
199
|
+
success: true,
|
|
200
|
+
templatePath: variables.templatePath,
|
|
201
|
+
analysis: {
|
|
202
|
+
variables: variables.variables,
|
|
203
|
+
filters: variables.filters,
|
|
204
|
+
functions: variables.functions,
|
|
205
|
+
complexity: variables.complexity,
|
|
206
|
+
frontmatter: variables.frontmatter,
|
|
207
|
+
deterministic: validation.deterministic,
|
|
208
|
+
score: validation.score,
|
|
209
|
+
issues: validation.issues,
|
|
210
|
+
warnings: validation.warnings,
|
|
211
|
+
suggestions: validation.suggestions,
|
|
212
|
+
linting: {
|
|
213
|
+
passesLint: linting.summary.passesLint,
|
|
214
|
+
totalIssues: linting.summary.totalIssues,
|
|
215
|
+
criticalIssues: linting.summary.criticalIssues
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
summary: {
|
|
219
|
+
variableCount: variables.variables.length,
|
|
220
|
+
filterCount: variables.filters.length,
|
|
221
|
+
determinismScore: validation.score,
|
|
222
|
+
isDeterministic: validation.deterministic,
|
|
223
|
+
hasIssues: validation.issues.length > 0,
|
|
224
|
+
complexity: variables.complexity
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Test template with sample data
|
|
231
|
+
*/
|
|
232
|
+
export async function testTemplate(templatePath, testData = {}, options = {}) {
|
|
233
|
+
try {
|
|
234
|
+
// Extract required variables
|
|
235
|
+
const variableResult = await extractVariables(templatePath, options);
|
|
236
|
+
if (!variableResult.success) {
|
|
237
|
+
return variableResult;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Check if test data provides required variables
|
|
241
|
+
const missingVars = variableResult.variables.filter(
|
|
242
|
+
varName => !(varName in testData) && !varName.startsWith('_')
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
if (missingVars.length > 0 && options.strict !== false) {
|
|
246
|
+
return {
|
|
247
|
+
success: false,
|
|
248
|
+
error: `Missing test data for variables: ${missingVars.join(', ')}`,
|
|
249
|
+
missingVariables: missingVars,
|
|
250
|
+
templatePath
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Render template with test data
|
|
255
|
+
const renderResult = await renderTemplate(templatePath, testData, {
|
|
256
|
+
...options,
|
|
257
|
+
validateVars: options.strict !== false
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
if (!renderResult.success) {
|
|
261
|
+
return renderResult;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Test determinism if requested
|
|
265
|
+
let determinismTest = null;
|
|
266
|
+
if (options.testDeterminism !== false) {
|
|
267
|
+
const engine = new TemplateEngine({
|
|
268
|
+
deterministicMode: true,
|
|
269
|
+
...options.engineOptions
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
const verification = await engine.deterministicRenderer.verifyDeterminism(
|
|
273
|
+
engine.env,
|
|
274
|
+
renderResult.content,
|
|
275
|
+
testData,
|
|
276
|
+
options.determinismIterations || 3
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
determinismTest = verification;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return {
|
|
283
|
+
success: true,
|
|
284
|
+
templatePath: renderResult.templatePath,
|
|
285
|
+
renderResult,
|
|
286
|
+
determinismTest,
|
|
287
|
+
testData,
|
|
288
|
+
missingVariables: missingVars
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
} catch (error) {
|
|
292
|
+
return {
|
|
293
|
+
success: false,
|
|
294
|
+
error: error.message,
|
|
295
|
+
templatePath
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Discover templates in directory
|
|
302
|
+
*/
|
|
303
|
+
export async function discoverTemplates(directory, options = {}) {
|
|
304
|
+
try {
|
|
305
|
+
const fs = await import('fs/promises');
|
|
306
|
+
const path = await import('path');
|
|
307
|
+
|
|
308
|
+
const templates = [];
|
|
309
|
+
const extensions = options.extensions || ['.njk', '.j2', '.html'];
|
|
310
|
+
|
|
311
|
+
async function scanDirectory(dir, relativePath = '') {
|
|
312
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
313
|
+
|
|
314
|
+
for (const entry of entries) {
|
|
315
|
+
if (entry.isDirectory() && !entry.name.startsWith('.')) {
|
|
316
|
+
if (options.recursive !== false) {
|
|
317
|
+
await scanDirectory(
|
|
318
|
+
path.join(dir, entry.name),
|
|
319
|
+
path.join(relativePath, entry.name)
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
} else if (entry.isFile()) {
|
|
323
|
+
const ext = path.extname(entry.name);
|
|
324
|
+
if (extensions.includes(ext)) {
|
|
325
|
+
const templatePath = path.join(relativePath, entry.name);
|
|
326
|
+
const fullPath = path.join(dir, entry.name);
|
|
327
|
+
|
|
328
|
+
const template = {
|
|
329
|
+
name: path.basename(entry.name, ext),
|
|
330
|
+
path: templatePath,
|
|
331
|
+
fullPath,
|
|
332
|
+
extension: ext,
|
|
333
|
+
directory: relativePath,
|
|
334
|
+
size: (await fs.stat(fullPath)).size,
|
|
335
|
+
modified: (await fs.stat(fullPath)).mtime.toISOString()
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
// Add analysis if requested
|
|
339
|
+
if (options.analyze) {
|
|
340
|
+
try {
|
|
341
|
+
const analysis = await analyzeTemplate(fullPath, options);
|
|
342
|
+
template.analysis = analysis.success ? analysis.summary : null;
|
|
343
|
+
template.analysisError = analysis.success ? null : analysis.error;
|
|
344
|
+
} catch (error) {
|
|
345
|
+
template.analysisError = error.message;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
templates.push(template);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
await scanDirectory(directory);
|
|
356
|
+
|
|
357
|
+
return {
|
|
358
|
+
success: true,
|
|
359
|
+
directory,
|
|
360
|
+
templates: templates.sort((a, b) => a.path.localeCompare(b.path)),
|
|
361
|
+
count: templates.length
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
} catch (error) {
|
|
365
|
+
return {
|
|
366
|
+
success: false,
|
|
367
|
+
error: error.message,
|
|
368
|
+
directory
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Batch process templates
|
|
375
|
+
*/
|
|
376
|
+
export async function batchProcess(templates, operation, options = {}) {
|
|
377
|
+
const results = [];
|
|
378
|
+
const concurrency = options.concurrency || 5;
|
|
379
|
+
|
|
380
|
+
// Process in batches for better performance
|
|
381
|
+
for (let i = 0; i < templates.length; i += concurrency) {
|
|
382
|
+
const batch = templates.slice(i, i + concurrency);
|
|
383
|
+
const batchPromises = batch.map(async (template) => {
|
|
384
|
+
try {
|
|
385
|
+
const result = await operation(template, options);
|
|
386
|
+
return {
|
|
387
|
+
template,
|
|
388
|
+
result,
|
|
389
|
+
success: true
|
|
390
|
+
};
|
|
391
|
+
} catch (error) {
|
|
392
|
+
return {
|
|
393
|
+
template,
|
|
394
|
+
error: error.message,
|
|
395
|
+
success: false
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
const batchResults = await Promise.all(batchPromises);
|
|
401
|
+
results.push(...batchResults);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
const successful = results.filter(r => r.success);
|
|
405
|
+
const failed = results.filter(r => !r.success);
|
|
406
|
+
|
|
407
|
+
return {
|
|
408
|
+
total: results.length,
|
|
409
|
+
successful: successful.length,
|
|
410
|
+
failed: failed.length,
|
|
411
|
+
results,
|
|
412
|
+
errors: failed.map(f => ({ template: f.template, error: f.error }))
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
export default {
|
|
417
|
+
renderTemplate,
|
|
418
|
+
renderString,
|
|
419
|
+
validateTemplate,
|
|
420
|
+
extractVariables,
|
|
421
|
+
lintTemplate,
|
|
422
|
+
analyzeTemplate,
|
|
423
|
+
testTemplate,
|
|
424
|
+
discoverTemplates,
|
|
425
|
+
batchProcess
|
|
426
|
+
};
|