File Explorer

/var/lang/lib/node_modules/npm/node_modules/promzard/lib

This explorer reads the filesystem of the server it runs on, so /workspace/user isn't present here. Browsing and the terminal still work against this server's own disk from /.

0 dirs
1 file
index.js4.6 KB · 177 lines
const fs = require('fs/promises')const { runInThisContext } = require('vm')const { promisify } = require('util')const { randomBytes } = require('crypto')const { Module } = require('module')const { dirname, basename } = require('path')const { read } = require('read') const files = {} class PromZard {  #file = null  #backupFile = null  #ctx = null  #unique = randomBytes(8).toString('hex')  #prompts = []   constructor (file, ctx = {}, options = {}) {    this.#file = file    this.#ctx = ctx    this.#backupFile = options.backupFile  }   static async promzard (file, ctx, options) {    const pz = new PromZard(file, ctx, options)    return pz.load()  }   static async fromBuffer (buf, ctx, options) {    let filename = 0    do {      filename = '\0' + Math.random()    } while (files[filename])    files[filename] = buf    const ret = await PromZard.promzard(filename, ctx, options)    delete files[filename]    return ret  }   async load () {    if (files[this.#file]) {      return this.#loaded()    }     try {      files[this.#file] = await fs.readFile(this.#file, 'utf8')    } catch (er) {      if (er && this.#backupFile) {        this.#file = this.#backupFile        this.#backupFile = null        return this.load()      }      throw er    }     return this.#loaded()  }   async #loaded () {    const mod = new Module(this.#file, module)    mod.loaded = true    mod.filename = this.#file    mod.id = this.#file    mod.paths = Module._nodeModulePaths(dirname(this.#file))     this.#ctx.prompt = this.#makePrompt()    this.#ctx.__filename = this.#file    this.#ctx.__dirname = dirname(this.#file)    this.#ctx.__basename = basename(this.#file)    this.#ctx.module = mod    this.#ctx.require = (p) => mod.require(p)    this.#ctx.require.resolve = (p) => Module._resolveFilename(p, mod)    this.#ctx.exports = mod.exports     const body = `(function(${Object.keys(this.#ctx).join(', ')}) { ${files[this.#file]}\n })`    runInThisContext(body, this.#file).apply(this.#ctx, Object.values(this.#ctx))    this.#ctx.res = mod.exports     return this.#walk()  }   #makePrompt () {    return (...args) => {      let p, d, t      for (let i = 0; i < args.length; i++) {        const a = args[i]        if (typeof a === 'string') {          if (p) {            d = a          } else {            p = a          }        } else if (typeof a === 'function') {          t = a        } else if (a && typeof a === 'object') {          p = a.prompt || p          d = a.default || d          t = a.transform || t        }      }      try {        return `${this.#unique}-${this.#prompts.length}`      } finally {        this.#prompts.push([p, d, t])      }    }  }   async #walk (o = this.#ctx.res) {    const keys = Object.keys(o)     const len = keys.length    let i = 0     while (i < len) {      const k = keys[i]      const v = o[k]      i++       if (v && typeof v === 'object') {        o[k] = await this.#walk(v)        continue      }       if (v && typeof v === 'string' && v.startsWith(this.#unique)) {        const n = +v.slice(this.#unique.length + 1)         // default to the key        // default to the ctx value, if there is one        const [prompt = k, def = this.#ctx[k], tx] = this.#prompts[n]         try {          o[k] = await this.#prompt(prompt, def, tx)        } catch (er) {          if (er.notValid) {            // eslint-disable-next-line no-console            console.log(er.message)            i--          } else {            throw er          }        }        continue      }       if (typeof v === 'function') {        // XXX: remove v.length check to remove cb from functions        // would be a breaking change for `npm init`        // XXX: if cb is no longer an argument then this.#ctx should        // be passed in to allow arrow fns to be used and still access ctx        const fn = v.length ? promisify(v) : v        o[k] = await fn.call(this.#ctx)        // back up so that we process this one again.        // this is because it might return a prompt() call in the cb.        i--        continue      }    }     return o  }   async #prompt (prompt, def, tx) {    const res = await read({ prompt: prompt + ':', default: def }).then((r) => tx ? tx(r) : r)    // XXX: remove this to require throwing an error instead of    // returning it. would be a breaking change for `npm init`    if (res instanceof Error && res.notValid) {      throw res    }    return res  }} module.exports = PromZard.promzardmodule.exports.fromBuffer = PromZard.fromBuffermodule.exports.PromZard = PromZard