@accordproject/template-engine 1.0.0 → 1.0.2

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 (43) hide show
  1. package/package.json +5 -2
  2. package/.github/workflows/npm-publish.yml +0 -37
  3. package/assets/template-compilation.png +0 -0
  4. package/assets/template-interpreter.png +0 -0
  5. package/src/Common.ts +0 -58
  6. package/src/TemplateMarkInterpreter.ts +0 -420
  7. package/src/TemplateMarkToJavaScriptCompiler.ts +0 -99
  8. package/src/TemplateMarkToTypeScriptCompiler.ts +0 -276
  9. package/src/TypeScriptCompilationContext.ts +0 -73
  10. package/src/TypeScriptRuntime.ts +0 -64
  11. package/src/TypeScriptToJavaScriptCompiler.ts +0 -59
  12. package/src/compilers/Clause.ts +0 -63
  13. package/src/compilers/Common.ts +0 -215
  14. package/src/compilers/Conditional.ts +0 -53
  15. package/src/compilers/ContractDefinition.ts +0 -31
  16. package/src/compilers/FormattedVariable.ts +0 -41
  17. package/src/compilers/Formula.ts +0 -36
  18. package/src/compilers/Join.ts +0 -33
  19. package/src/compilers/ListBlock.ts +0 -55
  20. package/src/compilers/NodeCompilers.ts +0 -90
  21. package/src/compilers/Optional.ts +0 -65
  22. package/src/compilers/Variable.ts +0 -40
  23. package/src/compilers/With.ts +0 -37
  24. package/src/drafting/Boolean/index.ts +0 -26
  25. package/src/drafting/DateTime/index.ts +0 -28
  26. package/src/drafting/Double/format.ts +0 -50
  27. package/src/drafting/Double/index.ts +0 -30
  28. package/src/drafting/Integer/format.ts +0 -41
  29. package/src/drafting/Integer/index.ts +0 -32
  30. package/src/drafting/Long/index.ts +0 -30
  31. package/src/drafting/MonetaryAmount/currencycode.ts +0 -49
  32. package/src/drafting/MonetaryAmount/index.ts +0 -73
  33. package/src/drafting/String/index.ts +0 -22
  34. package/src/drafting/index.ts +0 -31
  35. package/src/index.ts +0 -15
  36. package/src/markdown-common.d.ts +0 -14
  37. package/src/model-gen/concerto.metamodel@1.0.0.ts +0 -267
  38. package/src/model-gen/concerto@1.0.0.ts +0 -71
  39. package/src/model-gen/org.accordproject.ciceromark@0.6.0.ts +0 -82
  40. package/src/model-gen/org.accordproject.commonmark@0.5.0.ts +0 -159
  41. package/src/model-gen/org.accordproject.templatemark@0.4.0.ts +0 -119
  42. package/tsconfig.build.json +0 -4
  43. package/tsconfig.json +0 -101
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@accordproject/template-engine",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Generation of AgreementMark from TemplateMark + JSON Data",
5
5
  "homepage": "https://github.com/accordproject",
6
6
  "engines": {
@@ -8,7 +8,10 @@
8
8
  "npm": ">=6"
9
9
  },
10
10
  "main": "dist/index.js",
11
- "typings": "dist/index.d.ts",
11
+ "types": "dist/index.d.ts",
12
+ "files": [
13
+ "/dist"
14
+ ],
12
15
  "scripts": {
13
16
  "clean": "rimraf dist",
14
17
  "prebuild": "npm-run-all clean",
@@ -1,37 +0,0 @@
1
- # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
2
- # For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages
3
-
4
- name: Node.js Package
5
-
6
- on:
7
- release:
8
- types: [created]
9
-
10
- jobs:
11
- build:
12
- runs-on: ubuntu-latest
13
- steps:
14
- - uses: actions/checkout@v3
15
- - uses: actions/setup-node@v3
16
- with:
17
- node-version: 16
18
- - run: npm ci
19
- - run: npm test
20
-
21
- publish-npm:
22
- needs: build
23
- runs-on: ubuntu-latest
24
- steps:
25
- - uses: actions/checkout@v3
26
- - uses: actions/setup-node@v3
27
- with:
28
- node-version: 16
29
- registry-url: https://registry.npmjs.org/
30
- - name: build and publish
31
- run: |
32
- npm ci
33
- npm config set //registry.npmjs.org/:_authToken ${{ secrets.NPM_TOKEN }}
34
- npm version --no-git-tag-version --yes --exact ${{ steps.timestamp.outputs.stamp }}
35
- npm publish --access public ${{ steps.tag.outputs.tag }} 2>&1
36
- env:
37
- NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
Binary file
Binary file
package/src/Common.ts DELETED
@@ -1,58 +0,0 @@
1
- /*
2
- * Licensed under the Apache License, Version 2.0 (the "License");
3
- * you may not use this file except in compliance with the License.
4
- * You may obtain a copy of the License at
5
- *
6
- * http://www.apache.org/licenses/LICENSE-2.0
7
- *
8
- * Unless required by applicable law or agreed to in writing, software
9
- * distributed under the License is distributed on an "AS IS" BASIS,
10
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
- * See the License for the specific language governing permissions and
12
- * limitations under the License.
13
- */
14
-
15
- import { ClassDeclaration, Introspector, ModelManager } from '@accordproject/concerto-core';
16
- import { TemplateMarkModel } from '@accordproject/markdown-common';
17
-
18
- // use to create agreementmark from templatemark
19
- export const TEMPLATEMARK_RE = /^(org\.accordproject\.templatemark)@(.+)\.(\w+)Definition$/;
20
- export const FORMULA_DEFINITION_RE = /^(org\.accordproject\.templatemark)@(.+)\.FormulaDefinition$/;
21
- export const VARIABLE_DEFINITION_RE = /^(org\.accordproject\.templatemark)@(.+)\.VariableDefinition$/;
22
- export const CONDITIONAL_DEFINITION_RE = /^(org\.accordproject\.templatemark)@(.+)\.ConditionalDefinition$/;
23
- export const ENUM_VARIABLE_DEFINITION_RE = /^(org\.accordproject\.templatemark)@(.+)\.EnumVariableDefinition$/;
24
- export const FORMATTED_VARIABLE_DEFINITION_RE = /^(org\.accordproject\.templatemark)@(.+)\.FormattedVariableDefinition$/;
25
- export const WITH_DEFINITION_RE = /^(org\.accordproject\.templatemark)@(.+)\.WithDefinition$/;
26
- export const LISTBLOCK_DEFINITION_RE = /^(org\.accordproject\.templatemark)@(.+)\.ListBlockDefinition$/;
27
- export const JOIN_DEFINITION_RE = /^(org\.accordproject\.templatemark)@(.+)\.JoinDefinition$/;
28
- export const OPTIONAL_DEFINITION_RE = /^(org\.accordproject\.templatemark)@(.+)\.OptionalDefinition$/;
29
- export const CLAUSE_DEFINITION_RE = /^(org\.accordproject\.templatemark)@(.+)\.ClauseDefinition$/;
30
- export const CONTRACT_DEFINITION_RE = /^(org\.accordproject\.templatemark)@(.+)\.ContractDefinition$/;
31
-
32
- export type TemplateData = Record<string, unknown>;
33
-
34
- /**
35
- * TemplateMark nodes that implicity change the data access scope
36
- * by specifying the name of a property on the node.
37
- */
38
- export const NAVIGATION_NODES = [
39
- `${TemplateMarkModel.NAMESPACE}.ListBlockDefinition`,
40
- `${TemplateMarkModel.NAMESPACE}.WithDefinition`,
41
- `${TemplateMarkModel.NAMESPACE}.JoinDefinition`,
42
- `${TemplateMarkModel.NAMESPACE}.OptionalDefinition`,
43
- `${TemplateMarkModel.NAMESPACE}.ClauseDefinition`
44
- ];
45
-
46
- export function getTemplateClassDeclaration(modelManager: ModelManager) : ClassDeclaration {
47
- const introspector = new Introspector(modelManager);
48
- const templateModels = introspector.getClassDeclarations().filter((item) => {
49
- return !item.isAbstract() && item.getDecorator('template');
50
- });
51
- if (templateModels.length > 1) {
52
- throw new Error('Found multiple concepts with @template decorator. The model for the template must contain a single concept with the @template decorator.');
53
- } else if (templateModels.length === 0) {
54
- throw new Error('Failed to find a concept with the @template decorator. The model for the template must contain a single concept with the @template decoratpr.');
55
- } else {
56
- return templateModels[0];
57
- }
58
- }
@@ -1,420 +0,0 @@
1
- /*
2
- * Licensed under the Apache License, Version 2.0 (the "License");
3
- * you may not use this file except in compliance with the License.
4
- * You may obtain a copy of the License at
5
- *
6
- * http://www.apache.org/licenses/LICENSE-2.0
7
- *
8
- * Unless required by applicable law or agreed to in writing, software
9
- * distributed under the License is distributed on an "AS IS" BASIS,
10
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
- * See the License for the specific language governing permissions and
12
- * limitations under the License.
13
- */
14
-
15
- import dayjs from 'dayjs';
16
- import jp from 'jsonpath';
17
- import traverse from 'traverse';
18
-
19
- import { ClassDeclaration, Factory, Introspector, ModelManager, Serializer } from '@accordproject/concerto-core';
20
- import { draftingMap } from './drafting';
21
- import { TemplateMarkModel, CommonMarkModel, CiceroMarkModel, ConcertoMetaModel } from '@accordproject/markdown-common';
22
- import { ModelUtil } from '@accordproject/concerto-core';
23
-
24
- import {
25
- TEMPLATEMARK_RE,
26
- FORMULA_DEFINITION_RE,
27
- VARIABLE_DEFINITION_RE,
28
- CONDITIONAL_DEFINITION_RE,
29
- ENUM_VARIABLE_DEFINITION_RE,
30
- FORMATTED_VARIABLE_DEFINITION_RE,
31
- WITH_DEFINITION_RE,
32
- LISTBLOCK_DEFINITION_RE,
33
- JOIN_DEFINITION_RE,
34
- OPTIONAL_DEFINITION_RE,
35
- CLAUSE_DEFINITION_RE,
36
- CONTRACT_DEFINITION_RE,
37
- TemplateData,
38
- NAVIGATION_NODES,
39
- getTemplateClassDeclaration
40
- } from './Common';
41
- import { TemplateMarkToJavaScriptCompiler } from './TemplateMarkToJavaScriptCompiler';
42
-
43
- /**
44
- * Evaluates a JS expression
45
- * @param {*} clauseLibrary the clause library
46
- * @param {*} data the contract data
47
- * @param {string} fn the JS function (including header)
48
- * @param {Date} now the current value for now
49
- * @returns {object} the result of evaluating the expression against the data
50
- */
51
- function evaluateJavaScript(clauseLibrary:object, data: TemplateData, fn: string, now?: dayjs.Dayjs): object {
52
- if (!data || !fn) {
53
- throw new Error(`Cannot evaluate JS ${fn} against ${data}`);
54
- }
55
- const runtimeNow = now ? now : dayjs();
56
- const functionArgNames = new Array<string>();
57
- functionArgNames.push('data');
58
- functionArgNames.push('library');
59
- functionArgNames.push('now');
60
- functionArgNames.push('jp');
61
-
62
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
63
- const functionArgValues = new Array<any>();
64
- functionArgValues.push(data);
65
- functionArgValues.push(clauseLibrary);
66
- functionArgValues.push(runtimeNow);
67
- functionArgValues.push(jp);
68
-
69
- // chop the function header and closing
70
- const expression = fn.substring(fn.indexOf('{') + 1, fn.lastIndexOf('}'));
71
- if(expression.trim().length === 0) {
72
- throw new Error('Empty expression');
73
- }
74
- try {
75
- const fun = new Function(...functionArgNames, expression); // SECURITY!
76
- const result = fun(...functionArgValues);
77
- return result;
78
- }
79
- catch(err) {
80
- throw new Error(`Caught error ${err} evaluating ${expression} with arguments ${functionArgValues}`);
81
- }
82
- }
83
-
84
- /**
85
- * Calculates a JSON path to use to retrieve data, based on a TemplatemMark tree.
86
- * For example, if we hit a VariableDefinition {{city}} that is nested inside a
87
- * WithDefinition {{#with address}} then the JSON path returned should be '$.address.city'.
88
- * Similarly ListBlockDefinition, JoinDefinition and OptionalDefinition also include
89
- * property names that must apply to their child nodes.
90
- * @param {*} rootData the root of the JSON document, typically this is a TemplateMark JSON
91
- * @param {*} currentNode the current TemplateMark node we are processing
92
- * @param {string[]} paths the traverse path to the current node
93
- * @returns {string} the JSON path to use to retrieve data
94
- */
95
- function getJsonPath(rootData:any, currentNode:any, paths:string[]) : string {
96
- if(!currentNode) {
97
- throw new Error('Node must be supplied');
98
- }
99
- if(!currentNode.name) {
100
- throw new Error(`Node must have a name: ${JSON.stringify(currentNode)}`);
101
- }
102
- if(currentNode.name.indexOf('.') >=0) {
103
- // prevent JSON path injection
104
- throw new Error(`Invalid name property ${currentNode.name}`);
105
- }
106
- if(!paths || !paths.length || paths.length <1 ) {
107
- throw new Error('Paths must be supplied');
108
- }
109
- const withPath = [];
110
- for (let n = 1; n < paths.length; n++) {
111
- const sub = paths.slice(0, n);
112
- const obj = traverse.get(rootData, sub);
113
- // HACK
114
- // if(obj===undefined) {
115
- // throw new Error(`Failed to find data with path ${sub} and data ${JSON.stringify(rootData, null, 2)}`);
116
- // }
117
- if (obj && obj.$class) {
118
- if(NAVIGATION_NODES.indexOf(obj.$class) >= 0) {
119
- withPath.push(`['${obj.name}']`);
120
- }
121
- }
122
- }
123
-
124
- if(currentNode.name !== 'this') {
125
- withPath.push(`['${currentNode.name}']`);
126
- }
127
-
128
- return withPath.length > 0 ? `$${withPath.join('')}` : '$';
129
- }
130
-
131
- /**
132
- * Generates an AgreementMark JSON document from a template plus data.
133
- * @param {ModelManager} modelManager - the template model
134
- * @param {*} clauseLibrary - the clause library
135
- * @param {*} templateMark - the TemplateMark JSON document
136
- * @param {*} data - the template data JSON
137
- * @param {[dayjs.Dayjs]} now - date/time to use for the now variable
138
- * @returns {*} the AgreementMark JSON
139
- */
140
- function generateAgreement(modelManager:ModelManager, clauseLibrary:object, templateMark: object, data: TemplateData, now?:dayjs.Dayjs): any {
141
- const introspector = new Introspector(modelManager);
142
- return traverse(templateMark).map(function (context: any) {
143
- let stopHere = false;
144
- if (typeof context === 'object' && context.$class && typeof context.$class === 'string') {
145
- const nodeClass = context.$class as string;
146
-
147
- // rewrite node types, mapping from TemplateMark namespace to CiceroMark
148
- const match = nodeClass.match(TEMPLATEMARK_RE);
149
- if (match && match.length > 1) {
150
- context.$class = `${CiceroMarkModel.NAMESPACE}.${match[3]}`;
151
- }
152
-
153
- // convert a contract node to a clause note HACK
154
- if (CONTRACT_DEFINITION_RE.test(nodeClass)) {
155
- context.$class = `${CommonMarkModel.NAMESPACE}.Paragraph`;
156
- delete context.name;
157
- delete context.elementType;
158
- }
159
-
160
- // convert a WithDefinition to a Paragraph in the output
161
- // not 100% sure we want to do that ... we may need to process
162
- // the child variable nodes and reparent them?
163
- if (WITH_DEFINITION_RE.test(nodeClass)) {
164
- context.$class = `${CommonMarkModel.NAMESPACE}.Paragraph`;
165
- delete context.name;
166
- delete context.elementType;
167
- }
168
-
169
- // add a 'value' property to FormulaDefinition
170
- // with the result of evaluating the JS code
171
- else if (FORMULA_DEFINITION_RE.test(nodeClass)) {
172
- if (context.code) {
173
- const result = evaluateJavaScript(clauseLibrary, data, context.code.contents, now);
174
- if(result === null) {
175
- context.value = '<null>';
176
- }
177
- else if(typeof result === 'string') {
178
- context.value = result;
179
- }
180
- else {
181
- context.value = JSON.stringify(result);
182
- }
183
- delete context.code;
184
- }
185
- else {
186
- throw new Error('Formula node is missing code.');
187
- }
188
- }
189
-
190
- // evaluate lists, recursing on each list item
191
- else if (LISTBLOCK_DEFINITION_RE.test(nodeClass)) {
192
- const path = getJsonPath(templateMark, context, this.path);
193
- const variableValues = jp.query(data, path, 1);
194
-
195
- if (variableValues.length === 0) {
196
- throw new Error(`No values found for path '${path}' in data ${data}.`);
197
- }
198
- else {
199
- const arrayData = variableValues[0];
200
- if(!Array.isArray(arrayData)) {
201
- throw new Error(`Values found for path '${path}' in data ${data} is not an array: ${arrayData}.`);
202
- }
203
- else {
204
- context.$class = `${CommonMarkModel.NAMESPACE}.List`;
205
- delete context.elementType;
206
- delete context.name;
207
- context.nodes = arrayData.map( arrayItem => {
208
- // arrayItem is now the data for the nested traversal
209
- const subResult = generateAgreement(modelManager, clauseLibrary, context.nodes[0].nodes[0], arrayItem);
210
- return {
211
- $class: `${CommonMarkModel.NAMESPACE}.Item`,
212
- nodes: subResult.nodes ? subResult.nodes : []
213
- };
214
- });
215
- stopHere = true; // do not process child nodes, we've already done it above...
216
- }
217
- }
218
- }
219
-
220
- // map over an array of items, joining them into a Text node
221
- else if (JOIN_DEFINITION_RE.test(nodeClass)) {
222
- const path = getJsonPath(templateMark, context, this.path);
223
- const variableValues = jp.query(data, path, 1);
224
-
225
- if (variableValues.length === 0) {
226
- throw new Error(`No values found for path '${path}' in data ${data}.`);
227
- }
228
- else {
229
- const arrayData = variableValues[0];
230
- if(!Array.isArray(arrayData)) {
231
- throw new Error(`Values found for path '${path}' in data ${data} is not an array: ${arrayData}.`);
232
- }
233
- else {
234
- context.$class = `${CommonMarkModel.NAMESPACE}.Text`;
235
- const drafter = draftingMap.get(context.elementType);
236
- context.text = arrayData.map( arrayItem => {
237
- return drafter ? drafter(arrayItem, context.format) : arrayItem as string;
238
- }).join(context.separator);
239
- delete context.elementType;
240
- delete context.name;
241
- delete context.separator;
242
- delete context.nodes;
243
- stopHere = true; // do not process child nodes, we've already done it above...
244
- }
245
- }
246
- }
247
-
248
- // add a 'value' property to VariableDefinition
249
- // with the value of the variable from 'data'
250
- else if (VARIABLE_DEFINITION_RE.test(nodeClass) ||
251
- ENUM_VARIABLE_DEFINITION_RE.test(nodeClass) ||
252
- FORMATTED_VARIABLE_DEFINITION_RE.test(nodeClass)) {
253
- if(typeof data === 'object') {
254
- const path = getJsonPath(templateMark, context, this.path);
255
- const variableValues = jp.query(data, path, 1);
256
- if (variableValues.length === 0) {
257
- throw new Error(`No values found for path '${path}' in data ${data}.`);
258
- }
259
- else {
260
- // convert the value to a string, optionally using the formatter
261
- const variableValue = variableValues[0];
262
- const drafter = draftingMap.get(context.elementType);
263
- context.value = drafter ? drafter(variableValue, context.format) : JSON.stringify(variableValue) as string;
264
- }
265
- }
266
- else {
267
- // a list of enum values or primitives brings us here
268
- const variableValue = data;
269
- const type = (ModelUtil as any).isPrimitiveType(context.elementType) ? null : introspector.getClassDeclaration(context.elementType);
270
- // we want to draft Enums as strings, not objects
271
- const drafter = draftingMap.get(type && type.isEnum() ? 'String' : context.elementType);
272
- context.value = drafter ? drafter(variableValue, context.format) : JSON.stringify(variableValue) as string;
273
- }
274
- }
275
-
276
- // add a 'isTrue' property to ConditionDefinition
277
- // with the result of evaluating the JS code or a boolean property
278
- else if (CONDITIONAL_DEFINITION_RE.test(nodeClass)) {
279
- if (context.condition) {
280
- context.isTrue = !!evaluateJavaScript(clauseLibrary, data, context.condition.contents, now) as unknown as boolean;
281
- }
282
- else {
283
- const path = getJsonPath(templateMark, context, this.path);
284
- const variableValues = jp.query(data, path, 1);
285
- if (variableValues && variableValues.length) {
286
- if(variableValues.length === 1) {
287
- context.isTrue = !!variableValues[0];
288
- }
289
- else {
290
- throw new Error(`Multiple values found for path '${path}' in data ${data}.`);
291
- }
292
- }
293
- else {
294
- context.isTrue = false;
295
- }
296
- }
297
- context.nodes = context.isTrue ? context.whenTrue : context.whenFalse;
298
- delete context.condition;
299
- delete context.dependencies;
300
- delete context.functionName;
301
- }
302
-
303
- // only include the children of a clause if its condition is true
304
- else if (CLAUSE_DEFINITION_RE.test(nodeClass)) {
305
- if (context.condition) {
306
- const result = !!evaluateJavaScript(clauseLibrary, data, context.condition.contents, now) as unknown as boolean;
307
- if(!result) {
308
- delete context.nodes;
309
- stopHere = true;
310
- }
311
- }
312
- delete context.condition;
313
- delete context.functionName;
314
- }
315
-
316
- // add a 'hasSome' property to OptionalDefinition
317
- else if (OPTIONAL_DEFINITION_RE.test(nodeClass)) {
318
- const path = getJsonPath(templateMark, context, this.path);
319
- const variableValues = jp.query(data, path, 1);
320
- if (variableValues && variableValues.length) {
321
- if(variableValues.length === 1) {
322
- context.hasSome = true;
323
- context.whenNone = [];
324
- }
325
- else {
326
- throw new Error(`Multiple values found for path '${path}' in data ${data}.`);
327
- }
328
- }
329
- else {
330
- context.hasSome = false;
331
- context.whenSome = [];
332
- }
333
- context.nodes = context.hasSome ? context.whenSome : context.whenNone;
334
- }
335
- }
336
- this.update(context, stopHere);
337
- });
338
- }
339
-
340
- /**
341
- * A template engine: merges the markup and logic of a template with
342
- * JSON data to produce JSON data.
343
- */
344
- export class TemplateMarkInterpreter {
345
- modelManager: ModelManager;
346
- templateClass: ClassDeclaration;
347
- clauseLibrary: object;
348
-
349
- constructor(modelManager: ModelManager, clauseLibrary:object) {
350
- this.modelManager = modelManager;
351
- this.clauseLibrary = clauseLibrary;
352
- this.templateClass = getTemplateClassDeclaration(this.modelManager);
353
- }
354
-
355
- /**
356
- * Checks that a TemplateMark JSON document is valid with respect to the
357
- * TemplateMark model, as well as the template model.
358
- *
359
- * Checks:
360
- * 1. Variable names are valid properties in the template model
361
- * 2. Optional properties have guards
362
- * @param {*} templateMark the TemplateMark JSON object
363
- * @returns {*} TemplateMark JSON that has been typed checked and has type metadata added
364
- * @throws {Error} if the templateMark document is invalid
365
- */
366
- checkTypes(templateMark: object): object {
367
- const modelManager = new ModelManager({ strict: true });
368
- modelManager.addCTOModel(ConcertoMetaModel.MODEL, 'concertometamodel.cto');
369
- modelManager.addCTOModel(CommonMarkModel.MODEL, 'commonmark.cto');
370
- modelManager.addCTOModel(TemplateMarkModel.MODEL, 'templatemark.cto');
371
- const factory = new Factory(modelManager);
372
- const serializer = new Serializer(factory, modelManager);
373
- try {
374
- serializer.fromJSON(templateMark);
375
- return templateMark;
376
- }
377
- catch(err) {
378
- throw new Error(`Generated invalid agreement: ${err}: ${JSON.stringify(templateMark, null, 2)}`);
379
- }
380
- }
381
-
382
- /**
383
- * Compiles the code nodes containing TS to code nodes containing JS.
384
- * @param {*} templateMark the TemplateMark JSON object
385
- * @returns {*} TemplateMark JSON with JS nodes
386
- * @throws {Error} if the templateMark document is invalid
387
- */
388
- compileTypeScriptToJavaScript(templateMark: object): object {
389
- const compiler = new TemplateMarkToJavaScriptCompiler(this.modelManager);
390
- return compiler.compile(templateMark);
391
- }
392
-
393
- validateCiceroMark(ciceroMark: object) {
394
- const modelManager = new ModelManager({ strict: true });
395
- modelManager.addCTOModel(ConcertoMetaModel.MODEL, 'concertometamodel.cto');
396
- modelManager.addCTOModel(CommonMarkModel.MODEL, 'commonmark.cto');
397
- modelManager.addCTOModel(CiceroMarkModel.MODEL, 'ciceromark.cto');
398
- const factory = new Factory(modelManager);
399
- const serializer = new Serializer(factory, modelManager);
400
- try {
401
- return serializer.fromJSON(ciceroMark);
402
- }
403
- catch(err) {
404
- throw new Error(`Generated invalid agreement: ${err}: ${JSON.stringify(ciceroMark, null, 2)}`);
405
- }
406
- }
407
-
408
- async generate(templateMark: object, data: TemplateData, now?:dayjs.Dayjs): Promise<any> {
409
- const factory = new Factory(this.modelManager);
410
- const serializer = new Serializer(factory, this.modelManager);
411
- const templateData = serializer.fromJSON(data);
412
- if (templateData.getFullyQualifiedType() !== this.templateClass.getFullyQualifiedName()) {
413
- throw new Error(`Template data must be of type '${this.templateClass.getFullyQualifiedName()}'.`);
414
- }
415
- const typedTemplateMark = this.checkTypes(templateMark);
416
- const jsTemplateMark = this.compileTypeScriptToJavaScript(typedTemplateMark);
417
- const ciceroMark = generateAgreement(this.modelManager, this.clauseLibrary, jsTemplateMark, data, now);
418
- return this.validateCiceroMark(ciceroMark);
419
- }
420
- }
@@ -1,99 +0,0 @@
1
- /*
2
- * Licensed under the Apache License, Version 2.0 (the "License");
3
- * you may not use this file except in compliance with the License.
4
- * You may obtain a copy of the License at
5
- *
6
- * http://www.apache.org/licenses/LICENSE-2.0
7
- *
8
- * Unless required by applicable law or agreed to in writing, software
9
- * distributed under the License is distributed on an "AS IS" BASIS,
10
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
- * See the License for the specific language governing permissions and
12
- * limitations under the License.
13
- */
14
-
15
- import traverse from 'traverse';
16
- import { TemplateMarkModel } from '@accordproject/markdown-common';
17
- import { ClassDeclaration, ModelManager } from '@accordproject/concerto-core';
18
- import { TypeScriptToJavaScriptCompiler } from './TypeScriptToJavaScriptCompiler';
19
- import { TwoSlashReturn } from '@typescript/twoslash';
20
- import { getTemplateClassDeclaration } from './Common';
21
- import { TemplateMarkToTypeScriptCompiler } from './TemplateMarkToTypeScriptCompiler';
22
-
23
- const CODE_NODES = [
24
- `${TemplateMarkModel.NAMESPACE}.FormulaDefinition`,
25
- `${TemplateMarkModel.NAMESPACE}.ConditionalDefinition`,
26
- `${TemplateMarkModel.NAMESPACE}.ClauseDefinition`,
27
- ];
28
-
29
- export type CompilerError = {
30
- nodeId: string;
31
- code: string;
32
- errors:TwoSlashReturn['errors'];
33
- };
34
-
35
- /**
36
- * Compiles all the Typescript nodes in a TemplateMark JSON
37
- * to ES_2020 and returns a modified TemplateMark JSON.
38
- */
39
- export class TemplateMarkToJavaScriptCompiler {
40
- modelManager:ModelManager;
41
- compiler: TypeScriptToJavaScriptCompiler;
42
- templateClass: ClassDeclaration;
43
-
44
- constructor(modelManager: ModelManager) {
45
- this.modelManager = modelManager;
46
- this.compiler = new TypeScriptToJavaScriptCompiler(modelManager);
47
- this.templateClass = getTemplateClassDeclaration(modelManager);
48
- }
49
-
50
- compile(templateJson: any) : any {
51
- const namedTemplateMark = TemplateMarkToTypeScriptCompiler.nameUserCode(templateJson);
52
- const functionCompiler = new TemplateMarkToTypeScriptCompiler(this.modelManager);
53
-
54
- // eslint-disable-next-line @typescript-eslint/no-this-alias
55
- const that = this;
56
- const errors = Array<CompilerError>();
57
- const compiled = traverse(namedTemplateMark).map(function (x) {
58
- if (x && CODE_NODES.includes(x.$class)) {
59
- if (x.code) { // formula
60
- const result = that.compiler.compile(functionCompiler.writeFunctionToString(x.name, 'any', x.code.contents));
61
- if(result.errors.length === 0) {
62
- x.code.contents = result.code;
63
- x.code.type = 'ES_2020';
64
- this.update(x);
65
- }
66
- else {
67
- errors.push({
68
- nodeId: x.name,
69
- code: x.code.contents,
70
- errors: result.errors
71
- });
72
- }
73
- }
74
- else if (x.condition) { // condition or clause (boolean condition)
75
- const result = that.compiler.compile(functionCompiler.writeFunctionToString(x.functionName, 'boolean', x.condition.contents));
76
- if(result.errors.length === 0) {
77
- x.condition.contents = result.code;
78
- x.condition.type = 'ES_2020';
79
- this.update(x);
80
- }
81
- else {
82
- errors.push({
83
- nodeId: x.functionName,
84
- code: x.condition.contents,
85
- errors: result.errors
86
- });
87
- }
88
- }
89
- }
90
- });
91
-
92
- if(errors.length === 0) {
93
- return compiled;
94
- }
95
- else {
96
- throw errors;
97
- }
98
- }
99
- }