@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,583 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KGEN Injection Target Templates - Code injection patterns
|
|
3
|
+
*
|
|
4
|
+
* Provides templates for creating injection targets in existing codebases
|
|
5
|
+
* Supports marker-based, semantic-based, and position-based injection strategies
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export class KGenInjectionTargets {
|
|
9
|
+
constructor(options = {}) {
|
|
10
|
+
this.options = {
|
|
11
|
+
deterministicMode: options.deterministicMode !== false,
|
|
12
|
+
staticBuildTime: options.staticBuildTime || '2024-01-01T00:00:00.000Z',
|
|
13
|
+
namespace: options.namespace || 'kgen',
|
|
14
|
+
...options
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
this.targetTemplates = new Map();
|
|
18
|
+
this.initializeTargetTemplates();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Initialize built-in injection target templates
|
|
23
|
+
*/
|
|
24
|
+
initializeTargetTemplates() {
|
|
25
|
+
// Marker-based injection target
|
|
26
|
+
this.registerTargetTemplate('marker_target', {
|
|
27
|
+
name: 'MarkerBasedTarget',
|
|
28
|
+
description: 'Injection target using comment markers',
|
|
29
|
+
pattern: {
|
|
30
|
+
startMarker: '{{ startComment | default("// KGEN:START") }} {{ markerName }}',
|
|
31
|
+
endMarker: '{{ endComment | default("// KGEN:END") }} {{ markerName }}',
|
|
32
|
+
content: '{{ injectionContent }}'
|
|
33
|
+
},
|
|
34
|
+
template: `{{ startComment | default("// KGEN:START") }} {{ markerName }}
|
|
35
|
+
{{ injectionContent | indent(indentLevel | default(0)) }}
|
|
36
|
+
{{ endComment | default("// KGEN:END") }} {{ markerName }}`,
|
|
37
|
+
detection: {
|
|
38
|
+
regex: '{{ startComment | escape_regex }} {{ markerName | escape_regex }}[\\s\\S]*?{{ endComment | escape_regex }} {{ markerName | escape_regex }}',
|
|
39
|
+
flags: 'g'
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// Function injection target
|
|
44
|
+
this.registerTargetTemplate('function_target', {
|
|
45
|
+
name: 'FunctionInjectionTarget',
|
|
46
|
+
description: 'Inject code at specific points in functions',
|
|
47
|
+
pattern: {
|
|
48
|
+
functionStart: 'function {{ functionName }}({{ parameters | default("") }}) {',
|
|
49
|
+
functionEnd: '}',
|
|
50
|
+
injectionPoint: '{{ injectionPoint | default("start") }}' // start, end, before-return
|
|
51
|
+
},
|
|
52
|
+
template: `{% if injectionPoint == "start" -%}
|
|
53
|
+
function {{ functionName }}({{ parameters | default("") }}) {
|
|
54
|
+
{{ injectionContent | indent(2) }}
|
|
55
|
+
{% elif injectionPoint == "end" -%}
|
|
56
|
+
{{ injectionContent | indent(2) }}
|
|
57
|
+
}
|
|
58
|
+
{% elif injectionPoint == "before-return" -%}
|
|
59
|
+
{{ injectionContent | indent(2) }}
|
|
60
|
+
return
|
|
61
|
+
{% endif -%}`,
|
|
62
|
+
detection: {
|
|
63
|
+
regex: 'function\\s+{{ functionName | escape_regex }}\\s*\\([^)]*\\)\\s*{',
|
|
64
|
+
flags: 'g'
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// Class injection target
|
|
69
|
+
this.registerTargetTemplate('class_target', {
|
|
70
|
+
name: 'ClassInjectionTarget',
|
|
71
|
+
description: 'Inject code into class definitions',
|
|
72
|
+
pattern: {
|
|
73
|
+
className: '{{ className }}',
|
|
74
|
+
injectionType: '{{ injectionType | default("method") }}', // method, property, constructor
|
|
75
|
+
visibility: '{{ visibility | default("public") }}'
|
|
76
|
+
},
|
|
77
|
+
template: `{% if injectionType == "method" -%}
|
|
78
|
+
{{ visibility }} {{ methodName }}({{ parameters | default("") }}) {
|
|
79
|
+
{{ methodBody | indent(4) }}
|
|
80
|
+
}
|
|
81
|
+
{% elif injectionType == "property" -%}
|
|
82
|
+
{{ visibility }} {{ propertyName }}{{ propertyValue ? " = " + propertyValue : "" }};
|
|
83
|
+
{% elif injectionType == "constructor" -%}
|
|
84
|
+
constructor({{ parameters | default("") }}) {
|
|
85
|
+
{{ constructorBody | indent(4) }}
|
|
86
|
+
}
|
|
87
|
+
{% endif -%}`,
|
|
88
|
+
detection: {
|
|
89
|
+
regex: 'class\\s+{{ className | escape_regex }}\\s*(?:extends\\s+\\w+)?\\s*{',
|
|
90
|
+
flags: 'g'
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// Import injection target
|
|
95
|
+
this.registerTargetTemplate('import_target', {
|
|
96
|
+
name: 'ImportInjectionTarget',
|
|
97
|
+
description: 'Inject import statements',
|
|
98
|
+
pattern: {
|
|
99
|
+
importType: '{{ importType | default("named") }}', // named, default, namespace
|
|
100
|
+
modulePath: '{{ modulePath }}'
|
|
101
|
+
},
|
|
102
|
+
template: `{% if importType == "named" -%}
|
|
103
|
+
import { {{ importNames | join(", ") }} } from '{{ modulePath }}';
|
|
104
|
+
{% elif importType == "default" -%}
|
|
105
|
+
import {{ defaultImport }} from '{{ modulePath }}';
|
|
106
|
+
{% elif importType == "namespace" -%}
|
|
107
|
+
import * as {{ namespaceImport }} from '{{ modulePath }}';
|
|
108
|
+
{% elif importType == "side-effect" -%}
|
|
109
|
+
import '{{ modulePath }}';
|
|
110
|
+
{% endif -%}`,
|
|
111
|
+
detection: {
|
|
112
|
+
regex: '^\\s*import\\s+.*from\\s+["\']{{ modulePath | escape_regex }}["\'];?\\s*$',
|
|
113
|
+
flags: 'gm'
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Configuration injection target
|
|
118
|
+
this.registerTargetTemplate('config_target', {
|
|
119
|
+
name: 'ConfigurationTarget',
|
|
120
|
+
description: 'Inject configuration values',
|
|
121
|
+
pattern: {
|
|
122
|
+
configFormat: '{{ configFormat | default("json") }}', // json, yaml, js, env
|
|
123
|
+
configKey: '{{ configKey }}'
|
|
124
|
+
},
|
|
125
|
+
template: `{% if configFormat == "json" -%}
|
|
126
|
+
"{{ configKey }}": {{ configValue | json }}
|
|
127
|
+
{% elif configFormat == "yaml" -%}
|
|
128
|
+
{{ configKey }}: {{ configValue | yaml }}
|
|
129
|
+
{% elif configFormat == "js" -%}
|
|
130
|
+
{{ configKey }}: {{ configValue | js_value }},
|
|
131
|
+
{% elif configFormat == "env" -%}
|
|
132
|
+
{{ configKey }}={{ configValue }}
|
|
133
|
+
{% endif -%}`,
|
|
134
|
+
detection: {
|
|
135
|
+
regex: '"{{ configKey | escape_regex }}"\\s*:',
|
|
136
|
+
flags: 'g'
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// HTML injection target
|
|
141
|
+
this.registerTargetTemplate('html_target', {
|
|
142
|
+
name: 'HTMLInjectionTarget',
|
|
143
|
+
description: 'Inject HTML content at specific points',
|
|
144
|
+
pattern: {
|
|
145
|
+
targetElement: '{{ targetElement | default("body") }}',
|
|
146
|
+
position: '{{ position | default("append") }}' // append, prepend, before, after
|
|
147
|
+
},
|
|
148
|
+
template: `{% if position == "append" -%}
|
|
149
|
+
<{{ targetElement }}{{ attributes ? " " + attributes : "" }}>
|
|
150
|
+
{{ existingContent }}
|
|
151
|
+
{{ injectionContent | indent(2) }}
|
|
152
|
+
</{{ targetElement }}>
|
|
153
|
+
{% elif position == "prepend" -%}
|
|
154
|
+
<{{ targetElement }}{{ attributes ? " " + attributes : "" }}>
|
|
155
|
+
{{ injectionContent | indent(2) }}
|
|
156
|
+
{{ existingContent }}
|
|
157
|
+
</{{ targetElement }}>
|
|
158
|
+
{% elif position == "before" -%}
|
|
159
|
+
{{ injectionContent }}
|
|
160
|
+
<{{ targetElement }}{{ attributes ? " " + attributes : "" }}>{{ existingContent }}</{{ targetElement }}>
|
|
161
|
+
{% elif position == "after" -%}
|
|
162
|
+
<{{ targetElement }}{{ attributes ? " " + attributes : "" }}>{{ existingContent }}</{{ targetElement }}>
|
|
163
|
+
{{ injectionContent }}
|
|
164
|
+
{% endif -%}`,
|
|
165
|
+
detection: {
|
|
166
|
+
regex: '<{{ targetElement | escape_regex }}(?:\\s[^>]*)?>',
|
|
167
|
+
flags: 'gi'
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// CSS injection target
|
|
172
|
+
this.registerTargetTemplate('css_target', {
|
|
173
|
+
name: 'CSSInjectionTarget',
|
|
174
|
+
description: 'Inject CSS rules and selectors',
|
|
175
|
+
pattern: {
|
|
176
|
+
selector: '{{ cssSelector }}',
|
|
177
|
+
injectionType: '{{ injectionType | default("rule") }}' // rule, property, media
|
|
178
|
+
},
|
|
179
|
+
template: `{% if injectionType == "rule" -%}
|
|
180
|
+
{{ cssSelector }} {
|
|
181
|
+
{{ cssProperties | indent(2) }}
|
|
182
|
+
}
|
|
183
|
+
{% elif injectionType == "property" -%}
|
|
184
|
+
{{ propertyName }}: {{ propertyValue }};
|
|
185
|
+
{% elif injectionType == "media" -%}
|
|
186
|
+
@media {{ mediaQuery }} {
|
|
187
|
+
{{ mediaRules | indent(2) }}
|
|
188
|
+
}
|
|
189
|
+
{% endif -%}`,
|
|
190
|
+
detection: {
|
|
191
|
+
regex: '{{ cssSelector | escape_regex }}\\s*{',
|
|
192
|
+
flags: 'g'
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// JSON injection target
|
|
197
|
+
this.registerTargetTemplate('json_target', {
|
|
198
|
+
name: 'JSONInjectionTarget',
|
|
199
|
+
description: 'Inject properties into JSON objects',
|
|
200
|
+
pattern: {
|
|
201
|
+
targetPath: '{{ jsonPath }}',
|
|
202
|
+
operation: '{{ operation | default("merge") }}' // merge, replace, append
|
|
203
|
+
},
|
|
204
|
+
template: `{% if operation == "merge" -%}
|
|
205
|
+
{{ jsonKey }}: {{ jsonValue | json }}
|
|
206
|
+
{% elif operation == "replace" -%}
|
|
207
|
+
{{ jsonValue | json }}
|
|
208
|
+
{% elif operation == "append" and jsonValue is iterable -%}
|
|
209
|
+
{{ existingValue | concat(jsonValue) | json }}
|
|
210
|
+
{% endif -%}`,
|
|
211
|
+
detection: {
|
|
212
|
+
regex: '"{{ jsonKey | escape_regex }}"\\s*:\\s*',
|
|
213
|
+
flags: 'g'
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
// Package.json target
|
|
218
|
+
this.registerTargetTemplate('package_json_target', {
|
|
219
|
+
name: 'PackageJSONTarget',
|
|
220
|
+
description: 'Inject dependencies and scripts into package.json',
|
|
221
|
+
pattern: {
|
|
222
|
+
section: '{{ section | default("dependencies") }}', // dependencies, devDependencies, scripts, etc.
|
|
223
|
+
operation: '{{ operation | default("add") }}' // add, update, remove
|
|
224
|
+
},
|
|
225
|
+
template: `{% if section == "dependencies" or section == "devDependencies" -%}
|
|
226
|
+
"{{ packageName }}": "{{ packageVersion }}"
|
|
227
|
+
{% elif section == "scripts" -%}
|
|
228
|
+
"{{ scriptName }}": "{{ scriptCommand }}"
|
|
229
|
+
{% elif section == "keywords" -%}
|
|
230
|
+
"{{ keyword }}"
|
|
231
|
+
{% elif section == "exports" -%}
|
|
232
|
+
"{{ exportPath }}": "{{ exportTarget }}"
|
|
233
|
+
{% endif -%}`,
|
|
234
|
+
detection: {
|
|
235
|
+
regex: '"{{ section | escape_regex }}"\\s*:\\s*{',
|
|
236
|
+
flags: 'g'
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
// Docker injection target
|
|
241
|
+
this.registerTargetTemplate('dockerfile_target', {
|
|
242
|
+
name: 'DockerfileTarget',
|
|
243
|
+
description: 'Inject instructions into Dockerfile',
|
|
244
|
+
pattern: {
|
|
245
|
+
instruction: '{{ instruction | upper }}',
|
|
246
|
+
position: '{{ position | default("append") }}' // append, prepend, after-instruction
|
|
247
|
+
},
|
|
248
|
+
template: `{{ instruction | upper }} {{ instructionArgs }}`,
|
|
249
|
+
detection: {
|
|
250
|
+
regex: '^{{ instruction | upper | escape_regex }}\\s+',
|
|
251
|
+
flags: 'gm'
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
// YAML injection target
|
|
256
|
+
this.registerTargetTemplate('yaml_target', {
|
|
257
|
+
name: 'YAMLInjectionTarget',
|
|
258
|
+
description: 'Inject properties into YAML files',
|
|
259
|
+
pattern: {
|
|
260
|
+
yamlPath: '{{ yamlPath }}',
|
|
261
|
+
indentLevel: '{{ indentLevel | default(0) }}'
|
|
262
|
+
},
|
|
263
|
+
template: `{{ ' '.repeat(indentLevel * 2) }}{{ yamlKey }}: {{ yamlValue | yaml_value }}`,
|
|
264
|
+
detection: {
|
|
265
|
+
regex: '^\\s*{{ yamlKey | escape_regex }}\\s*:',
|
|
266
|
+
flags: 'gm'
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Register an injection target template
|
|
273
|
+
*/
|
|
274
|
+
registerTargetTemplate(name, template) {
|
|
275
|
+
this.targetTemplates.set(name, {
|
|
276
|
+
...template,
|
|
277
|
+
registered: this.options.staticBuildTime,
|
|
278
|
+
namespace: this.options.namespace
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Generate injection target from template
|
|
284
|
+
*/
|
|
285
|
+
generateTarget(templateName, context = {}) {
|
|
286
|
+
const template = this.targetTemplates.get(templateName);
|
|
287
|
+
if (!template) {
|
|
288
|
+
throw new Error(`Injection target template '${templateName}' not found`);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Process template with context
|
|
292
|
+
let targetContent = template.template;
|
|
293
|
+
|
|
294
|
+
// Replace template variables
|
|
295
|
+
for (const [key, value] of Object.entries(context)) {
|
|
296
|
+
const regex = new RegExp(`{{\\s*${key}\\s*(?:\\|[^}]*)?\\s*}}`, 'g');
|
|
297
|
+
|
|
298
|
+
const matches = targetContent.match(regex);
|
|
299
|
+
if (matches) {
|
|
300
|
+
for (const match of matches) {
|
|
301
|
+
const defaultMatch = match.match(/\|\s*default\('([^']*)'\)/);
|
|
302
|
+
const replacement = value !== undefined ? value : (defaultMatch ? defaultMatch[1] : match);
|
|
303
|
+
targetContent = targetContent.replace(match, String(replacement));
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Process template logic
|
|
309
|
+
targetContent = this.processTemplateLogic(targetContent, context);
|
|
310
|
+
|
|
311
|
+
// Generate detection pattern
|
|
312
|
+
const detectionPattern = this.generateDetectionPattern(template.detection, context);
|
|
313
|
+
|
|
314
|
+
return {
|
|
315
|
+
name: this.interpolate(template.name, context),
|
|
316
|
+
description: this.interpolate(template.description, context),
|
|
317
|
+
content: targetContent,
|
|
318
|
+
pattern: template.pattern ? this.processPattern(template.pattern, context) : null,
|
|
319
|
+
detection: detectionPattern,
|
|
320
|
+
metadata: {
|
|
321
|
+
template: templateName,
|
|
322
|
+
generated: this.options.staticBuildTime,
|
|
323
|
+
namespace: this.options.namespace,
|
|
324
|
+
context: Object.keys(context)
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Generate detection pattern for finding injection points
|
|
331
|
+
*/
|
|
332
|
+
generateDetectionPattern(detection, context) {
|
|
333
|
+
if (!detection) return null;
|
|
334
|
+
|
|
335
|
+
let regex = detection.regex;
|
|
336
|
+
|
|
337
|
+
// Replace template variables in regex
|
|
338
|
+
for (const [key, value] of Object.entries(context)) {
|
|
339
|
+
const regexVar = new RegExp(`{{\\s*${key}\\s*\\|\\s*escape_regex\\s*}}`, 'g');
|
|
340
|
+
regex = regex.replace(regexVar, this.escapeRegex(String(value || '')));
|
|
341
|
+
|
|
342
|
+
const simpleVar = new RegExp(`{{\\s*${key}\\s*}}`, 'g');
|
|
343
|
+
regex = regex.replace(simpleVar, String(value || ''));
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
return {
|
|
347
|
+
regex: new RegExp(regex, detection.flags || 'g'),
|
|
348
|
+
source: regex,
|
|
349
|
+
flags: detection.flags || 'g'
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Process pattern object with context
|
|
355
|
+
*/
|
|
356
|
+
processPattern(pattern, context) {
|
|
357
|
+
const processed = {};
|
|
358
|
+
|
|
359
|
+
for (const [key, value] of Object.entries(pattern)) {
|
|
360
|
+
if (typeof value === 'string') {
|
|
361
|
+
processed[key] = this.interpolate(value, context);
|
|
362
|
+
} else {
|
|
363
|
+
processed[key] = value;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
return processed;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Process template logic (loops and conditions)
|
|
372
|
+
*/
|
|
373
|
+
processTemplateLogic(content, context) {
|
|
374
|
+
let result = content;
|
|
375
|
+
|
|
376
|
+
// Process {% if %} conditions
|
|
377
|
+
const ifRegex = /{%\s*if\s+([\w.\[\]|()\s!="']+)\s*%}([\s\S]*?)(?:{%\s*elif\s+([\w.\[\]|()\s!="']+)\s*%}([\s\S]*?))*(?:{%\s*else\s*%}([\s\S]*?))?{%\s*endif\s*%}/g;
|
|
378
|
+
|
|
379
|
+
result = result.replace(ifRegex, (match, condition, ifContent, elifCondition, elifContent, elseContent) => {
|
|
380
|
+
const conditionValue = this.evaluateCondition(condition, context);
|
|
381
|
+
if (conditionValue) {
|
|
382
|
+
return ifContent;
|
|
383
|
+
} else if (elifCondition && this.evaluateCondition(elifCondition, context)) {
|
|
384
|
+
return elifContent;
|
|
385
|
+
} else {
|
|
386
|
+
return elseContent || '';
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
// Process {% for %} loops
|
|
391
|
+
const forLoopRegex = /{%\s*for\s+(\w+)\s+in\s+([\w.\[\]|()]+)\s*%}([\s\S]*?){%\s*endfor\s*%}/g;
|
|
392
|
+
|
|
393
|
+
result = result.replace(forLoopRegex, (match, itemVar, listExpr, loopContent) => {
|
|
394
|
+
const listValue = this.evaluateExpression(listExpr, context);
|
|
395
|
+
if (!Array.isArray(listValue)) {
|
|
396
|
+
return '';
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
return listValue.map((item, index) => {
|
|
400
|
+
const loopContext = {
|
|
401
|
+
...context,
|
|
402
|
+
[itemVar]: item,
|
|
403
|
+
loop: {
|
|
404
|
+
index: index,
|
|
405
|
+
first: index === 0,
|
|
406
|
+
last: index === listValue.length - 1
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
return this.interpolateWithContext(loopContent, loopContext);
|
|
411
|
+
}).join('');
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
return result;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Evaluate conditional expressions
|
|
419
|
+
*/
|
|
420
|
+
evaluateCondition(condition, context) {
|
|
421
|
+
// Handle simple conditions
|
|
422
|
+
const trimmed = condition.trim();
|
|
423
|
+
|
|
424
|
+
// Handle == comparisons
|
|
425
|
+
const eqMatch = trimmed.match(/(\w+)\s*==\s*"([^"]*)"/);
|
|
426
|
+
if (eqMatch) {
|
|
427
|
+
const value = this.getNestedValue(context, eqMatch[1]);
|
|
428
|
+
return value === eqMatch[2];
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// Handle simple property access
|
|
432
|
+
const value = this.evaluateExpression(trimmed, context);
|
|
433
|
+
return Boolean(value);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Evaluate simple expressions
|
|
438
|
+
*/
|
|
439
|
+
evaluateExpression(expr, context) {
|
|
440
|
+
// Handle default expressions
|
|
441
|
+
const defaultMatch = expr.match(/(\w+)\s*\|\s*default\('([^']*)'\)/);
|
|
442
|
+
if (defaultMatch) {
|
|
443
|
+
const value = this.getNestedValue(context, defaultMatch[1]);
|
|
444
|
+
return value !== undefined ? value : defaultMatch[2];
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// Handle simple property access
|
|
448
|
+
return this.getNestedValue(context, expr.trim());
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Get nested value from object
|
|
453
|
+
*/
|
|
454
|
+
getNestedValue(obj, path) {
|
|
455
|
+
return path.split('.').reduce((current, key) => {
|
|
456
|
+
return current && current[key] !== undefined ? current[key] : undefined;
|
|
457
|
+
}, obj);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Interpolate template with context
|
|
462
|
+
*/
|
|
463
|
+
interpolateWithContext(template, context) {
|
|
464
|
+
let result = template;
|
|
465
|
+
|
|
466
|
+
for (const [key, value] of Object.entries(context)) {
|
|
467
|
+
if (typeof value === 'object' && value !== null) {
|
|
468
|
+
// Handle nested objects
|
|
469
|
+
for (const [nestedKey, nestedValue] of Object.entries(value)) {
|
|
470
|
+
const regex = new RegExp(`{{\\s*${key}\\.${nestedKey}\\s*}}`, 'g');
|
|
471
|
+
result = result.replace(regex, String(nestedValue));
|
|
472
|
+
}
|
|
473
|
+
} else {
|
|
474
|
+
const regex = new RegExp(`{{\\s*${key}\\s*}}`, 'g');
|
|
475
|
+
result = result.replace(regex, String(value || ''));
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
return result;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Simple template interpolation
|
|
484
|
+
*/
|
|
485
|
+
interpolate(template, context) {
|
|
486
|
+
let result = template;
|
|
487
|
+
|
|
488
|
+
for (const [key, value] of Object.entries(context)) {
|
|
489
|
+
const regex = new RegExp(`{{\\s*${key}\\s*(?:\\|[^}]*)?\\s*}}`, 'g');
|
|
490
|
+
|
|
491
|
+
const matches = result.match(regex);
|
|
492
|
+
if (matches) {
|
|
493
|
+
for (const match of matches) {
|
|
494
|
+
const defaultMatch = match.match(/\|\s*default\('([^']*)'\)/);
|
|
495
|
+
const replacement = value !== undefined ? value : (defaultMatch ? defaultMatch[1] : match);
|
|
496
|
+
result = result.replace(match, replacement);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
return result;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* Escape string for regex
|
|
506
|
+
*/
|
|
507
|
+
escapeRegex(string) {
|
|
508
|
+
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* Generate injection operation specification
|
|
513
|
+
*/
|
|
514
|
+
generateInjectionSpec(targetName, context = {}, options = {}) {
|
|
515
|
+
const target = this.generateTarget(targetName, context);
|
|
516
|
+
|
|
517
|
+
return {
|
|
518
|
+
target: target.name,
|
|
519
|
+
operation: options.operation || 'inject',
|
|
520
|
+
content: target.content,
|
|
521
|
+
detection: target.detection,
|
|
522
|
+
position: options.position || 'replace',
|
|
523
|
+
backup: options.backup !== false,
|
|
524
|
+
validate: options.validate !== false,
|
|
525
|
+
metadata: {
|
|
526
|
+
...target.metadata,
|
|
527
|
+
operation: options.operation || 'inject',
|
|
528
|
+
position: options.position || 'replace'
|
|
529
|
+
}
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* Get available target template names
|
|
535
|
+
*/
|
|
536
|
+
getTemplateNames() {
|
|
537
|
+
return Array.from(this.targetTemplates.keys()).sort();
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/**
|
|
541
|
+
* Get target template by name
|
|
542
|
+
*/
|
|
543
|
+
getTemplate(name) {
|
|
544
|
+
return this.targetTemplates.get(name);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* Export injection target templates
|
|
549
|
+
*/
|
|
550
|
+
exportTemplates(templateNames = []) {
|
|
551
|
+
const templatesToExport = templateNames.length > 0
|
|
552
|
+
? templateNames.filter(name => this.targetTemplates.has(name))
|
|
553
|
+
: Array.from(this.targetTemplates.keys());
|
|
554
|
+
|
|
555
|
+
const exported = {
|
|
556
|
+
format: 'kgen-injection-targets',
|
|
557
|
+
version: '1.0.0',
|
|
558
|
+
generated: this.options.staticBuildTime,
|
|
559
|
+
namespace: this.options.namespace,
|
|
560
|
+
templates: {}
|
|
561
|
+
};
|
|
562
|
+
|
|
563
|
+
for (const name of templatesToExport) {
|
|
564
|
+
exported.templates[name] = this.targetTemplates.get(name);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
return exported;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
/**
|
|
571
|
+
* Get template statistics
|
|
572
|
+
*/
|
|
573
|
+
getStats() {
|
|
574
|
+
return {
|
|
575
|
+
totalTemplates: this.targetTemplates.size,
|
|
576
|
+
namespace: this.options.namespace,
|
|
577
|
+
deterministicMode: this.options.deterministicMode,
|
|
578
|
+
templates: Array.from(this.targetTemplates.keys()).sort()
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
export default KGenInjectionTargets;
|