• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

hetalang / heta-compiler / 13140253202

04 Feb 2025 04:33PM UTC coverage: 68.878%. Remained the same
13140253202

push

github

metelkin
bug wrong path for debug

1798 of 2739 branches covered (65.64%)

Branch coverage included in aggregate %.

0 of 1 new or added line in 1 file covered. (0.0%)

3071 of 4330 relevant lines covered (70.92%)

85.75 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

78.45
/src/builder/index.js
1
const path = require('path');
1✔
2
const declarationSchema = require('./declaration-schema');
1✔
3
const { ajv } = require('../utils');
1✔
4
const Container = require('../container');
1✔
5
const HetaLevelError = require('../heta-level-error');
1✔
6
const ModuleSystem = require('../module-system');
1✔
7
const semver = require('semver');
1✔
8
const { version } = require('../../package');
1✔
9

10
/**
11
 * Auxiliary class for performing compilation and storing a platform's options.
12
 * The main purpose is a support of Heta compiler's CLI mode.
13
 *
14
 * The main method of the class is {@link Builder#run} which  
15
 * runs a sequence of compilation steps.
16
 * 
17
 * @class Builder
18
 *
19
 * @param {Object} declaration - Object representing options for builder,
20
 *  see [CLI references]{@link https://hetalang.github.io/#/heta-compiler/cli-references}
21
 *  for more information about the structure.
22
 *
23
 * @property {Container} container This is the reference to the main platform storage. 
24
 * @property {Logger} logger Reference to logger object of platform. This object can also be called with `this.container.logger`
25
 * @property {string} _distDirname Absolute path of the directory for exporting files.
26
 *    Calculated from `declaration.options.distDir` property.
27
 * @property {string} _metaDirname Absolute path of the directory for storing meta files.
28
 *    Calculated from `declaration.options.metaDir`.
29
 * @property ... Other properties inherit from `declaration` object, see 
30
 *   [CLI references]{@link https://hetalang.github.io/#/heta-compiler/cli-references?id=declaration-file-format}
31
 * @property {object} _exportClasses map-like structure for storing all available constructors describing `_Export`s.
32
 * @property {object} exportClasses the same as `_exportClasses` but bound to this builder.
33
 * @property {object[]} exportArray Storage for `_Export` instances.
34
 */
35
class Builder {
36
  constructor(
37
    declaration = {},
1✔
38
    fileReadHandler = (fn) => { throw new Error('File read is not set for Builder'); }, // must return text
✔
39
    fileWriteHandler = (fn, text) => { throw new Error('File write is not set for Builder'); }, // must return undefined
✔
40
    transportArray = [] // Builder-level Transport
10✔
41
  ) {
42

43
    // create container
44
    this.container = new Container();
10✔
45
    this.container._builder = this; // back reference to parent builder
10✔
46

47
    // set logger and transports
48
    let logger = this.logger = this.container.logger;
10✔
49
    transportArray.forEach((...args) => logger.addTransport(...args));
10✔
50

51
    // file handlers
52
    this.fileReadHandler = fileReadHandler;
10✔
53
    this.fileWriteHandler = fileWriteHandler;
10✔
54

55
    // check based on schema, use default values from schema
56
    let validate = ajv.compile(declarationSchema);
10✔
57
    let valid = validate(declaration);
10✔
58
    if (!valid) {
10✔
59
      // convert validation errors to heta errors
60
      validate.errors.forEach((x) => {
1✔
61
        logger.error(`${x.message} (${x.dataPath})`, {type: 'BuilderError', params: x.params});
1✔
62
      });
63
      throw new HetaLevelError('Wrong structure of platform file.');
1✔
64
    }
65
    
66
    // === wrong version throws, if no version stated than skip ===
67
    let satisfiesVersion = declaration.builderVersion
9!
68
      ? semver.satisfies(version, declaration.builderVersion)
69
      : true;
70
    if (!satisfiesVersion) {
9!
71
      let msg = `Version "${declaration.builderVersion}" stated in declaration file is not supported by the heta-compiler ${version}.`;
×
72
      logger.error(msg, {type: 'BuilderError'});
×
73

74
      throw new HetaLevelError('Version incompatibility.');
×
75
    }
76

77
    // assign from declaration
78
    Object.assign(this, declaration);
9✔
79

80
    // set directories
81
    this._distDirname = declaration.options.distDir;
9✔
82
    this._metaDirname = declaration.options.metaDir;
9✔
83

84
    logger.info(`Heta compiler v${version} is initialized for the platform "${this.id}"`);
9✔
85
    
86
    // create "export" classes bound to this container
87
    Object.entries(Builder._exportClasses).forEach(([key, _Class]) => {
9✔
88
      this.exportClasses[key] = class extends _Class {};
135✔
89
      this.exportClasses[key].prototype._builder = this;
135✔
90
    });
91
    
92
    this.exportArray = [];
9✔
93
    // create "export" instances
94
    declaration.export.forEach((exportItem) => {
9✔
95
      // check the filepath
96
      if (!!exportItem.filepath && path.isAbsolute(exportItem.filepath)) {
10!
97
        logger.error(`Export item property "filepath" must be relative, got ${JSON.stringify(exportItem)}.`, {type: 'BuilderError'});
×
98
      }
99

100
      // check if format is supported
101
      let ExportClass = this.exportClasses.hasOwnProperty(exportItem.format) 
10✔
102
        && this.exportClasses[exportItem.format];
103
      if (ExportClass) {
10!
104
        this.exportArray.push(new ExportClass(exportItem));
10✔
105
      } else {
106
        logger.error(`Export format "${exportItem.format}" is not supported.`, {type: 'BuilderError'});
×
107
      }
108
    });
109
  }
110

111
  static _exportClasses = {}; // storing abstract Export classes
1✔
112
  exportClasses = {}; // storing Export classes bound to builder
10✔
113

114
  /**
115
   * The method runs building of a platform declared with `Builder` object.
116
   * If the execution throws an error platform building will stop.
117
   * 
118
   * The sequence of operations is following:
119
   * 
120
   * 1. Read and parse platform modules (files).
121
   * 2. Modules integration. Creation meta files if required.
122
   * 3. Loading integrated structures into `Platform`.
123
   * 4. Setting cross-references in platform's elements.
124
   * 5. Checking circular references in mathematical expressions.
125
   * 6. Checking circular unitDef references. Checking circular functionDef rferences.
126
   * 7. Checking left and right side units compatibility for mathematical expressions.
127
   * 8. Checking unit\'s terms.
128
   * 9. Export of a platform to series of formats. 
129
   *    Depending on the declaration file it runs {@link Builder#exportMany} or {@link Builder#exportJuliaOnly}.
130
   * 
131
   * @method Builder#run
132
   * 
133
   */
134
  run() {
135
    // 1. Parsing
136
    let ms = new ModuleSystem(this.logger, this.fileReadHandler);
8✔
137
    if (path.isAbsolute(this.importModule.source)) {
8!
138
      this.logger.error(`Import module source must be relative, got "${this.importModule.source}".`, {type: 'BuilderError'});
×
139
      throw new HetaLevelError('Import module source must be relative.');
×
140
    }
141
    ms.addModuleDeep(this.importModule.source, this.importModule.type, this.importModule);
8✔
142

143
    // 2. Modules integration
144
    if (this.options.debug) {
8!
145
      Object.getOwnPropertyNames(ms.moduleCollection).forEach((sourcePath) => { // relative path, i.e. src/index.heta
×
146
        let fullPath = path.join(this._metaDirname, sourcePath + '.json');
×
NEW
147
        let str = JSON.stringify(ms.moduleCollection[sourcePath], null, 2);
×
148
        this.fileWriteHandler(fullPath, str);
×
149
        this.logger.info(`Meta file was saved to ${fullPath}`);
×
150
      });
151
    }
152
    let qArr = ms.integrate();
8✔
153

154
    if (qArr.length > 0) {
8✔
155
      // 3. Translation
156
      this.container.loadMany(qArr, false);
7✔
157

158
      // 4. Binding
159
      this.logger.info('Setting references in elements, total length ' + this.container.length);
7✔
160
      this.container.knitMany();
7✔
161

162
      // 5. Circular start_ and ode_
163
      this.logger.info('Checking for circular references in Records.');
7✔
164
      this.container.checkCircRecord();
7✔
165

166
      // 6. check circ UnitDef & FunctionDef
167
      this.container.checkCircUnitDef();
7✔
168
      this.container.checkCircFunctionDef();
7✔
169
    } else {
170
      this.logger.warn('Empty platform. No elements were loaded.');
1✔
171
    }
172

173
    // === STOP if errors ===
174
    if (!this.logger.hasErrors) {
8✔
175
      // 7. Units checking
176
      if (this.options.unitsCheck) {
7!
177
        this.logger.info('Checking unit\'s consistency.');
×
178
        this.container.checkUnits();
×
179
      } else {
180
        this.logger.warn('Units checking skipped. To turn it on set "unitsCheck: true" in declaration.');
7✔
181
      }
182

183
      // 8. Terms checking
184
      this.logger.info('Checking unit\'s terms.');
7✔
185
      this.container.checkTerms();
7✔
186

187
      // 9. Exports
188
      this.exportMany();
7✔
189
    } else {
190
      this.logger.warn('Units checking and export were skipped because of errors in compilation.');
1✔
191
    }
192

193
    return this;
8✔
194
  }
195

196
  /**
197
   * Run exporting of files based on components of `this.container.exportArray`.
198
   * 
199
   * @method Builder#exportMany
200
   */
201
  exportMany() {
202
    this.logger.info(`Start exporting to files, total: ${this.exportArray.length}.`);
7✔
203

204
    this.exportArray.forEach((exportItem) => _makeAndSave(exportItem, this._distDirname));
10✔
205
  }
206
}
207

208
function _makeAndSave(exportItem, pathPrefix) {
209
  let { logger, fileWriteHandler } = exportItem._builder;
10✔
210
  let absPath = path.join(pathPrefix, exportItem.filepath);
10✔
211
  let msg = `Exporting to "${absPath}" of format "${exportItem.format}"...`;
10✔
212
  logger.info(msg);
10✔
213

214
  exportItem.make().forEach((out) => {
10✔
215
    let filePath = [absPath, out.pathSuffix].join('');
12✔
216
    try {
12✔
217
      fileWriteHandler(filePath, out.content);
12✔
218
    } catch (err) {
219
      let msg =`Heta compiler cannot export to file: "${err.path}": ${err.message}`;
×
220
      logger.error(msg, {type: 'ExportError'});
×
221
    }
222
  });
223
}
224

225
module.exports = {
1✔
226
  Builder
227
};
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc