1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 | 'use strict';
|
16 |
|
17 | const Stream = require('stream');
|
18 |
|
19 | const dijkstra = require('dijkstrajs');
|
20 | const find_path = dijkstra.find_path;
|
21 |
|
22 | const ModelLoader = require('@accordproject/concerto-core').ModelLoader;
|
23 |
|
24 | const CommonMarkTransformer = require('@accordproject/markdown-common').CommonMarkTransformer;
|
25 | const CiceroMarkTransformer = require('@accordproject/markdown-cicero').CiceroMarkTransformer;
|
26 | const TemplateMarkTransformer = require('@accordproject/markdown-template').TemplateMarkTransformer;
|
27 | const SlateTransformer = require('@accordproject/markdown-slate').SlateTransformer;
|
28 | const HtmlTransformer = require('@accordproject/markdown-html').HtmlTransformer;
|
29 | const PdfTransformer = require('@accordproject/markdown-pdf').PdfTransformer;
|
30 | const DocxTransformer = require('@accordproject/markdown-docx').DocxTransformer;
|
31 |
|
32 |
|
33 |
|
34 |
|
35 | const templateToTemplateMark = async (input,parameters,options) => {
|
36 | const t = new TemplateMarkTransformer();
|
37 | const modelOptions = { offline: false };
|
38 | if (options && options.offline) {
|
39 | modelOptions.offline = true;
|
40 | }
|
41 | const modelManager = await ModelLoader.loadModelManager(parameters.model, options);
|
42 | return {
|
43 | modelManager: modelManager,
|
44 | templateMark: t.fromMarkdownTemplate({ fileName:parameters.inputFileName, content:input }, modelManager, parameters.templateKind, options)
|
45 | };
|
46 | };
|
47 | const transformationGraph = {
|
48 | markdown_template: {
|
49 | docs: 'Template markdown (string)',
|
50 | fileFormat: 'utf8',
|
51 | templatemark_tokens: (input,parameters,options) => {
|
52 | const t = new TemplateMarkTransformer();
|
53 | return t.toTokens({ fileName:parameters.inputFileName, content:input }, options);
|
54 | },
|
55 | },
|
56 | templatemark_tokens: {
|
57 | docs: 'TemplateMark tokens (JSON)',
|
58 | fileFormat: 'json',
|
59 | templatemark: async (input,parameters,options) => {
|
60 | const t = new TemplateMarkTransformer();
|
61 | const modelManager = await ModelLoader.loadModelManager(parameters.model, options);
|
62 | return t.tokensToMarkdownTemplate(input, modelManager, parameters.templateKind, options);
|
63 | },
|
64 | },
|
65 | templatemark: {
|
66 | docs: 'TemplateMark DOM (JSON)',
|
67 | fileFormat: 'json',
|
68 | markdown_template: (input,parameters,options) => {
|
69 | const t = new TemplateMarkTransformer();
|
70 | return t.toMarkdownTemplate(input);
|
71 | },
|
72 | },
|
73 | markdown: {
|
74 | docs: 'Markdown (string)',
|
75 | fileFormat: 'utf8',
|
76 | commonmark_tokens: (input,parameters,options) => {
|
77 | const t = new CommonMarkTransformer();
|
78 | return t.toTokens(input, options);
|
79 | },
|
80 | },
|
81 | commonmark_tokens: {
|
82 | docs: 'Markdown tokens (JSON)',
|
83 | fileFormat: 'json',
|
84 | commonmark: async (input,parameters,options) => {
|
85 | const t = new CommonMarkTransformer();
|
86 | return t.fromTokens(input);
|
87 | },
|
88 | },
|
89 | markdown_cicero: {
|
90 | docs: 'Cicero markdown (string)',
|
91 | fileFormat: 'utf8',
|
92 | ciceromark_tokens: (input,parameters,options) => {
|
93 | const t = new CiceroMarkTransformer();
|
94 | return t.toTokens(input, options);
|
95 | },
|
96 | },
|
97 | ciceromark_tokens: {
|
98 | docs: 'CiceroMark tokens (JSON)',
|
99 | fileFormat: 'json',
|
100 | ciceromark: async (input,parameters,options) => {
|
101 | const t = new CiceroMarkTransformer();
|
102 | return t.fromTokens(input);
|
103 | },
|
104 | },
|
105 | commonmark: {
|
106 | docs: 'CommonMark DOM (JSON)',
|
107 | fileFormat: 'json',
|
108 | markdown: (input,parameters,options) => {
|
109 | const t = new CommonMarkTransformer();
|
110 | return t.toMarkdown(input);
|
111 | },
|
112 | ciceromark: (input,parameters,options) => {
|
113 | const t = new CiceroMarkTransformer();
|
114 | return t.fromCommonMark(input, options);
|
115 | },
|
116 | plaintext: (input,parameters,options) => {
|
117 | const t = new CommonMarkTransformer();
|
118 | return t.toMarkdown(t.removeFormatting(input));
|
119 | },
|
120 | },
|
121 | ciceromark: {
|
122 | docs: 'CiceroMark DOM (JSON)',
|
123 | fileFormat: 'json',
|
124 | markdown_cicero: (input,parameters,options) => {
|
125 | const t = new CiceroMarkTransformer();
|
126 | const inputUnwrapped = t.toCiceroMarkUnwrapped(input,options);
|
127 | return t.toMarkdownCicero(inputUnwrapped);
|
128 | },
|
129 | commonmark: (input,parameters,options) => {
|
130 | const t = new CiceroMarkTransformer();
|
131 | return t.toCommonMark(input, options);
|
132 | },
|
133 | ciceromark_parsed: (input,parameters,options) => {
|
134 | return input;
|
135 | },
|
136 | data: async (input,parameters,options) => {
|
137 | const t = new TemplateMarkTransformer(parameters.plugin);
|
138 | const templateParameters = Object.assign({},parameters);
|
139 | templateParameters.inputFileName = parameters.templateFileName;
|
140 | const { templateMark, modelManager } = await templateToTemplateMark(parameters.template,templateParameters,options);
|
141 | const result = await t.fromCiceroMark({ fileName:parameters.inputFileName, content:input }, templateMark, modelManager, parameters.templateKind, parameters.currentTime, parameters.utcOffset, options);
|
142 | return result;
|
143 | },
|
144 | },
|
145 | ciceromark_parsed: {
|
146 | docs: 'Parsed CiceroMark DOM (JSON)',
|
147 | fileFormat: 'json',
|
148 | html: (input,parameters,options) => {
|
149 | const t = new HtmlTransformer();
|
150 | return t.toHtml(input);
|
151 | },
|
152 | ciceromark: (input,parameters,options) => {
|
153 | const t = new CiceroMarkTransformer();
|
154 | return t.toCiceroMarkUnwrapped(input, options);
|
155 | },
|
156 | ciceromark_unquoted: (input,parameters,options) => {
|
157 | const t = new CiceroMarkTransformer();
|
158 | return t.unquote(input, options);
|
159 | },
|
160 | slate: (input,parameters,options) => {
|
161 | const t = new SlateTransformer();
|
162 | return t.fromCiceroMark(input);
|
163 | },
|
164 | pdf: (input, parameters, options) => {
|
165 | const t = new PdfTransformer();
|
166 | const outputStream = new Stream.Writable();
|
167 |
|
168 | let arrayBuffer = new ArrayBuffer(8);
|
169 | let memStore = Buffer.from(arrayBuffer);
|
170 | outputStream._write = (chunk, encoding, next) => {
|
171 | let buffer = (Buffer.isBuffer(chunk))
|
172 | ? chunk
|
173 | : new Buffer(chunk, encoding);
|
174 |
|
175 |
|
176 | memStore = Buffer.concat([memStore, buffer]);
|
177 | next();
|
178 | };
|
179 | return new Promise( (resolve) => {
|
180 | outputStream.on('finish', () => {
|
181 | resolve(memStore);
|
182 | });
|
183 | t.toPdf(input, options, outputStream );
|
184 | });
|
185 | },
|
186 | },
|
187 | data: {
|
188 | docs: 'Contract Data (JSON)',
|
189 | fileFormat: 'json',
|
190 | ciceromark_parsed: async (input,parameters,options) => {
|
191 | const t = new TemplateMarkTransformer(parameters.plugin);
|
192 | const templateParameters = Object.assign({},parameters);
|
193 | templateParameters.inputFileName = parameters.templateFileName;
|
194 | const { templateMark, modelManager } = await templateToTemplateMark(parameters.template,templateParameters,options);
|
195 | return t.instantiateCiceroMark(input, templateMark, modelManager, parameters.templateKind, parameters.currentTime, parameters.utcOffset, options);
|
196 | },
|
197 | },
|
198 | plaintext: {
|
199 | docs: 'Plain text (string)',
|
200 | fileFormat: 'utf8',
|
201 | markdown: (input,parameters,options) => {
|
202 | return input;
|
203 | },
|
204 | },
|
205 | ciceroedit: {
|
206 | docs: 'CiceroEdit (string)',
|
207 | fileFormat: 'utf8',
|
208 | ciceromark_parsed: (input,parameters,options) => {
|
209 | const t = new CiceroMarkTransformer();
|
210 | return t.fromCiceroEdit(input, options);
|
211 | },
|
212 | },
|
213 | ciceromark_unquoted: {
|
214 | docs: 'CiceroMark DOM (JSON) with quotes around variables removed',
|
215 | fileFormat: 'json',
|
216 | ciceromark_parsed: (input,parameters,options) => {
|
217 | return input;
|
218 | }
|
219 | },
|
220 | pdf: {
|
221 | docs: 'PDF (buffer)',
|
222 | fileFormat: 'binary',
|
223 | ciceromark_parsed: (input,parameters,options) => {
|
224 | const t = new PdfTransformer();
|
225 | return t.toCiceroMark(input, options);
|
226 | },
|
227 | },
|
228 | docx: {
|
229 | docs: 'DOCX (buffer)',
|
230 | fileFormat: 'binary',
|
231 | ciceromark_parsed: async (input,parameters,options) => {
|
232 | const t = new DocxTransformer();
|
233 | return t.toCiceroMark(input, options);
|
234 | },
|
235 | },
|
236 | html: {
|
237 | docs: 'HTML (string)',
|
238 | fileFormat: 'utf8',
|
239 | ciceromark_parsed: (input,parameters,options) => {
|
240 | const t = new HtmlTransformer();
|
241 | return t.toCiceroMark(input, options);
|
242 | },
|
243 | },
|
244 | slate: {
|
245 | docs: 'Slate DOM (JSON)',
|
246 | fileFormat: 'json',
|
247 | ciceromark_parsed: (input,parameters,options) => {
|
248 | const t = new SlateTransformer();
|
249 | return t.toCiceroMark(input, options);
|
250 | },
|
251 | },
|
252 | };
|
253 |
|
254 |
|
255 |
|
256 |
|
257 |
|
258 |
|
259 | function pruneGraph(graph) {
|
260 | const result = {};
|
261 | for (const sourceKey in graph) {
|
262 | result[sourceKey] = {};
|
263 | for (const targetKey in graph[sourceKey]) {
|
264 |
|
265 | if (targetKey !== 'docs' && targetKey !== 'fileFormat') {
|
266 | result[sourceKey][targetKey] = 1;
|
267 | }
|
268 | }
|
269 | }
|
270 | return result;
|
271 | }
|
272 | const rawGraph = pruneGraph(transformationGraph);
|
273 |
|
274 |
|
275 |
|
276 |
|
277 |
|
278 | function generateTransformationDiagram() {
|
279 | let result = `@startuml
|
280 | hide empty description
|
281 |
|
282 | `;
|
283 |
|
284 | Object.keys(transformationGraph).forEach(src => {
|
285 | result += `${src} : \n`;
|
286 | result += `${src} : ${transformationGraph[src].docs}\n`;
|
287 | Object.keys(transformationGraph[src]).forEach(dest => {
|
288 | if(dest !== 'docs' && dest !== 'fileFormat') {
|
289 | result += `${src} --> ${dest}\n`;
|
290 | }
|
291 | });
|
292 | result += '\n';
|
293 | });
|
294 |
|
295 | result += '@enduml';
|
296 | return result;
|
297 | }
|
298 |
|
299 |
|
300 |
|
301 |
|
302 |
|
303 |
|
304 |
|
305 |
|
306 |
|
307 |
|
308 |
|
309 |
|
310 |
|
311 | async function transformToDestination(source, sourceFormat, destinationFormat, parameters, options) {
|
312 | let result = source;
|
313 |
|
314 | const path = find_path(rawGraph, sourceFormat, destinationFormat);
|
315 | for(let n=0; n < path.length-1; n++) {
|
316 | const src = path[n];
|
317 | const dest = path[n+1];
|
318 | const srcNode = transformationGraph[src];
|
319 | const destinationNode = transformationGraph[dest];
|
320 | result = await srcNode[dest](result,parameters,options);
|
321 | if(options && options.verbose) {
|
322 | console.log(`Converted from ${src} to ${dest}. Result:`);
|
323 | if(destinationNode.fileFormat !== 'binary') {
|
324 | if(typeof result === 'object') {
|
325 | console.log(JSON.stringify(result, null, 2));
|
326 | } else {
|
327 | console.log(result);
|
328 | }
|
329 | }
|
330 | else {
|
331 | console.log(`<binary ${dest} data>`);
|
332 | }
|
333 | }
|
334 | }
|
335 |
|
336 | return result;
|
337 | }
|
338 |
|
339 |
|
340 |
|
341 |
|
342 |
|
343 |
|
344 |
|
345 |
|
346 |
|
347 |
|
348 |
|
349 |
|
350 |
|
351 |
|
352 | async function transform(source, sourceFormat, destinationFormat, parameters, options) {
|
353 | let result = source;
|
354 | options = options ? options : {};
|
355 | parameters = parameters ? parameters : {};
|
356 | if (sourceFormat === 'markdown') {
|
357 | options.source = source;
|
358 | }
|
359 |
|
360 | let currentSourceFormat = sourceFormat;
|
361 |
|
362 | for(let i=0; i < destinationFormat.length; i++) {
|
363 | let destination = destinationFormat[i];
|
364 | result = await transformToDestination(result, currentSourceFormat, destination, parameters, options);
|
365 | currentSourceFormat = destination;
|
366 | }
|
367 | return result;
|
368 | }
|
369 |
|
370 |
|
371 |
|
372 |
|
373 |
|
374 |
|
375 |
|
376 | function formatDescriptor(format) {
|
377 | if (Object.prototype.hasOwnProperty.call(transformationGraph,format)) {
|
378 | return transformationGraph[format];
|
379 | } else {
|
380 | throw new Error('Unknown format ' + format);
|
381 | }
|
382 | }
|
383 |
|
384 | module.exports.formatDescriptor = formatDescriptor;
|
385 | module.exports.transform = transform;
|
386 | module.exports.transformationGraph = transformationGraph;
|
387 | module.exports.generateTransformationDiagram = generateTransformationDiagram;
|