111 lines
3.0 KiB
TypeScript
111 lines
3.0 KiB
TypeScript
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)
|
|
}
|