@accordproject/cicero-core
Version:
Cicero Core - Implementation of Accord Protocol Template Specification
275 lines (252 loc) • 12.3 kB
JavaScript
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
;
function 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); } }
function _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); }); }; }
var fs = require('fs');
var fsPath = require('path');
var slash = require('slash');
var JSZip = require('jszip');
var xregexp = require('xregexp');
var languageTagRegex = require('ietf-language-tag-regex');
var DefaultArchiveLoader = require('./loaders/defaultarchiveloader');
var Logger = require('@accordproject/ergo-compiler').Logger;
var FileLoader = require('@accordproject/ergo-compiler').FileLoader;
// Matches 'sample.md' or 'sample_TAG.md' where TAG is an IETF language tag (BCP 47)
var IETF_REGEXP = languageTagRegex({
exact: false
}).toString().slice(1, -2);
var SAMPLE_FILE_REGEXP = xregexp('text[/\\\\]sample(_(' + IETF_REGEXP + '))?.md$');
/**
* A utility class to create templates from data sources.
* @class
* @private
* @abstract
*/
class TemplateLoader extends FileLoader {
/**
* Create a template from an archive.
* @param {*} Template - the type to construct
* @param {Buffer} buffer - the buffer to a Cicero Template Archive (cta) file
* @param {object} options - additional options
* @return {Promise<Template>} a Promise to the template
*/
static fromArchive(Template, buffer, options) {
return _asyncToGenerator(function* () {
var method = 'fromArchive';
var zip = yield JSZip.loadAsync(buffer);
// const allFiles = await TemplateLoader.loadZipFilesContents(zip, /.*/);
// console.log(allFiles);
var ctoModelFiles = [];
var ctoModelFileNames = [];
var sampleTextFiles = {};
var readmeContents = yield TemplateLoader.loadZipFileContents(zip, 'README.md');
var logo = yield TemplateLoader.loadZipFileBuffer(zip, 'logo.png');
var sampleFiles = yield TemplateLoader.loadZipFilesContents(zip, SAMPLE_FILE_REGEXP);
sampleFiles.forEach( /*#__PURE__*/function () {
var _ref = _asyncToGenerator(function* (sampleFile) {
var matches = sampleFile.name.match(SAMPLE_FILE_REGEXP);
var locale = 'default';
// Locale match found
if (matches !== null && matches[2]) {
locale = matches[2];
}
sampleTextFiles[locale] = sampleFile.contents;
});
return function (_x) {
return _ref.apply(this, arguments);
};
}());
var requestContents = yield TemplateLoader.loadZipFileContents(zip, 'request.json', true);
var packageJsonObject = yield TemplateLoader.loadZipFileContents(zip, 'package.json', true, true);
var grammar = yield TemplateLoader.loadZipFileContents(zip, 'text/grammar.tem.md', false, false);
var authorSignature = yield TemplateLoader.loadZipFileContents(zip, 'signature.json', true, false);
Logger.debug(method, 'Looking for model files');
var ctoFiles = yield TemplateLoader.loadZipFilesContents(zip, /model[/\\].*\.cto$/);
ctoFiles.forEach( /*#__PURE__*/function () {
var _ref2 = _asyncToGenerator(function* (file) {
ctoModelFileNames.push(file.name);
ctoModelFiles.push(file.contents);
});
return function (_x2) {
return _ref2.apply(this, arguments);
};
}());
// create the template
var template = new (Function.prototype.bind.call(Template, null, packageJsonObject, readmeContents, sampleTextFiles, requestContents, logo, options, authorSignature))();
// add model files
Logger.debug(method, 'Adding model files to model manager');
template.getModelManager().addAPModelFiles(ctoModelFiles, ctoModelFileNames, true); // Archives can always be loaded offline
Logger.debug(method, 'Setting grammar');
if (!grammar) {
throw new Error('A template must contain a grammar.tem.md file.');
} else {
var templateKind = template.getMetadata().getTemplateType() !== 0 ? 'clause' : 'contract';
template.parserManager.setTemplate(grammar);
template.parserManager.setTemplateKind(templateKind);
template.parserManager.buildParser();
}
// load and add the ergo files
if (template.getMetadata().getRuntime() === 'ergo') {
Logger.debug(method, 'Adding Ergo files to script manager');
var scriptFiles = yield TemplateLoader.loadZipFilesContents(zip, /logic[/\\].*\.ergo$/);
scriptFiles.forEach(function (obj) {
template.getLogicManager().addLogicFile(obj.contents, obj.name);
});
} else {
// load and add compiled JS files - we assume all runtimes are JS based (review!)
Logger.debug(method, 'Adding JS files to script manager');
var _scriptFiles = yield TemplateLoader.loadZipFilesContents(zip, /logic[/\\].*\.js$/);
_scriptFiles.forEach(function (obj) {
template.getLogicManager().addLogicFile(obj.contents, obj.name);
});
}
TemplateLoader.registerFormulas(template.parserManager, template.getLogicManager());
// check the integrity of the model and logic of the template
authorSignature ? template.validate({
verifySignature: true
}) : template.validate();
return template; // Returns template
})();
}
/**
* Create a template from an URL.
* @param {*} Template - the type to construct
* @param {String} url - the URL to a Cicero Template Archive (cta) file
* @param {object} options - additional options
* @return {Promise} a Promise to the template
*/
static fromUrl(Template, url, options) {
return _asyncToGenerator(function* () {
var loader = new DefaultArchiveLoader();
var buffer = yield loader.load(url, options);
return TemplateLoader.fromArchive(Template, buffer, options);
})();
}
/**
* Builds a Template from the contents of a directory.
* The directory must include a package.json in the root (used to specify
* the name, version and description of the template).
*
* @param {*} Template - the type to construct
* @param {String} path to a local directory
* @param {Object} [options] - an optional set of options to configure the instance.
* @return {Promise<Template>} a Promise to the instantiated template
*/
static fromDirectory(Template, path) {
var _arguments = arguments;
return _asyncToGenerator(function* () {
var options = _arguments.length > 2 && _arguments[2] !== undefined ? _arguments[2] : {};
var method = 'fromDirectory';
// grab the README.md
var readmeContents = yield TemplateLoader.loadFileContents(path, 'README.md');
// grab the logo.png
var logo = yield TemplateLoader.loadFileBuffer(path, 'logo.png');
// grab the request.json
var requestJsonObject = yield TemplateLoader.loadFileContents(path, 'request.json', true);
// grab the package.json
var packageJsonObject = yield TemplateLoader.loadFileContents(path, 'package.json', true, true);
// grab the sample files
Logger.debug(method, 'Looking for sample files');
var sampleFiles = yield TemplateLoader.loadFilesContents(path, SAMPLE_FILE_REGEXP);
var sampleTextFiles = {};
sampleFiles.forEach(file => {
var matches = file.name.match(SAMPLE_FILE_REGEXP);
var locale = 'default';
// Match found
if (matches !== null && matches[2]) {
locale = matches[2];
}
Logger.debug(method, 'Using sample file locale', locale);
sampleTextFiles[locale] = file.contents;
});
// grab the signature.json
var authorSignature = yield TemplateLoader.loadFileContents(path, 'signature.json', true, false);
// create the template
var template = new (Function.prototype.bind.call(Template, null, packageJsonObject, readmeContents, sampleTextFiles, requestJsonObject, logo, options, authorSignature))();
var modelFiles = [];
var modelFileNames = [];
var ctoFiles = yield TemplateLoader.loadFilesContents(path, /model[/\\].*\.cto$/);
ctoFiles.forEach(file => {
modelFileNames.push(slash(file.name));
modelFiles.push(file.contents);
});
var externalModelFiles = yield template.getModelManager().addAPModelFiles(modelFiles, modelFileNames, options && options.offline);
if (!options || !options.offline) {
externalModelFiles.forEach(function (file) {
fs.writeFileSync(path + '/model/' + file.name, file.content);
});
}
// load and add the template
var grammar = yield TemplateLoader.loadFileContents(path, 'text/grammar.tem.md', false, false);
if (!grammar) {
throw new Error('A template must either contain a grammar.tem.md file.');
} else {
var templateKind = template.getMetadata().getTemplateType() !== 0 ? 'clause' : 'contract';
template.parserManager.setTemplate(grammar);
template.parserManager.setTemplateKind(templateKind);
template.parserManager.buildParser();
Logger.debug(method, 'Loaded grammar.tem.md', grammar);
}
Logger.debug(method, 'Loaded grammar.tem.md');
// load and add the ergo files
if (template.getMetadata().getRuntime() === 'ergo') {
var ergoFiles = yield TemplateLoader.loadFilesContents(path, /logic[/\\].*\.ergo$/);
ergoFiles.forEach(file => {
var resolvedPath = slash(fsPath.resolve(path));
var resolvedFilePath = slash(fsPath.resolve(file.name));
var truncatedPath = resolvedFilePath.replace(resolvedPath + '/', '');
template.getLogicManager().addLogicFile(file.contents, truncatedPath);
});
} else {
// load and add compiled JS files - we assume all runtimes are JS based (review!)
var jsFiles = yield TemplateLoader.loadFilesContents(path, /logic[/\\].*\.js$/);
jsFiles.forEach(file => {
var resolvedPath = slash(fsPath.resolve(path));
var resolvedFilePath = slash(fsPath.resolve(file.name));
var truncatedPath = resolvedFilePath.replace(resolvedPath + '/', '');
template.getLogicManager().addLogicFile(file.contents, truncatedPath);
});
}
TemplateLoader.registerFormulas(template.parserManager, template.getLogicManager());
// check the template
authorSignature ? template.validate({
verifySignature: true
}) : template.validate();
return template;
})();
}
/**
* Prepare the text for parsing (normalizes new lines, etc)
* @param {*} parserManager - the parser manager
* @param {*} logicManager - the logic manager
*/
static registerFormulas(parserManager, logicManager) {
var formulas = parserManager.getFormulas();
formulas.forEach(x => {
logicManager.addTemplateFile(x.code, x.name);
});
}
/**
* Prepare the text for parsing (normalizes new lines, etc)
* @param {string} input - the text for the clause
* @return {string} - the normalized text for the clause
*/
static normalizeText(input) {
// we replace all \r and \n with \n
var text = input.replace(/\r/gm, '');
return text;
}
}
module.exports = TemplateLoader;