import { existsSync, readFileSync } from 'fs' import { dirname, join, parse } from 'path' import { REPLServer } from 'repl' import { pathToFileURL } from 'url' import { runInContext, createContext, RunningScriptOptions, Script } from 'vm' import { logv } from './util' import { getArgs } from './util/args' import { readScript, supportedExt } from './build' import { runRepl } from './repl' // TODO possible `export` solution // i might be able to convert TS to CJS and custom define `module.exports` into something i can pass to the REPL for context export function runVM() { const Options = getArgs() const path = parse(Options.script) const file = join(path.dir, path.base) const content = readScript(file) const loader = supportedExt(file) if (!loader) { throw new Error(`Unsupported file type: ${file}`) } const context: any = { ...global, __dirname: process.cwd(), __filename: join(process.cwd(), file), // exports: {}, // module, console, require: (module: string) => { logv(`Requiring ${module}`) try { return require(module) } catch (e) { if (module.startsWith('.')) { return require(join(process.cwd(), path.dir, module)) } else { return require(join(process.cwd(), 'node_modules', module)) } } }, } logv(`Running ${file}`) context.__FILE_NODE__INTERNAL__ = function (cont: any, exp: any, mod: any) { logv(`Done`) let context = { ...cont, ...exp, ...mod.exports, } let exported = Object.keys({ ...exp, ...mod.exports }) runRepl(context, loader, exported) } REPLServer.prototype.context const script = `(async function() { const exports = {} const module = {exports: {}} ${content} __FILE_NODE__INTERNAL__(this, exports, module); })().catch(e => {console.error('script error', e)})` logv(script) createContext(context) runInContext(script, context, { filename: path.base, importModuleDynamically, } as RunningScriptOptions) } function importModuleDynamically( module: string, _script: Script, _importAssertions: any ) { const Options = getArgs() logv(`Importing ${module}`) let modulePath = '' if (module.startsWith('.')) { modulePath = join(process.cwd(), dirname(Options.script), module) } else { modulePath = join(process.cwd(), 'node_modules', module) } //windows `C:` if (/^[a-z]:\\/i.test(modulePath)) modulePath = modulePath.substring(2) if (existsSync(join(modulePath, 'package.json'))) { let { main } = JSON.parse( readFileSync(join(modulePath, 'package.json')).toString() ) modulePath = join(modulePath, main) } modulePath = pathToFileURL(modulePath).href return import(modulePath) }