runkit-plus-ultra
Version:
Runkit tools for loading TypeScript, ES Modules and more. Makes testing Isomorphic code easyer, lets you use unstable features and import modules from git.
128 lines (119 loc) • 4.18 kB
JavaScript
// Todo, depends on PWD to determine node_modules location
// This makes testing hard.
// This might work: node -e 'console.log(require.resolve("@babel/core"))'
import dep from 'runkit-plus-ultra'
import fs from 'fs'
import { execSync } from 'child_process'
import os from 'os'
// import { URL } from 'url'
const utils = {} // hoist
const cm = {
root: process.cwd(),
NS: "runkit-plus-ultra",
// Warning: Accessing non-existent property 'version' of module exports inside circular dependency
// depVer: dep.version,
defaultEnv: {
NODE_OPTIONS: '--no-warnings --loader ts-node/esm/transpile-only'
}
}
cm.workdir = `${cm.root}/.${cm.NS}`
cm.myScripts = `${cm.workdir}/internal`
export default class OmniLoader {
constructor (argsRaw) {
const args = argsRaw || { env: {} }
Object.assign(this, {
auto: true,
nodeModules: '',
runKitVersion: '',
vcsVersion: '',
promise: new Promise((resolve, reject) => resolve()),
scriptSpecs: [],
env: cm.defaultEnv
}, args)
Object.assign(this.env, args.env)
console.debug(`cm.root is ${cm.root}`)
if (this.auto) { // todo seperate prepare function and require promise
this.prepareBasics()
this.prepareTSNode()
this.saveCode()
this.runCode()
}
}
// get promise {}
// Use promise to allow async when { async: true } is set
// Otherwise block until async resolves.
static getPkgJson(path) {
return JSON.parse(fs.readFileSync(`${path}/package.json`).toString())
}
static getModVer(path) {
return utils.getPkgJson(path).version
}
static getModRepo(path) { // https://docs.npmjs.com/cli/v7/configuring-npm/package-json#repository
const repo = utils.getPkgJson(path).repository || {}
if (typeof repo.url === 'undefined') throw new Error('CANT_READ_MOD_REPO_URL')
return repo.url
}
prepareBasics() {
execSync(`rm -r "${cm.workdir}" || :`)
execSync(`mkdir -p "${cm.workdir}"`)
execSync(`cd "${cm.workdir}" npm init --yes ${cm.NS}`)
execSync(`cd "${cm.workdir}" && npm config set type=module`)
if (process.env.RUNKIT_HOST) {
// Create a symlink to the real node_modules under our work directory
const upSince = Math.round(Date.now() / 1000 - os.uptime()) * 1000
execSync(`ln -s /app/available_modules/${upSince}/ "${cm.root}/node_modules" || :`)
} else {
console.debug('Not running in Runkit, not creating node_modules link.')
}
execSync(`mkdir -p "${cm.myScripts}"`)
}
prepareTSNode() {
const tsConfig = {
compilerOptions: {
"target": "ESnext",
"module": "ESNext",
"allowJs": true,
"moduleResolution": "node",
"allowSyntheticDefaultImports": true
}
}
fs.writeFileSync(`${cm.workdir}/tsconfig.json`, JSON.stringify(tsConfig, null, 2))
}
/* canned due to overhead
loadLatest(modName) {
const modIPath = `${cm.myScripts}/${modName}`
// const modRepo = utils.getModRepo(modNMPath)
execSync(`git clone "${modRepo}" "${cm.myScripts}/${modName}"`)
this.vcsVersion = utils.getModVer(modIPath)
}*/
getScriptSpecs (scriptSpecsRaw) { return scriptSpecsRaw || this.scriptSpecs }
saveCode(scriptSpecsRaw) {
for (const spec of this.getScriptSpecs(scriptSpecsRaw)) {
fs.writeFileSync(`${cm.workdir}/${spec.path}`, spec.code)
}
}
getEnvString() {
const envVars = Object.keys(this.env)
const resultTokens = []
for (const envK of envVars) {
resultTokens.push(`${envK}="${this.env[envK]}"`)
}
return resultTokens.join(' ')
}
runCode() {
const results = []
for (const spec of this.scriptSpecs) {
results.push({
path: spec.path,
output: execSync(`cd "${cm.workdir}" && env ${this.getEnvString()} node ${spec.path}`).toString()
})
}
return results
}
}
Object.assign(utils, ((ClassRef) => { // returns static Fn's as Object entries
const pNames = Object.getOwnPropertyNames(ClassRef)
// const staticFnNames = pNames.filter(n => typeof ClassRef[n] === 'function')
const map = pNames.map(n => [n, ClassRef[n]])
return Object.fromEntries(map)
})(OmniLoader))