mmir-tooling
Version:
Build tools for MMIR projects
148 lines (118 loc) • 7.27 kB
text/typescript
import { CompilerCallback , StateCompilerOptions } from '../index.d';
import scxml from '@scion-scxml/scxml';
import { fixRawCompileInjectDataModule } from './scxml-fix-datamodel-declaration';
//err.tagname+'>: '+err.reason + '\n at '+file+':'+err.line+':'+err.column
type ScxmlError = {
tagname: string;
reason: string;
line: number;
column: number;
};
function isScxmlError(err: Error | ScxmlError): err is ScxmlError {
return !!( err && (err as ScxmlError).tagname && (err as ScxmlError).reason);
}
//FIXME do fix datamodel declaration in generated scxml model FIXES @scion-scxml/scxml@4.3.25
//TODO remove when not neccessary any more
fixRawCompileInjectDataModule(scxml);
// import logUtils from '../utils/log-utils';
// const log = logUtils.log;
const MODULE_CODE_PREFIX = 'var ScxmlModel = ';
//TODO support optional prepare() arguments? ->
/**
* Prepares an scxml model for execution by binding it to an execution context
* @param {prepareCallback} cb Callback to execute with the prepared model or an error
* The prepared model is a function to be passed into a SCION StateChart object
* @param {vm.Context | object} [executionContext] The execution context (e.g. v8 VM sandbox).
* If a vm.Context object is provided, then the compiled SCXML module code is evaluated in that context.
* Otherwise, if a regular object is provided, then the given object is "contextified", which means that it is passed to vm.createContext to create a new execution context. Object keys are exposed as global variables on the new context in which the generated SCXML module code is evaluated.
* @param {HostContext} [hostContext] Context provided by the interpreter host
*/
const MODULE_CODE_SUFFIX = ';\n' +
'ScxmlModel.prepare = function(prepareCallback, executionContext, hostContext) {' + //TODO support optional arguments executionContext & hostContext?
//NOTE use setTimeout() for simulating async execution:
'if(!prepareCallback) return; setTimeout(function(){prepareCallback(null, ScxmlModel)}, 0);' +
'};\nmodule.exports = ScxmlModel;';
const AMD_PREFIX = 'define(["module"], function(module){\n';
const AMD_SUFFIX = '\n});';
const STRICT_MODE = '"use strict";\n';
function toError(errList: ScxmlError | Error | Array<ScxmlError|Error>, file: string, _map: any, _meta: any): Error {
if(Array.isArray(errList)){
var sb = [];
var len = errList.length;
errList.forEach(function(err, index){
if(isScxmlError(err)){
sb.push('SCXML ERROR ('+(index+1)+') in <'+err.tagname+'>: '+err.reason + '\n at '+file+':'+err.line+':'+err.column);
} else {
sb.push(err && err.stack? err.stack : 'SCXML ERROR ('+(index+1)+') in file '+file+': '+err);
}
});
return new Error('encountered '+len+' error'+(len === 1? '' : 's')+' while parsing SCXML:\n' + sb.join('\n'));
}
return errList instanceof Error? errList : new Error('SCXML ERROR in file '+file+': '+errList);
}
/**
* compile an SCXML file to exectuable scion statechart model
*
* @param {string} content the SCXML definition as string
* @param {string} scxmlFile the path of the SCXML file (for debugging/error information)
* @param {ScxmlLoadOptions} options the ScxmlLoadOptions with property mapping (list of ScxmlOptions)
* @param {Function} callback the callback when SCXML compilation has been completed: callback(error | null, compiledStatechart, map, meta)
* @param {any} [_map] source mapping (unused)
* @param {any} [_meta] meta data (unused)
*/
function compile(content: string, scxmlFile: string, options: StateCompilerOptions, callback: CompilerCallback, _map: any, _meta: any): void {
// log('mmir-scxml-loader: resource -> ', scxmlFile);//DEBU
const i = options.mapping.findIndex(function(g){
return g.file === scxmlFile;
});
const scxmlInfo = options.mapping[i];
// log('mmir-scxml-loader: options for resource -> ', scxmlInfo);//DEBU
if(!scxmlInfo || !scxmlInfo.id){
let error: string;
if(options.mapping.length === 0){
error = 'failed to parse SCXML definition: empty list for SCXML settings [{id: "the ID", file: "the file path", ...}, ...]';
}
else if(i === -1 || !scxmlInfo){
error = 'failed to parse SCXML definition: could not find settings for SCXML in SCXML-settings list: '+JSON.stringify(options.mapping);
} else if(!scxmlInfo.id){
error = 'failed to parse SCXML definition: missing field id for SCXML: '+JSON.stringify(scxmlInfo);
} else {
error = 'failed to parse SCXML definition: invalid SCXML settings in list: '+JSON.stringify(options.mapping);
}
callback(error, null, _map, _meta);
return;/////////////// EARLY EXIT /////////////////
}
// log('mmir-scxml-loader: resource ID at '+i+' -> ', scxmlInfo.id);//, ', parsing content: ', content);//DEBU
//TODO ID optional settable via loader options?
const id = scxmlInfo.id;
const ignoreRuntimeErrors = typeof scxmlInfo.ignoreErrors === 'boolean'? scxmlInfo.ignoreErrors : (options.config && options.config.ignoreErrors === true);
// log('SCXML parsing, ignoreErrors -> ', ignoreRuntimeErrors, ', options.ignoreErrors: ', options.config, scxmlInfo)//DEBU
const moduleType = scxmlInfo.moduleType? scxmlInfo.moduleType : (options.config && options.config.moduleType);
//log('SCXML parsing, moduleType -> ', moduleType, ', options.config: ', options.config, scxmlInfo)//DEBU
const strictMode = typeof scxmlInfo.strict === 'boolean'? scxmlInfo.strict : (options.config && typeof options.config.strict === 'boolean'? options.config.strict : true);
//log('SCXML parsing, strictMode -> ', strictMode, ', options.config: ', options.config, scxmlInfo)//DEBU
scxml.documentStringToModel(id, content, function(err, model){
if(err){
callback(toError(err, scxmlFile, _map, _meta));
return;/////////////// EARLY EXIT /////////////////
}
// log('mmir-scxml-loader: successfully created model factory for '+id+'.');//DEBU
model.prepare(function(err, fnModel) {
if(err){
callback(toError(err, scxmlFile, _map, _meta));
return;/////////////// EARLY EXIT /////////////////
}
let scxmlCode: string = MODULE_CODE_PREFIX + fnModel.toString() + MODULE_CODE_SUFFIX;
if(moduleType === 'amd'){
scxmlCode = AMD_PREFIX + (strictMode? STRICT_MODE : '') + scxmlCode + AMD_SUFFIX;
}
// log('mmir-scxml-loader: created model for '+id+'.');//DEBU
callback(null, scxmlCode, _map, _meta);
});
}, {reportAllErrors: !ignoreRuntimeErrors})
return;
};
export = {
compile: compile,
// fileVersion: stateEngineFactory.getFileVersion() //TODO impl. file-version for compiled SCXML models
}