From b054e5d38b8dc03aff3f2b8cfba5ca10acbcdc20 Mon Sep 17 00:00:00 2001 From: Evgeny Metelkin <evgeny.metelkin@gmail.com> Date: Thu, 5 Sep 2024 15:03:05 +0300 Subject: [PATCH] use export only in builder --- bin/heta-build.js | 2 +- cases/0-hello-world/platform copy.yml | 56 +++++++++++++++++++++++++++ cases/0-hello-world/platform.yml | 48 +---------------------- package.json | 2 +- src/browser.js | 4 +- src/builder/index.js | 45 ++++++++++++++------- src/container/actions.js | 25 ++++++------ src/container/main.js | 10 +---- src/index.js | 4 +- src/webpack.js | 4 +- test/cases/0.js | 16 ++++---- test/cases/12.js | 4 +- test/cases/14.js | 2 +- test/cases/6.js | 4 +- test/export/export-check.js | 19 ++++----- test/space/index.js | 10 +---- 16 files changed, 140 insertions(+), 115 deletions(-) create mode 100644 cases/0-hello-world/platform copy.yml diff --git a/bin/heta-build.js b/bin/heta-build.js index 34b3c7a7..b2b0a767 100644 --- a/bin/heta-build.js +++ b/bin/heta-build.js @@ -3,7 +3,7 @@ const { Command } = require('commander'); const program = new Command(); const fs = require('fs'); const path = require('path'); -const { Builder } = require('../src/builder'); +const { Builder } = require('../src'); const { load } = require('js-yaml'); // https://www.npmjs.com/package/js-yaml const _merge = require('lodash/merge'); const semver = require('semver'); diff --git a/cases/0-hello-world/platform copy.yml b/cases/0-hello-world/platform copy.yml new file mode 100644 index 00000000..6848be20 --- /dev/null +++ b/cases/0-hello-world/platform copy.yml @@ -0,0 +1,56 @@ +id: mm +builderVersion: '*' +options: + logLevel: info + debug: false + unitsCheck: true +importModule: + type: heta + source: src/index.heta +export: + - format: HetaCode + filepath: heta_code + id: heta_code + - format: SBML + filepath: mm_sbml_l2v4 + version: L2V4 + spaceFilter: mm + - format: SBML + filepath: mm_sbml_l3v1 + version: L3V1 + spaceFilter: mm + - format: Simbio + filepath: mm_simbio + spaceFilter: mm + - format: JSON + filepath: full_json + spaceFilter: mm + - format: YAML + filepath: full_yaml + spaceFilter: mm + - format: SLV + filepath: mm_slv + version: 25 + spaceFilter: mm + - format: Julia + filepath: mm_julia + spaceFilter: mm + - format: DBSolve + filepath: mm_dbsolve + spaceFilter: mm + - format: Mrgsolve + filepath: mm_mrg + spaceFilter: mm + - format: Matlab + filepath: matlab1 + spaceFilter: mm + - format: XLSX + filepath: table + spaceFilter: mm + - format: Table + filepath: table2 + spaceFilter: mm + bookType: biff8 + - format: Dot + filepath: graph + - format: Summary diff --git a/cases/0-hello-world/platform.yml b/cases/0-hello-world/platform.yml index 6848be20..ade5d04c 100644 --- a/cases/0-hello-world/platform.yml +++ b/cases/0-hello-world/platform.yml @@ -7,50 +7,4 @@ options: importModule: type: heta source: src/index.heta -export: - - format: HetaCode - filepath: heta_code - id: heta_code - - format: SBML - filepath: mm_sbml_l2v4 - version: L2V4 - spaceFilter: mm - - format: SBML - filepath: mm_sbml_l3v1 - version: L3V1 - spaceFilter: mm - - format: Simbio - filepath: mm_simbio - spaceFilter: mm - - format: JSON - filepath: full_json - spaceFilter: mm - - format: YAML - filepath: full_yaml - spaceFilter: mm - - format: SLV - filepath: mm_slv - version: 25 - spaceFilter: mm - - format: Julia - filepath: mm_julia - spaceFilter: mm - - format: DBSolve - filepath: mm_dbsolve - spaceFilter: mm - - format: Mrgsolve - filepath: mm_mrg - spaceFilter: mm - - format: Matlab - filepath: matlab1 - spaceFilter: mm - - format: XLSX - filepath: table - spaceFilter: mm - - format: Table - filepath: table2 - spaceFilter: mm - bookType: biff8 - - format: Dot - filepath: graph - - format: Summary + \ No newline at end of file diff --git a/package.json b/package.json index 8cdc2947..1b59f135 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "Programming platform for Quantitative Systems Pharmacology modeling in NodeJS", "main": "src/index.js", "scripts": { - "test:dev": "mocha test/cases/0 --config=./test/.mocharc.json", + "test:dev": "mocha test/export/export-check --config=./test/.mocharc.json", "test": "mocha test --config=./test/.mocharc.json", "jsdoc": "jsdoc -r -c .jsdoc.json --readme api-references.md -d docs/dev src", "test:cov": "nyc --reporter=lcov npm run test", diff --git a/src/browser.js b/src/browser.js index 33695132..6d24fd26 100644 --- a/src/browser.js +++ b/src/browser.js @@ -9,6 +9,7 @@ or precompilation of templates is also possible */ +const { Builder } = require('./builder'); const Container = require('./container'); const coreItems = require('./container/core-items'); const ModuleSystem = require('./module-system'); @@ -44,7 +45,7 @@ global.compiledTemplates = { 'slv-template.slv.njk': nunjucksEnv.getTemplate('slv-template.slv.njk'), }; -Container._exportClasses = { +Builder._exportClasses = { DBSolve: require('./dbsolve-export'), YAML: require('./yaml-export'), JSON: require('./json-export'), @@ -63,6 +64,7 @@ Container._exportClasses = { }; module.exports = { + Builder, Container, coreItems, nunjucksEnv, diff --git a/src/builder/index.js b/src/builder/index.js index 3e7b10c7..8866bdeb 100644 --- a/src/builder/index.js +++ b/src/builder/index.js @@ -3,7 +3,8 @@ const fs = require('fs-extra'); const declarationSchema = require('./declaration-schema'); const Ajv = require('ajv'); const ajv = new Ajv({ useDefaults: true }); //.addSchema(declarationSchema); -const { Container, HetaLevelError } = require('../index'); +const Container = require('../container'); +const HetaLevelError = require('../heta-level-error'); const ModuleSystem = require('../module-system'); const { StdoutTransport } = require('../logger'); @@ -32,16 +33,20 @@ const { StdoutTransport } = require('../logger'); * Calculated from `declaration.options.logPath`. * @property ... Other properties inherit from `declaration` object, see * [CLI references]{@link https://hetalang.github.io/#/heta-compiler/cli-references?id=declaration-file-format} + * @property {object} _exportClasses map-like structure for storing all available constructors describing `_Export`s. + * @property {object[]} export Storage for `_Export` instances. */ class Builder { constructor(declaration, coreDirname = '.'){ // create container this.container = new Container(); + this.container._builder = this; // back reference to parent builder this.export = []; // storing Export objects + this.exportClasses = {}; // storing Export classes bound to builder (XXX: maybe it must be static) // set transport and logger this.logger = this.container.logger; - let minLogLevel = declaration.options?.logLevel || 'info'; + let minLogLevel = declaration?.options?.logLevel || 'info'; this.logger.addTransport(new StdoutTransport(minLogLevel)); // check based on schema @@ -66,13 +71,28 @@ class Builder { this.logger.info(`Builder initialized in directory "${this._coreDirname}".`); if (this.id) this.logger.info(`Platform id: "${this.id}"`); - // index file not found - //let indexFilepath = path.resolve(coreDirname, declaration.importModule.source); - //if (!fs.existsSync(indexFilepath)) { - // this.logger.error(`Index file "${indexFilepath}" does not exist.`, {type: 'BuilderError'}); - //} + // create "export" classes bound to this container + Builder._exportClasses && Object.entries(Builder._exportClasses).forEach(([key, _Class]) => { + this.exportClasses[key] = class extends _Class {}; + this.exportClasses[key].prototype._builder = this; // XXX: not sure it required + this.exportClasses[key].prototype._container = this.container; // XXX: not sure it required + }); + + this.exportArray = []; + // create "export" instances + declaration.export && declaration.export.forEach((exportItem) => { + let ExportClass = this.exportClasses.hasOwnProperty(exportItem.format) + && this.exportClasses[exportItem.format]; + if (ExportClass) { + this.exportArray.push(new ExportClass(exportItem)); + } else { + this.logger.error(`Export format "${exportItem.format}" is not supported.`, {type: 'BuilderError'}); + } + }); } + static _exportClasses = {}; // storing abstract Export classes + /** * The method runs building of a platform declared with `Builder` object. * If the execution throws an error platform building will stop. @@ -184,11 +204,10 @@ class Builder { * * @method Builder#exportMany */ - exportMany(){ - let exportElements = this.container.exportArray; - this.logger.info(`Start exporting to files, total: ${exportElements.length}.`); + exportMany() { + this.logger.info(`Start exporting to files, total: ${this.exportArray.length}.`); - exportElements.forEach((exportItem) => _makeAndSave(exportItem, this._distDirname)); + this.exportArray.forEach((exportItem) => _makeAndSave(exportItem, this._distDirname)); } /** @@ -199,7 +218,7 @@ class Builder { */ exportJuliaOnly(){ // create export without putting it to exportArray - let Julia = this.container.classes['Julia']; + let Julia = this.exportClasses['Julia']; let exportItem = new Julia({ format: 'Julia', filepath: '_julia' @@ -209,7 +228,7 @@ class Builder { } } -function _makeAndSave(exportItem, pathPrefix){ +function _makeAndSave(exportItem, pathPrefix) { let logger = exportItem._container.logger; let absPath = path.resolve(pathPrefix, exportItem.filepath); let msg = `Exporting to "${absPath}" of format "${exportItem.format}"...`; diff --git a/src/container/actions.js b/src/container/actions.js index e0ab6e9b..bf7e6696 100644 --- a/src/container/actions.js +++ b/src/container/actions.js @@ -20,26 +20,29 @@ const reservedWords = [ * * @returns {AbstractExport} The created object. */ -Container.prototype.export = function(q = {}, isCore = false){ +// TODO: this is part which will be removed later +// invisible => deprecated => removed +Container.prototype.export = function(q = {}, isCore = false) { + let { exportClasses, exportArray } = this._builder; if (q.format === undefined) { this.logger.error( 'Empty "format" option in #export', {type: 'QError'} ); - return; + return; // BRAKE } - if (typeof this.classes[q.format] !== 'function') { - this.logger.error( - `Unknown format "${q.format}" in #export action.`, - {type: 'QError'} - ); - return; + if (typeof exportClasses[q.format] !== 'function') { + this.logger.error(`Unknown format "${q.format}" in #export action.`, {type: 'QError'}); + + return; // BRAKE } // create and push to storage - let exportInstance = new this.classes[q.format](q, isCore); - if (!exportInstance.errored) this.exportArray.push(exportInstance); - + let exportInstance = new exportClasses[q.format](q, isCore); + if (!exportInstance.errored) { + exportArray.push(exportInstance); + } + return exportInstance; }; diff --git a/src/container/main.js b/src/container/main.js index 6e83eccc..b2099822 100644 --- a/src/container/main.js +++ b/src/container/main.js @@ -3,7 +3,6 @@ const { Top } = require('../core/top'); const { UnitDef } = require('../core/unit-def'); const { FunctionDef } = require('../core/function-def'); const { Scenario } = require('../core/scenario'); -// const { AbstractExport } = require('../abstract-export'); // Component classes const { Component } = require('../core/component'); const { Record } = require('../core/record'); @@ -38,7 +37,6 @@ const TopoSort = require('@insysbio/topo-sort'); * @property {Logger} logger object providing transport of errors, warnings and info messages on Heta platform level. * @property {object[]} defaultLogs Default storage of errors which will be used for diagnostics. * The {@link JSONTransport} is used here. - * @property {object[]} exportArray Storage for `_Export` instances. * @property {Map<string,UnitDef>} unitDefStorage Storage for `UnitDef` instances. Key is a string identifier. * @property {Map<string,FunctionDef>} functionDefStorage Storage for `FunctionDef` instances. Key is a string identifier. * @property {Map<string,Scenario>} scenarioStorage Storage for `Scenario` instances. Key is a string identifier. @@ -46,7 +44,7 @@ const TopoSort = require('@insysbio/topo-sort'); * There is a default namespace with identifier `nameless` which will be used as a default namespace * for all components where namespace name is not set. * @property {object} _componentClasses map-like structure for storing all available constructors for `Component`s. - * @property {object} _exportClasses map-like structure for storing all available constructors describing `_Export`s. + * @property {object} _builder reference to the parent builder object (if exists). */ class Container { /* constructor can be run many times */ @@ -62,12 +60,6 @@ class Container { this.classes.FunctionDef.prototype._container = this; this.classes.Scenario = class extends Scenario {}; this.classes.Scenario.prototype._container = this; - // create "export" classes bound to this container - Container._exportClasses && Object.entries(Container._exportClasses) - .forEach(([key, _Class]) => { - this.classes[key] = class extends _Class {}; - this.classes[key].prototype._container = this; - }); // logger this.logger = new Logger(); diff --git a/src/index.js b/src/index.js index 6c81be71..6bac93a5 100644 --- a/src/index.js +++ b/src/index.js @@ -1,3 +1,4 @@ +const { Builder } = require('./builder'); const Container = require('./container'); const coreItems = require('./container/core-items'); const ModuleSystem = require('./module-system'); @@ -33,7 +34,7 @@ global.compiledTemplates = { 'slv-template.slv.njk': nunjucksEnv.getTemplate('slv-template.slv.njk'), }; -Container._exportClasses = { +Builder._exportClasses = { DBSolve: require('./dbsolve-export'), YAML: require('./yaml-export'), JSON: require('./json-export'), @@ -52,6 +53,7 @@ Container._exportClasses = { }; module.exports = { + Builder, Container, coreItems, nunjucksEnv, diff --git a/src/webpack.js b/src/webpack.js index de29713a..23a04e7d 100644 --- a/src/webpack.js +++ b/src/webpack.js @@ -17,6 +17,7 @@ module: { ] } */ +const { Builder } = require('./builder'); const Container = require('./container'); const coreItems = require('./container/core-items'); const ModuleSystem = require('./module-system'); @@ -49,7 +50,7 @@ global.compiledTemplates = { 'slv-template.slv.njk': require('./templates/slv-template.slv.njk'), }; -Container._exportClasses = { +Builder._exportClasses = { DBSolve: require('./dbsolve-export'), YAML: require('./yaml-export'), JSON: require('./json-export'), @@ -68,6 +69,7 @@ Container._exportClasses = { }; module.exports = { + Builder, Container, coreItems, //nunjucksEnv, diff --git a/test/cases/0.js b/test/cases/0.js index aa5b8375..ae3c4526 100644 --- a/test/cases/0.js +++ b/test/cases/0.js @@ -53,7 +53,7 @@ describe('Testing "cases/0-hello-world"', () => { }); it('Run #export {format: SBML}, check and compare.', () => { - let sbml_export = b.container.exportArray + let sbml_export = b.exportArray .find(x => x.filepath === 'mm_sbml_l2v4'); let code = sbml_export.makeText(true)[0].content; expect(code).xml.to.to.be.valid(); @@ -61,7 +61,7 @@ describe('Testing "cases/0-hello-world"', () => { }); it('Run #export {format: SBML}, check and compare.', () => { - let sbml_export = b.container.exportArray + let sbml_export = b.exportArray .find(x => x.filepath === 'mm_sbml_l3v1'); let code = sbml_export.makeText(true)[0].content; expect(code).xml.to.to.be.valid(); @@ -69,7 +69,7 @@ describe('Testing "cases/0-hello-world"', () => { }); it('Run #export {format: JSON}, check and compare.', () => { - let json_export = b.container.exportArray + let json_export = b.exportArray .find(x => x.filepath === 'full_json'); let code = json_export.makeText(true)[0].content; let obj = JSON.parse(code); @@ -78,7 +78,7 @@ describe('Testing "cases/0-hello-world"', () => { }); it('Run #export {format: YAML}, check and compare.', () => { - let yaml_export = b.container.exportArray.find(x => x.filepath === 'full_yaml'); + let yaml_export = b.exportArray.find(x => x.filepath === 'full_yaml'); let code = yaml_export.makeText(true)[0].content; let obj = load(code); expect(obj).to.be.deep.equal(yaml_correct); @@ -86,7 +86,7 @@ describe('Testing "cases/0-hello-world"', () => { }); it('Run #export {format: SLV}, check and compare.', () => { - let slv_export = b.container.exportArray.find(x => x.filepath === 'mm_slv'); + let slv_export = b.exportArray.find(x => x.filepath === 'mm_slv'); let code = slv_export.makeText(true)[0].content; let obj = slvParse.parse(code); expect(obj).to.be.deep.equal(slv_correct); @@ -94,7 +94,7 @@ describe('Testing "cases/0-hello-world"', () => { }); it('Run #export {format: XLSX}, check and compare.', () => { - let xlsx_export = b.container.exportArray.find(x => x.filepath === 'table'); + let xlsx_export = b.exportArray.find(x => x.filepath === 'table'); let code = xlsx_export.makeSheet(true); // check only sheet #0 // check number of sheets @@ -118,7 +118,7 @@ describe('Testing "cases/0-hello-world"', () => { }); it('Run #export {format: Mrgsolve}, check and compare.', () => { - let mm_mrg = b.container.exportArray.find(x => x.filepath === 'mm_mrg'); + let mm_mrg = b.exportArray.find(x => x.filepath === 'mm_mrg'); let code = mm_mrg.makeText(true); // compare model.cpp text content expect(code[0].pathSuffix).to.be.equal('/mm.cpp'); @@ -127,7 +127,7 @@ describe('Testing "cases/0-hello-world"', () => { }); it('Run #export {format: Julia}, check and compare.', () => { - let mm_mrg = b.container.exportArray.find(x => x.filepath === 'mm_julia'); + let mm_mrg = b.exportArray.find(x => x.filepath === 'mm_julia'); let code = mm_mrg.makeText(true); // compare model.js text content expect(code[0].pathSuffix).to.be.equal('/model.jl'); diff --git a/test/cases/12.js b/test/cases/12.js index 8f796af1..7e3ea61c 100644 --- a/test/cases/12.js +++ b/test/cases/12.js @@ -35,7 +35,7 @@ describe('Testing "cases/12-to-sbml"', () => { }); it('Run @SBMLExport, check and compare.', () => { - const SBMLExport = b.container.classes.SBML; + const SBMLExport = b.exportClasses.SBML; let sbml_export = new SBMLExport({spaceFilter: 'first', filepath: 'yyy'}); expect(sbml_export).not.to.has.property('errored', true); @@ -46,7 +46,7 @@ describe('Testing "cases/12-to-sbml"', () => { }); it('Run @JSONExport, check and compare.', () => { - const JSONExport = b.container.classes.JSON; + const JSONExport = b.exportClasses.JSON; let json_export = new JSONExport({spaceFilter: 'first', filepath: 'xxx'}); expect(json_export).not.to.has.property('errored', true); diff --git a/test/cases/14.js b/test/cases/14.js index 6e7844e5..7e8d5277 100644 --- a/test/cases/14.js +++ b/test/cases/14.js @@ -26,7 +26,7 @@ describe('Case #14: testing SBML module with units', () => { }); it('compare JSON export', () => { - let json_export = b.container.exportArray[0]; + let json_export = b.exportArray[0]; let code = json_export.make(true)[0].content; let obj = JSON.parse(code); expect(obj).to.be.deep.equal(json_correct); diff --git a/test/cases/6.js b/test/cases/6.js index 4c24808c..0649e6b6 100644 --- a/test/cases/6.js +++ b/test/cases/6.js @@ -32,7 +32,7 @@ describe('Testing "cases/6-import"', () => { it('Run include', () => { b.run(); - exportArray = b.container.exportArray; + exportArray = b.exportArray; }); it('Run @SBMLExport, check and compare.', () => { @@ -44,7 +44,7 @@ describe('Testing "cases/6-import"', () => { }); it('Run @JSONExport, check and compare.', () => { - const JSONExport = b.container.classes.JSON; + const JSONExport = b.exportClasses.JSON; let json_export = new JSONExport({spaceFilter: 'model', filepath: 'xxx'}); expect(json_export).not.to.have.property('errored', true); diff --git a/test/export/export-check.js b/test/export/export-check.js index 7e0c2b86..c78c7ff9 100644 --- a/test/export/export-check.js +++ b/test/export/export-check.js @@ -1,12 +1,13 @@ /* global describe, it */ -const { Container } = require('../../src'); +const { Builder } = require('../../src'); const { expect } = require('chai'); describe('General argument checking', () => { - const p = new Container(); + const b = new Builder({importModule: {}}); + const p = b.container; it('Create JSON Export directly', () => { - let json_export = new p.classes.JSON({ + let json_export = new b.exportClasses.JSON({ id: 'json_export', filepath: './1.json', omit: ['num'], @@ -32,7 +33,7 @@ describe('General argument checking', () => { expect(p.logger).to.have.property('hasErrors').false; expect(json_export).to.have.property('filepath', './_1.json'); expect(json_export).to.have.property('format', 'JSON'); - expect(json_export).to.be.instanceOf(p.classes.JSON); + expect(json_export).to.be.instanceOf(b.exportClasses.JSON); p.logger.resetErrors(); }); @@ -61,7 +62,7 @@ describe('General argument checking', () => { }); expect(p.logger).to.have.property('hasErrors').false; expect(json_export).to.have.property('filepath', 'json'); - expect(json_export).to.be.instanceOf(p.classes.JSON); + expect(json_export).to.be.instanceOf(b.exportClasses.JSON); p.logger.resetErrors(); }); @@ -71,7 +72,7 @@ describe('General argument checking', () => { filepath: './@1.xxx' }); expect(p.logger).to.have.property('hasErrors').true; - expect(json_export).to.be.instanceOf(p.classes.JSON); + expect(json_export).to.be.instanceOf(b.exportClasses.JSON); p.logger.resetErrors(); }); @@ -83,7 +84,7 @@ describe('General argument checking', () => { }); expect(p.logger).to.have.property('hasErrors').true; expect(json_export).to.not.have.property('omit'); - expect(json_export).to.be.instanceOf(p.classes.JSON); + expect(json_export).to.be.instanceOf(b.exportClasses.JSON); p.logger.resetErrors(); }); @@ -94,7 +95,7 @@ describe('General argument checking', () => { noUnitsExpr: 12 }); expect(p.logger).to.have.property('hasErrors').true; - expect(json_export).to.be.instanceOf(p.classes.JSON); + expect(json_export).to.be.instanceOf(b.exportClasses.JSON); p.logger.resetErrors(); }); @@ -105,7 +106,7 @@ describe('General argument checking', () => { spaceFilter: 12 }); expect(p.logger).to.have.property('hasErrors').true; - expect(json_export).to.be.instanceOf(p.classes.JSON); + expect(json_export).to.be.instanceOf(b.exportClasses.JSON); p.logger.resetErrors(); }); }); diff --git a/test/space/index.js b/test/space/index.js index 59104b6b..780c33ca 100644 --- a/test/space/index.js +++ b/test/space/index.js @@ -5,7 +5,7 @@ const { Container } = require('../../src'); describe('Testing anonymous space.', () => { it('Create element in global.', () => { - let c = new Container; + let c = new Container(); let counter = c.length; c.loadMany([ @@ -30,12 +30,6 @@ describe('Testing anonymous space.', () => { fromSpace: 'nameless', space: 'one' }, - { - action: 'export', // 5 - space: 'one', - format: 'JSON', - filepath: '1.json' - }, { id: 'p1', // 5 space: 'one', @@ -54,6 +48,6 @@ describe('Testing anonymous space.', () => { } ]); - expect(c.length - counter).to.be.eq(6 + 1); // + t @TimeScale + expect(c.length - counter).to.be.eq(5 + 1); // + t @TimeScale }); });