UNPKG

json-processing

Version:

JSON Processing Tool

163 lines (136 loc) 4.89 kB
import { NodeVM } from 'vm2'; import filter from './lib/filter'; import { merge } from 'lodash'; import requireDir from 'require-dir'; import { createReadStream } from 'fs'; import { resolve, dirname } from 'path'; import { isString, isEmpty } from 'lodash'; import { Stream } from 'stream'; import { transformSync } from '@babel/core' import pipelineOperatorPlugin from '@babel/plugin-proposal-pipeline-operator' export class ScriptRunner { _input: any; _global: {}; _inlineScript: any; _command: any; _commandArgs: any; _commandsPath: any; constructor(input?:Stream) { this._input = input; this._global = {}; } _createSandbox() { let sandbox = merge({ requireDir: requireDir, __transpileCode: this._transpileCode, run: (command:string, input:Stream) => this._run(command, input), from: (path:string) => createReadStream(resolve(process.cwd(), path)), select: (pathOrInput:string|Stream, input:Stream) => { let path:string; if (!input && !isString(pathOrInput)) { input = pathOrInput; path = '$'; } else if (typeof pathOrInput == 'string') { path = (pathOrInput as string) } else { throw new Error("Invalid Parameters on select()"); } path = path[0] != '$' ? `$${path}` : path; return filter(input || this._input, path); } }, this._global); return sandbox; } _createVM() { return new NodeVM({ sandbox: this._createSandbox(), require: { external: true, context: 'sandbox', builtin: ['*'] }, compiler: this._transpileCode }); } _runScript(script:string, path?:string): any { const vm:any = this._createVM() return vm.run(`'use strict';${script}`, path); } _transpileCode(code: string, filename:string): string { const transpiled = transformSync(code, { plugins: [[ pipelineOperatorPlugin, { proposal: 'minimal' } ]] }) if (!transpiled || !transpiled.code) { throw new Error('script transpilation failed') } return transpiled.code; } addGlobal(object:any) { this._global = merge({}, this._global, object || {}); return this; } addGlobalFromPath(path:string) { return this.addGlobal(this._loadPath(path)); } setInlineScript(inline:string) { this._inlineScript = inline; return this; } setCommand(command:string) { this._command = command; return this; } setCommandArgs(args:any) { this._commandArgs = args; return this; } setCommandsPath(path:string) { this._commandsPath = path; return this; } setPluginsInfo(pluginsInfo:string) { return this.addGlobal(this._loadPlugins(pluginsInfo)); } run() { if (!isEmpty(this._command)) { const commandFile:string = resolveCommand(this._commandsPath, this._command); return this._runScript(` const command = require('${commandFile}'); module.exports = (argv) => command(argv)${this._inlineScript ? this._inlineScript : ''};`, commandFile)(this._commandArgs || {}); } const inlineScript = isEmpty(this._inlineScript) ? 'select()' : this._inlineScript; return this._runScript(`module.exports = () => ${inlineScript};`)(); } _loadPlugins(pluginsInfo:string) { const homeDir = dirname(pluginsInfo); return this._runScript(` module.exports = () => { const pluginsInfo = require("${pluginsInfo}"); const result = {}; Object.keys(pluginsInfo).forEach(name => { result["$" + name] = require(pluginsInfo[name])("${homeDir}"); }); return result; } `, pluginsInfo)(); } _loadPath(path:string) { return this._runScript(` module.exports = () => requireDir('${path}', { recurse: true })`, `${path}/index.js`)(); } _run(command:string, input:Stream) { const runner = new ScriptRunner(input); runner._global = this._global; runner._command = command; runner._commandsPath = this._commandsPath; return runner.run(); } } function resolveCommand(commandsPath:string, command:string): string { if (/^.+\.js$/.test(command)) return resolve(process.cwd(), command); return resolve(commandsPath, `${command}.js`); }