UNPKG

11.1 kBJavaScriptView Raw
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'use strict';
15
16function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
17
18function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
19
20const fs = require('fs');
21
22const fsPath = require('path');
23
24const slash = require('slash');
25
26const JSZip = require('jszip');
27
28const xregexp = require('xregexp');
29
30const languageTagRegex = require('ietf-language-tag-regex');
31
32const DefaultArchiveLoader = require('./loaders/defaultarchiveloader');
33
34const FileLoader = require('@accordproject/ergo-compiler').FileLoader;
35
36const Logger = require('@accordproject/concerto-core').Logger; // Matches 'sample.md' or 'sample_TAG.md' where TAG is an IETF language tag (BCP 47)
37
38
39const IETF_REGEXP = languageTagRegex({
40 exact: false
41}).toString().slice(1, -2);
42const SAMPLE_FILE_REGEXP = xregexp('text[/\\\\]sample(_(' + IETF_REGEXP + '))?.md$');
43/**
44 * A utility class to create templates from data sources.
45 * @class
46 * @private
47 * @abstract
48 */
49
50class TemplateLoader extends FileLoader {
51 /**
52 * Create a template from an archive.
53 * @param {*} Template - the type to construct
54 * @param {Buffer} buffer - the buffer to a Cicero Template Archive (cta) file
55 * @param {object} options - additional options
56 * @return {Promise<Template>} a Promise to the template
57 */
58 static fromArchive(Template, buffer, options) {
59 return _asyncToGenerator(function* () {
60 const method = 'fromArchive';
61 const zip = yield JSZip.loadAsync(buffer); // const allFiles = await TemplateLoader.loadZipFilesContents(zip, /.*/);
62 // console.log(allFiles);
63
64 const ctoModelFiles = [];
65 const ctoModelFileNames = [];
66 const sampleTextFiles = {};
67 const readmeContents = yield TemplateLoader.loadZipFileContents(zip, 'README.md');
68 let sampleFiles = yield TemplateLoader.loadZipFilesContents(zip, SAMPLE_FILE_REGEXP);
69 sampleFiles.forEach(
70 /*#__PURE__*/
71 function () {
72 var _ref = _asyncToGenerator(function* (sampleFile) {
73 let matches = sampleFile.name.match(SAMPLE_FILE_REGEXP);
74 let locale = 'default'; // Locale match found
75
76 if (matches !== null && matches[2]) {
77 locale = matches[2];
78 }
79
80 sampleTextFiles[locale] = sampleFile.contents;
81 });
82
83 return function (_x) {
84 return _ref.apply(this, arguments);
85 };
86 }());
87 const requestContents = yield TemplateLoader.loadZipFileContents(zip, 'request.json', true);
88 const packageJsonObject = yield TemplateLoader.loadZipFileContents(zip, 'package.json', true, true);
89 const templatizedGrammar = yield TemplateLoader.loadZipFileContents(zip, 'text/grammar.tem.md', false, false);
90 Logger.debug(method, 'Looking for model files');
91 let ctoFiles = yield TemplateLoader.loadZipFilesContents(zip, /model[/\\].*\.cto$/);
92 ctoFiles.forEach(
93 /*#__PURE__*/
94 function () {
95 var _ref2 = _asyncToGenerator(function* (file) {
96 ctoModelFileNames.push(file.name);
97 ctoModelFiles.push(file.contents);
98 });
99
100 return function (_x2) {
101 return _ref2.apply(this, arguments);
102 };
103 }()); // create the template
104
105 const template = new (Function.prototype.bind.call(Template, null, packageJsonObject, readmeContents, sampleTextFiles, requestContents, options))(); // add model files
106
107 Logger.debug(method, 'Adding model files to model manager');
108 template.getModelManager().addModelFiles(ctoModelFiles, ctoModelFileNames, true); // validation is disabled
109
110 Logger.debug(method, 'Setting grammar');
111
112 if (!templatizedGrammar) {
113 throw new Error('A template must contain a grammar.tem.md file.');
114 } else {
115 template.parserManager.buildGrammar(templatizedGrammar);
116 } // load and add the ergo files
117
118
119 if (template.getMetadata().getRuntime() === 'ergo') {
120 template.getLogicManager().addTemplateFile(templatizedGrammar, 'text/grammar.tem.md');
121 Logger.debug(method, 'Adding Ergo files to script manager');
122 const scriptFiles = yield TemplateLoader.loadZipFilesContents(zip, /logic[/\\].*\.ergo$/);
123 scriptFiles.forEach(function (obj) {
124 template.getLogicManager().addLogicFile(obj.contents, obj.name);
125 });
126 } else {
127 // load and add compiled JS files - we assume all runtimes are JS based (review!)
128 Logger.debug(method, 'Adding JS files to script manager');
129 const scriptFiles = yield TemplateLoader.loadZipFilesContents(zip, /logic[/\\].*\.js$/);
130 scriptFiles.forEach(function (obj) {
131 template.getLogicManager().addLogicFile(obj.contents, obj.name);
132 });
133 } // check the integrity of the model and logic of the template
134
135
136 template.validate();
137 return template; // Returns template
138 })();
139 }
140 /**
141 * Create a template from an URL.
142 * @param {*} Template - the type to construct
143 * @param {String} url - the URL to a Cicero Template Archive (cta) file
144 * @param {object} options - additional options
145 * @return {Promise} a Promise to the template
146 */
147
148
149 static fromUrl(Template, url, options) {
150 return _asyncToGenerator(function* () {
151 const loader = new DefaultArchiveLoader();
152 const buffer = yield loader.load(url, options);
153 return TemplateLoader.fromArchive(Template, buffer, options);
154 })();
155 }
156 /**
157 * Builds a Template from the contents of a directory.
158 * The directory must include a package.json in the root (used to specify
159 * the name, version and description of the template).
160 *
161 * @param {*} Template - the type to construct
162 * @param {String} path to a local directory
163 * @param {Object} [options] - an optional set of options to configure the instance.
164 * @return {Promise<Template>} a Promise to the instantiated template
165 */
166
167
168 static fromDirectory(Template, path, options) {
169 return _asyncToGenerator(function* () {
170 if (!options) {
171 options = {};
172 }
173
174 const method = 'fromDirectory'; // grab the README.md
175
176 const readmeContents = yield TemplateLoader.loadFileContents(path, 'README.md'); // grab the request.json
177
178 const requestJsonObject = yield TemplateLoader.loadFileContents(path, 'request.json', true); // grab the package.json
179
180 const packageJsonObject = yield TemplateLoader.loadFileContents(path, 'package.json', true, true); // grab the sample files
181
182 Logger.debug(method, 'Looking for sample files');
183 const sampleFiles = yield TemplateLoader.loadFilesContents(path, SAMPLE_FILE_REGEXP);
184 const sampleTextFiles = {};
185 sampleFiles.forEach(file => {
186 const matches = file.name.match(SAMPLE_FILE_REGEXP);
187 let locale = 'default'; // Match found
188
189 if (matches !== null && matches[2]) {
190 locale = matches[2];
191 }
192
193 Logger.debug(method, 'Using sample file locale', locale);
194 sampleTextFiles[locale] = file.contents;
195 }); // create the template
196
197 const template = new (Function.prototype.bind.call(Template, null, packageJsonObject, readmeContents, sampleTextFiles, requestJsonObject, options))();
198 const modelFiles = [];
199 const modelFileNames = [];
200 const ctoFiles = yield TemplateLoader.loadFilesContents(path, /model[/\\].*\.cto$/);
201 ctoFiles.forEach(file => {
202 modelFileNames.push(slash(file.name));
203 modelFiles.push(file.contents);
204 });
205 template.getModelManager().addModelFiles(modelFiles, modelFileNames, true);
206
207 if (!options.skipUpdateExternalModels) {
208 yield template.getModelManager().updateExternalModels();
209 Logger.debug(method, 'Added model files', modelFiles.length);
210 const externalModelFiles = template.getModelManager().getModels();
211 externalModelFiles.forEach(function (file) {
212 fs.writeFileSync(path + '/model/' + file.name, file.content);
213 });
214 } // load and add the template
215
216
217 let templatizedGrammar = yield TemplateLoader.loadFileContents(path, 'text/grammar.tem.md', false, false);
218
219 if (!templatizedGrammar) {
220 throw new Error('A template must either contain a grammar.tem.md file.');
221 } else {
222 template.parserManager.buildGrammar(templatizedGrammar);
223 Logger.debug(method, 'Loaded grammar.tem.md', templatizedGrammar);
224 }
225
226 Logger.debug(method, 'Loaded grammar.tem.md'); // load and add the ergo files
227
228 if (template.getMetadata().getRuntime() === 'ergo') {
229 // If Ergo then also register the template
230 template.getLogicManager().addTemplateFile(templatizedGrammar, 'text/grammar.tem.md');
231 const ergoFiles = yield TemplateLoader.loadFilesContents(path, /logic[/\\].*\.ergo$/);
232 ergoFiles.forEach(file => {
233 const resolvedPath = slash(fsPath.resolve(path));
234 const resolvedFilePath = slash(fsPath.resolve(file.name));
235 const truncatedPath = resolvedFilePath.replace(resolvedPath + '/', '');
236 template.getLogicManager().addLogicFile(file.contents, truncatedPath);
237 });
238 } else {
239 // load and add compiled JS files - we assume all runtimes are JS based (review!)
240 const jsFiles = yield TemplateLoader.loadFilesContents(path, /logic[/\\].*\.js$/);
241 jsFiles.forEach(file => {
242 const resolvedPath = slash(fsPath.resolve(path));
243 const resolvedFilePath = slash(fsPath.resolve(file.name));
244 const truncatedPath = resolvedFilePath.replace(resolvedPath + '/', '');
245 template.getLogicManager().addLogicFile(file.contents, truncatedPath);
246 });
247 } // check the template
248
249
250 template.validate();
251 return template;
252 })();
253 }
254 /**
255 * Prepare the text for parsing (normalizes new lines, etc)
256 * @param {string} input - the text for the clause
257 * @return {string} - the normalized text for the clause
258 */
259
260
261 static normalizeText(input) {
262 // we replace all \r and \n with \n
263 let text = input.replace(/\r/gm, '');
264 return text;
265 }
266
267}
268
269module.exports = TemplateLoader;
\No newline at end of file