@izara_project/izara-core-generate-service-code 1.0.97 → 1.0.98

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.
Files changed (37) hide show
  1. package/package.json +1 -1
  2. package/src/compile/loadSchemas.js +29 -6
  3. package/src/emit/application/crud/templates/FlowEndpoint_HdrWbs.ejs +2 -2
  4. package/src/emit/application/crud/templates/FlowMain_Wbs.ejs +2 -2
  5. package/src/emit/application/custom/templates/s3/CreateObject_Main.ejs +5 -2
  6. package/src/emit/application/custom/templates/triggerCache/CheckTriggerCache_Main.ejs +2 -2
  7. package/src/emit/application/relationship/templates/ChangeRelationship_Main.ejs +3 -2
  8. package/src/emit/application/relationship/templates/CreateRelationship_Main.ejs +3 -2
  9. package/src/emit/application/relationship/templates/DeleteRelationship_Main.ejs +3 -2
  10. package/src/emit/application/relationship/templates/GetRelationship_Main.ejs +3 -2
  11. package/src/emit/application/relationship/templates/MoveRelationship_Main.ejs +3 -2
  12. package/src/emit/application/relationship/templates/UpdateRelationship_Main.ejs +3 -2
  13. package/src/emit/application/system/templates/RegisterComplete_Main.ejs +6 -2
  14. package/src/emit/application/system/templates/Register_Main.ejs +6 -2
  15. package/src/emit/application/system/templates/WebSocketComplete_Main.ejs +3 -3
  16. package/src/emit/infrastructure/resources/iamResourceArtifacts.js +1 -1
  17. package/src/emit/schemas/attributeTree/emitAttributeTreeSchemas.js +14 -22
  18. package/src/emit/schemas/attributeTree/emitAttributeTreeTargetValueStandard.js +132 -0
  19. package/src/emit/schemas/ledger/emitLedgerSchemas.js +33 -11
  20. package/src/emit/schemas/ledger/templates/LedgerObjectSchema.ejs +3 -0
  21. package/src/emit/schemas/ledger/templates/LedgerRelationshipSchema.ejs +1 -0
  22. package/src/emit/schemas/propertyValue/emitPropertyValueSchemas.js +329 -0
  23. package/src/generate.js +16 -75
  24. package/src/output/mergeHooks.js +69 -15
  25. package/src/plan/domainRegistry.js +41 -5
  26. package/task.md +132 -0
  27. package/test/integration/publicApi.test.js +54 -1
  28. package/test/unit/application/customFlowApplicationArtifacts.test.js +9 -0
  29. package/test/unit/application/relationshipApplicationArtifacts.test.js +30 -0
  30. package/test/unit/infrastructure/resourceArtifacts.test.js +1 -0
  31. package/test/unit/output/mergeHooks.test.js +268 -0
  32. package/test/unit/schemas/attributeTreeDirectives.test.js +177 -0
  33. package/test/unit/schemas/attributeTreeSchemas.test.js +10 -61
  34. package/test/unit/schemas/ledgerSchemas.test.js +33 -7
  35. package/test/unit/schemas/propertyValueSchemas.test.js +265 -0
  36. package/src/emit/schemas/attributeTree/emitAttributeTreeTargetSchemas.js +0 -153
  37. package/src/emit/schemas/attributeTree/resolveTargetServicePath.js +0 -111
@@ -1,3 +1,8 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import ejsLib from 'ejs';
3
+
4
+ const TEMPLATE_URL = new URL('./templates/', import.meta.url);
5
+
1
6
  /**
2
7
  * Emits dynamic ObjectSchemas and RelationshipSchemas for ledger add-ons
3
8
  */
@@ -92,13 +97,17 @@ function _extractGraphStorageResources(object, storageResourceTag) {
92
97
  return result;
93
98
  }
94
99
 
95
- function _renderObjectSchema(schema) {
96
- return `export default [\n ${JSON.stringify(schema, null, 2).replace(/\n/g, '\n ')}\n];\n`;
100
+ function _renderObjectSchema(template, schema) {
101
+ return ejsLib.render(template, { schema });
97
102
  }
98
103
 
99
- function _renderRelationshipSchema(relationshipTag, content) {
104
+ function _renderRelationshipSchema(template, relationshipTag, content) {
100
105
  const wrapper = [{ [relationshipTag]: content }];
101
- return `export default ${JSON.stringify(wrapper, null, 2)};\n`;
106
+ return ejsLib.render(template, {
107
+ relationshipTag,
108
+ content,
109
+ wrapper
110
+ });
102
111
  }
103
112
 
104
113
  function _buildParentToEntryLinks({
@@ -117,7 +126,7 @@ function _buildParentToEntryLinks({
117
126
  objectType: parentObjectType
118
127
  },
119
128
  linkType: 'many',
120
- requiredOnCreate: true,
129
+ requiredOnCreate: false,
121
130
  belongTo: true
122
131
  },
123
132
  to: {
@@ -176,12 +185,16 @@ export function ledgerRelationshipSchemaManagedPaths(objects = []) {
176
185
  return paths;
177
186
  }
178
187
 
179
- export function emitLedgerObjectSchemas({ objects = [] } = {}) {
188
+ export async function emitLedgerObjectSchemas({ objects = [] } = {}) {
180
189
  const ledgers = _extractLedgers(objects);
181
190
  if (ledgers.length === 0) {
182
191
  return [];
183
192
  }
184
193
 
194
+ const template = await readFile(
195
+ new URL('LedgerObjectSchema.ejs', TEMPLATE_URL),
196
+ 'utf8'
197
+ );
185
198
  const artifacts = [];
186
199
 
187
200
  for (const { parentObject, addOn, component } of ledgers) {
@@ -217,14 +230,14 @@ export function emitLedgerObjectSchemas({ objects = [] } = {}) {
217
230
 
218
231
  artifacts.push({
219
232
  path: `app/src/schemas/ObjectSchemas/generatedLedger/${objectType}.js`,
220
- content: _renderObjectSchema(schema)
233
+ content: _renderObjectSchema(template, schema)
221
234
  });
222
235
  }
223
236
 
224
237
  return artifacts;
225
238
  }
226
239
 
227
- export function emitLedgerRelationshipSchemas({
240
+ export async function emitLedgerRelationshipSchemas({
228
241
  objects = [],
229
242
  serviceTag = ''
230
243
  } = {}) {
@@ -233,6 +246,10 @@ export function emitLedgerRelationshipSchemas({
233
246
  return [];
234
247
  }
235
248
 
249
+ const template = await readFile(
250
+ new URL('LedgerRelationshipSchema.ejs', TEMPLATE_URL),
251
+ 'utf8'
252
+ );
236
253
  const artifacts = [];
237
254
 
238
255
  for (const { parentObject, addOn, component } of ledgers) {
@@ -281,6 +298,7 @@ export function emitLedgerRelationshipSchemas({
281
298
  artifacts.push({
282
299
  path: `app/src/schemas/RelationshipSchemas/generatedLedger/${component.newRelationshipTag}.js`,
283
300
  content: _renderRelationshipSchema(
301
+ template,
284
302
  component.newRelationshipTag,
285
303
  currentRel
286
304
  )
@@ -306,7 +324,11 @@ export function emitLedgerRelationshipSchemas({
306
324
  };
307
325
  artifacts.push({
308
326
  path: `app/src/schemas/RelationshipSchemas/generatedLedger/${component.oldRelationshipTag}.js`,
309
- content: _renderRelationshipSchema(component.oldRelationshipTag, oldRel)
327
+ content: _renderRelationshipSchema(
328
+ template,
329
+ component.oldRelationshipTag,
330
+ oldRel
331
+ )
310
332
  });
311
333
 
312
334
  // 3. is<Prefix>Entry.js (if targetObjType or moveToTargetObjTypes is present)
@@ -341,7 +363,7 @@ export function emitLedgerRelationshipSchemas({
341
363
  objectType: component.objectType
342
364
  },
343
365
  linkType: 'many',
344
- requiredOnCreate: true
366
+ requiredOnCreate: false
345
367
  },
346
368
  to: {
347
369
  objType: {
@@ -361,7 +383,7 @@ export function emitLedgerRelationshipSchemas({
361
383
  };
362
384
  artifacts.push({
363
385
  path: `app/src/schemas/RelationshipSchemas/generatedLedger/${isRelTag}.js`,
364
- content: _renderRelationshipSchema(isRelTag, isRel)
386
+ content: _renderRelationshipSchema(template, isRelTag, isRel)
365
387
  });
366
388
  }
367
389
  }
@@ -0,0 +1,3 @@
1
+ export default [
2
+ <%- JSON.stringify(schema, null, 2).replace(/\n/g, '\n ') %>
3
+ ];
@@ -0,0 +1 @@
1
+ export default <%- JSON.stringify(wrapper, null, 2) %>;
@@ -0,0 +1,329 @@
1
+ import propertyNode from '@izara_project/izara-property-nodes';
2
+
3
+ /**
4
+ * Emits dynamic ObjectSchemas and RelationshipSchemas for propertyValue add-ons
5
+ */
6
+
7
+ function _capitalize(str) {
8
+ if (!str) {
9
+ return '';
10
+ }
11
+ if (typeof str !== 'string') {
12
+ return '';
13
+ }
14
+ return str.charAt(0).toUpperCase() + str.slice(1);
15
+ }
16
+
17
+ function _extractPropertyValues(objects = []) {
18
+ if (!Array.isArray(objects)) {
19
+ return [];
20
+ }
21
+ const results = [];
22
+ const seenTags = new Set();
23
+
24
+ for (const object of objects) {
25
+ if (!Array.isArray(object?.addOnDataStructure)) {
26
+ continue;
27
+ }
28
+ for (const addOn of object.addOnDataStructure) {
29
+ if (addOn?.type === 'propertyValue') {
30
+ let tag = object.objectType;
31
+ if (addOn.propertyValuePrefix) {
32
+ tag = addOn.propertyValuePrefix;
33
+ }
34
+ if (!tag) {
35
+ continue;
36
+ }
37
+ if (seenTags.has(tag)) {
38
+ continue;
39
+ }
40
+ seenTags.add(tag);
41
+ results.push({
42
+ parentObject: object,
43
+ addOn,
44
+ tag
45
+ });
46
+ }
47
+ }
48
+ }
49
+
50
+ return results;
51
+ }
52
+
53
+ function _extractGraphStorageResources(object) {
54
+ const result = {};
55
+ if (object?.storageResources) {
56
+ for (const [tag, res] of Object.entries(object.storageResources)) {
57
+ if (res?.storageType === 'graph') {
58
+ let graphServerTag = 'GraphHandler';
59
+ if (res.graphServerTag) {
60
+ graphServerTag = res.graphServerTag;
61
+ }
62
+ result[tag] = {
63
+ storageType: 'graph',
64
+ graphServerTag
65
+ };
66
+ }
67
+ }
68
+ }
69
+ if (Object.keys(result).length === 0) {
70
+ result.myGraph = {
71
+ storageType: 'graph',
72
+ graphServerTag: 'GraphHandler'
73
+ };
74
+ }
75
+ return result;
76
+ }
77
+
78
+ function _renderObjectSchema(schema) {
79
+ const json = JSON.stringify(schema, null, 2).replace(/\n/g, '\n ');
80
+ return `export default [\n ${json}\n];\n`;
81
+ }
82
+
83
+ function _renderRelationshipSchema(relationshipTag, content) {
84
+ const wrapper = [{ [relationshipTag]: content }];
85
+ return `export default ${JSON.stringify(wrapper, null, 2)};\n`;
86
+ }
87
+
88
+ export function propertyValueObjectSchemaManagedPaths(objects = []) {
89
+ const items = _extractPropertyValues(objects);
90
+ const paths = [];
91
+
92
+ for (const { tag } of items) {
93
+ const labelType = propertyNode.createPropertyLabelGraphNodeLabel(tag);
94
+ const valueType = propertyNode.createPropertyValueGraphNodeLabel(tag);
95
+ paths.push(
96
+ `app/src/schemas/ObjectSchemas/generatedCode/propertyValue/${labelType}.js`,
97
+ `app/src/schemas/ObjectSchemas/generatedCode/propertyValue/${valueType}.js`
98
+ );
99
+ }
100
+
101
+ return paths;
102
+ }
103
+
104
+ export function propertyValueRelationshipSchemaManagedPaths(objects = []) {
105
+ const items = _extractPropertyValues(objects);
106
+ const paths = [];
107
+
108
+ for (const { tag } of items) {
109
+ const tagCap = _capitalize(tag);
110
+ const hasRel = propertyNode.createHasPropertyGraphRelationshipType(tagCap);
111
+ const disabledRel =
112
+ propertyNode.createDisabledPropertyGraphRelationshipType(tagCap);
113
+ const isRel =
114
+ propertyNode.createIsPropertyLabelGraphRelationshipType(tagCap);
115
+ paths.push(
116
+ `app/src/schemas/RelationshipSchemas/generatedCode/propertyValue/${hasRel}.js`,
117
+ `app/src/schemas/RelationshipSchemas/generatedCode/propertyValue/${disabledRel}.js`,
118
+ `app/src/schemas/RelationshipSchemas/generatedCode/propertyValue/${isRel}.js`
119
+ );
120
+ }
121
+
122
+ return paths;
123
+ }
124
+
125
+ export function emitPropertyValueObjectSchemas({ objects = [] } = {}) {
126
+ const items = _extractPropertyValues(objects);
127
+ if (items.length === 0) {
128
+ return [];
129
+ }
130
+
131
+ const artifacts = [];
132
+
133
+ for (const { parentObject, tag } of items) {
134
+ const graphStorageResources = _extractGraphStorageResources(parentObject);
135
+ const storageResourceTags = Object.keys(graphStorageResources);
136
+
137
+ const labelIdSuffix = _capitalize(
138
+ propertyNode.consts.PROPERTY_LABEL_GRAPH_IDENTIFIER_ID
139
+ );
140
+ const labelType = propertyNode.createPropertyLabelGraphNodeLabel(tag);
141
+ const labelIdField = `${tag}${labelIdSuffix}`;
142
+
143
+ const labelSchema = {
144
+ objectType: labelType,
145
+ canDelete: true,
146
+ generatedBy: 'user',
147
+ storageResources: graphStorageResources,
148
+ fieldNames: {
149
+ [labelIdField]: {
150
+ type: 'string',
151
+ randomOnCreate: true,
152
+ canUpdate: false,
153
+ storageResourceTags
154
+ }
155
+ },
156
+ identifiers: [
157
+ {
158
+ type: 'identifier',
159
+ fieldName: labelIdField
160
+ }
161
+ ]
162
+ };
163
+
164
+ artifacts.push({
165
+ path: `app/src/schemas/ObjectSchemas/generatedCode/propertyValue/${labelType}.js`,
166
+ content: _renderObjectSchema(labelSchema)
167
+ });
168
+
169
+ const valueIdSuffix = _capitalize(
170
+ propertyNode.consts.PROPERTY_VALUE_GRAPH_IDENTIFIERS_ID
171
+ );
172
+ const valueType = propertyNode.createPropertyValueGraphNodeLabel(tag);
173
+ const valueIdField = `${tag}${valueIdSuffix}`;
174
+
175
+ const valueSchema = {
176
+ objectType: valueType,
177
+ canDelete: true,
178
+ generatedBy: 'user',
179
+ storageResources: graphStorageResources,
180
+ fieldNames: {
181
+ [valueIdField]: {
182
+ type: 'string',
183
+ randomOnCreate: true,
184
+ canUpdate: false,
185
+ storageResourceTags
186
+ }
187
+ },
188
+ identifiers: [
189
+ {
190
+ type: 'identifier',
191
+ fieldName: valueIdField
192
+ }
193
+ ]
194
+ };
195
+
196
+ artifacts.push({
197
+ path: `app/src/schemas/ObjectSchemas/generatedCode/propertyValue/${valueType}.js`,
198
+ content: _renderObjectSchema(valueSchema)
199
+ });
200
+ }
201
+
202
+ return artifacts;
203
+ }
204
+
205
+ export function emitPropertyValueRelationshipSchemas({
206
+ objects = [],
207
+ serviceTag = ''
208
+ } = {}) {
209
+ const items = _extractPropertyValues(objects);
210
+ if (items.length === 0) {
211
+ return [];
212
+ }
213
+
214
+ const artifacts = [];
215
+
216
+ for (const { parentObject, tag } of items) {
217
+ const tagCap = _capitalize(tag);
218
+ const parentObjectType = parentObject.objectType;
219
+ let effectiveServiceTag = 'CurrentService';
220
+ if (parentObject?.serviceTag) {
221
+ effectiveServiceTag = parentObject.serviceTag;
222
+ } else if (serviceTag) {
223
+ effectiveServiceTag = serviceTag;
224
+ }
225
+
226
+ const graphStorageResources = _extractGraphStorageResources(parentObject);
227
+ const storageResourceTags = Object.keys(graphStorageResources);
228
+
229
+ const labelType = propertyNode.createPropertyLabelGraphNodeLabel(tag);
230
+ const valueType = propertyNode.createPropertyValueGraphNodeLabel(tag);
231
+
232
+ const hasRelTag =
233
+ propertyNode.createHasPropertyGraphRelationshipType(tagCap);
234
+ const hasRel = {
235
+ fieldNames: {},
236
+ storageResources: graphStorageResources,
237
+ generatedBy: 'user',
238
+ links: [
239
+ {
240
+ storageResourceTags,
241
+ from: {
242
+ objType: {
243
+ serviceTag: effectiveServiceTag,
244
+ objectType: parentObjectType
245
+ },
246
+ linkType: 'one'
247
+ },
248
+ to: {
249
+ objType: {
250
+ serviceTag: effectiveServiceTag,
251
+ objectType: valueType
252
+ },
253
+ linkType: 'many',
254
+ requiredOnCreate: true
255
+ }
256
+ }
257
+ ]
258
+ };
259
+ artifacts.push({
260
+ path: `app/src/schemas/RelationshipSchemas/generatedCode/propertyValue/${hasRelTag}.js`,
261
+ content: _renderRelationshipSchema(hasRelTag, hasRel)
262
+ });
263
+
264
+ const disabledRelTag =
265
+ propertyNode.createDisabledPropertyGraphRelationshipType(tagCap);
266
+ const disabledRel = {
267
+ fieldNames: {},
268
+ storageResources: graphStorageResources,
269
+ generatedBy: 'user',
270
+ links: [
271
+ {
272
+ storageResourceTags,
273
+ from: {
274
+ objType: {
275
+ serviceTag: effectiveServiceTag,
276
+ objectType: parentObjectType
277
+ },
278
+ linkType: 'one'
279
+ },
280
+ to: {
281
+ objType: {
282
+ serviceTag: effectiveServiceTag,
283
+ objectType: valueType
284
+ },
285
+ linkType: 'many'
286
+ }
287
+ }
288
+ ]
289
+ };
290
+ artifacts.push({
291
+ path: `app/src/schemas/RelationshipSchemas/generatedCode/propertyValue/${disabledRelTag}.js`,
292
+ content: _renderRelationshipSchema(disabledRelTag, disabledRel)
293
+ });
294
+
295
+ const isRelTag =
296
+ propertyNode.createIsPropertyLabelGraphRelationshipType(tagCap);
297
+ const isRel = {
298
+ fieldNames: {},
299
+ storageResources: graphStorageResources,
300
+ generatedBy: 'systemNoUser',
301
+ links: [
302
+ {
303
+ storageResourceTags,
304
+ from: {
305
+ objType: {
306
+ serviceTag: effectiveServiceTag,
307
+ objectType: valueType
308
+ },
309
+ linkType: 'many',
310
+ requiredOnCreate: true
311
+ },
312
+ to: {
313
+ objType: {
314
+ serviceTag: effectiveServiceTag,
315
+ objectType: labelType
316
+ },
317
+ linkType: 'one'
318
+ }
319
+ }
320
+ ]
321
+ };
322
+ artifacts.push({
323
+ path: `app/src/schemas/RelationshipSchemas/generatedCode/propertyValue/${isRelTag}.js`,
324
+ content: _renderRelationshipSchema(isRelTag, isRel)
325
+ });
326
+ }
327
+
328
+ return artifacts;
329
+ }
package/src/generate.js CHANGED
@@ -1,17 +1,13 @@
1
- import path from 'node:path';
2
1
  import { buildGenerationPlan } from './plan/buildGenerationPlan.js';
3
2
  import { resolveEmissionGroups } from './plan/domainRegistry.js';
4
3
  import { commitArtifacts } from './output/commitArtifacts.js';
5
4
  import { compileProject } from './compile/compileProject.js';
6
5
  import {
7
6
  loadLocalSchemaDirectory,
7
+ loadGenerateDirectives,
8
8
  resolveExtendedObjectSchemas,
9
9
  resolveGraphServiceTags
10
10
  } from './compile/loadSchemas.js';
11
- import {
12
- resolveTargetServicePath,
13
- emitAttributeTreeTargetSchemas
14
- } from './emit/schemas/attributeTree/emitAttributeTreeSchemas.js';
15
11
 
16
12
  export async function generate(rootPath, options = {}) {
17
13
  const startedAt = Date.now();
@@ -26,13 +22,23 @@ export async function generate(rootPath, options = {}) {
26
22
  relationships,
27
23
  flows,
28
24
  notifications,
29
- errors
25
+ errors,
26
+ directives
30
27
  ] = await Promise.all([
31
28
  loadLocalSchemaDirectory(project, 'ObjectSchemas', schemaLoadOptions),
32
- loadLocalSchemaDirectory(project, 'RelationshipSchemas', schemaLoadOptions),
29
+ loadLocalSchemaDirectory(
30
+ project,
31
+ 'RelationshipSchemas',
32
+ schemaLoadOptions
33
+ ),
33
34
  loadLocalSchemaDirectory(project, 'FlowSchemas', schemaLoadOptions),
34
- loadLocalSchemaDirectory(project, 'NotificationSchemas', schemaLoadOptions),
35
- loadLocalSchemaDirectory(project, 'ErrorSchemas', schemaLoadOptions)
35
+ loadLocalSchemaDirectory(
36
+ project,
37
+ 'NotificationSchemas',
38
+ schemaLoadOptions
39
+ ),
40
+ loadLocalSchemaDirectory(project, 'ErrorSchemas', schemaLoadOptions),
41
+ loadGenerateDirectives(project, schemaLoadOptions)
36
42
  ]);
37
43
  const schemaBucketName =
38
44
  options.schemaBucketName || process.env.iz_serviceSchemaBucketName;
@@ -76,6 +82,7 @@ export async function generate(rootPath, options = {}) {
76
82
  flows,
77
83
  notifications,
78
84
  errors,
85
+ directives,
79
86
  objectGraphServiceTags,
80
87
  relationshipGraphServiceTags,
81
88
  logger: options.logger
@@ -101,14 +108,6 @@ export async function generate(rootPath, options = {}) {
101
108
  )
102
109
  );
103
110
  }
104
- const targetFiles = await _emitTargetServiceAttributeTreeSchemas({
105
- project,
106
- objects,
107
- logger: options.logger
108
- });
109
- for (const targetFile of targetFiles) {
110
- files.push(targetFile);
111
- }
112
111
  const durationMs = Date.now() - startedAt;
113
112
  console.log(
114
113
  `[INFO] [Generate] STATUS=FINISHED | ` +
@@ -126,61 +125,3 @@ export async function generate(rootPath, options = {}) {
126
125
  }
127
126
  }
128
127
 
129
- async function _emitTargetServiceAttributeTreeSchemas({
130
- project,
131
- objects,
132
- logger
133
- }) {
134
- if (!Array.isArray(objects)) {
135
- return [];
136
- }
137
- const committedFiles = [];
138
- for (const object of objects) {
139
- if (!Array.isArray(object?.addOnDataStructure)) {
140
- continue;
141
- }
142
- for (const addOn of object.addOnDataStructure) {
143
- if (addOn?.type === 'attributeTree' && addOn?.attributeTreeTag) {
144
- const attributeTag = addOn.attributeTreeTag;
145
- const targetServiceRoot = resolveTargetServicePath({
146
- currentServiceRoot: project.rootPath,
147
- attributeTag
148
- });
149
- if (targetServiceRoot) {
150
- const artifacts = emitAttributeTreeTargetSchemas({
151
- attributeTag,
152
- targetServiceRoot
153
- });
154
- const relativeArtifacts = artifacts.map((artifact) => {
155
- return {
156
- path: path.relative(targetServiceRoot, artifact.path),
157
- content: artifact.content
158
- };
159
- });
160
- const targetPlan = buildGenerationPlan(
161
- { outputPath: targetServiceRoot },
162
- relativeArtifacts,
163
- {
164
- managedScopes: {
165
- 'attribute-tree-target-schemas': relativeArtifacts.map(
166
- (a) => a.path
167
- )
168
- }
169
- }
170
- );
171
- const targetFiles = await commitArtifacts(targetPlan);
172
- for (const targetFile of targetFiles) {
173
- committedFiles.push(targetFile);
174
- }
175
- if (logger && typeof logger.info === 'function') {
176
- logger.info(
177
- `[INFO] [AttributeTree] Target schemas emitted for ` +
178
- `tag=${attributeTag} to ${targetServiceRoot}`
179
- );
180
- }
181
- }
182
- }
183
- }
184
- }
185
- return committedFiles;
186
- }
@@ -24,7 +24,10 @@ async function mergeHook(outputRoot, artifact) {
24
24
  }
25
25
  }
26
26
 
27
- if (sourceContent !== null && (artifact.writeWhenFileExist === false || artifact.writeWhenFileExists === false)) {
27
+ const skipWrite =
28
+ artifact.writeWhenFileExist === false ||
29
+ artifact.writeWhenFileExists === false;
30
+ if (sourceContent !== null && skipWrite) {
28
31
  return sourceContent;
29
32
  }
30
33
 
@@ -37,32 +40,83 @@ async function mergeHook(outputRoot, artifact) {
37
40
  return artifact.content;
38
41
  }
39
42
 
43
+ const tagOccurrences = new Map();
40
44
  return artifact.content.replace(
41
45
  matchers.anyTag,
42
46
  (_match, p1, _body1, p2) => {
43
- const tagName = p1 || p2;
44
- const hookBlock = sourceContent.match(matchers.tagPattern(tagName))?.[0];
45
- return hookBlock ?? _match;
47
+ const tagName = _extractTagName(p1, p2);
48
+ if (!tagName) {
49
+ return _match;
50
+ }
51
+ let count = 0;
52
+ if (tagOccurrences.has(tagName)) {
53
+ count = tagOccurrences.get(tagName);
54
+ }
55
+ tagOccurrences.set(tagName, count + 1);
56
+
57
+ const matches = sourceContent.match(matchers.tagPattern(tagName));
58
+ if (!matches || count >= matches.length) {
59
+ return _match;
60
+ }
61
+ const hookBlock = matches[count];
62
+ if (hookBlock !== null && hookBlock !== undefined) {
63
+ return hookBlock;
64
+ }
65
+ return _match;
46
66
  }
47
67
  );
48
68
  }
49
69
 
70
+ function _extractTagName(p1, p2) {
71
+ if (typeof p1 === 'string' && p1.length > 0) {
72
+ return p1;
73
+ }
74
+ if (typeof p2 === 'string' && p2.length > 0) {
75
+ return p2;
76
+ }
77
+ return '';
78
+ }
79
+
50
80
  function hookMatchers(filePath) {
51
- if (filePath.endsWith('.js')) {
81
+ const lower = filePath.toLowerCase();
82
+ if (
83
+ lower.endsWith('.js') ||
84
+ lower.endsWith('.mjs') ||
85
+ lower.endsWith('.cjs')
86
+ ) {
87
+ const jsP1 =
88
+ '\\/\\/\\s*\\(<\\s*([\\w.-]+)\\s*>\\)([\\s\\S]*?)' +
89
+ '\\/\\/\\s*\\(<\\/\\s*\\1\\s*>\\)';
90
+ const jsP2 =
91
+ '\\/\\/\\s*<#\\s*([\\w.-]+)\\s*#>([\\s\\S]*?)' +
92
+ '\\/\\/\\s*<#\\/\\s*\\3\\s*#>';
52
93
  return {
53
- anyTag: /(?:\/\/\(<(\w+)>\)([\s\S]*?)\/\/\(<\/\1>\)|\/\/\s*<#(\w+)#>([\s\S]*?)\/\/\s*<#\/\3#>)/gm,
54
- tagPattern: (name) =>
55
- new RegExp(
56
- `(?:\\/\\/\\(<${name}>\\)([\\s\\S]*?)\\/\\/\\(<\\/${name}>\\)|\\/\\/\\s*<#${name}#>([\\s\\S]*?)\\/\\/\\s*<#\\/${name}#>)`,
57
- 'gm'
58
- )
94
+ anyTag: new RegExp(`(?:${jsP1}|${jsP2})`, 'gm'),
95
+ tagPattern: (name) => {
96
+ const esc = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
97
+ const p1 =
98
+ `\\/\\/\\s*\\(<\\s*${esc}\\s*>\\)([\\s\\S]*?)` +
99
+ `\\/\\/\\s*\\(<\\/\\s*${esc}\\s*>\\)`;
100
+ const p2 =
101
+ `\\/\\/\\s*<#\\s*${esc}\\s*#>([\\s\\S]*?)` +
102
+ `\\/\\/\\s*<#\\/\\s*${esc}\\s*#>`;
103
+ return new RegExp(`(?:${p1}|${p2})`, 'gm');
104
+ }
59
105
  };
60
106
  }
61
- if (filePath.endsWith('.yml') || filePath.endsWith('.yaml')) {
107
+ if (lower.endsWith('.yml') || lower.endsWith('.yaml')) {
108
+ const ymlPattern =
109
+ '#\\s*<#\\s*([\\w.-]+)\\s*#>([\\s\\S]*?)' +
110
+ '#\\s*<#\\/\\s*\\1\\s*#>';
62
111
  return {
63
- anyTag: /#<#(\w+)#>([\s\S]*?)#<#\/\1#>/gm,
64
- tagPattern: (name) =>
65
- new RegExp(`#<#${name}#>([\\s\\S]*?)#<#\\/${name}#>`, 'gm')
112
+ anyTag: new RegExp(ymlPattern, 'gm'),
113
+ tagPattern: (name) => {
114
+ const esc = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
115
+ return new RegExp(
116
+ `#\\s*<#\\s*${esc}\\s*#>([\\s\\S]*?)#\\s*<#\\/\\s*${esc}\\s*#>`,
117
+ 'gm'
118
+ );
119
+ }
66
120
  };
67
121
  }
68
122
  return null;