UNPKG

vite-plugin-erb

Version:

Use ERB files in Vite.js projects with a Ruby backend

80 lines (79 loc) 2.87 kB
// src/index.ts import { parse as parseQuery } from "querystring"; import { join, dirname, relative } from "path"; import { fileURLToPath } from "url"; import { accessSync } from "fs"; import execa from "execa"; import createDebugger from "debug"; var debug = createDebugger("vite-plugin-erb"); var _dirname = typeof __dirname === "undefined" ? dirname(fileURLToPath(import.meta.url)) : __dirname; var outputDelimiter = "__VITE_ERB_RESULT__"; var renderedOutputRegex = new RegExp(`${outputDelimiter}([\\s\\S]*?)${outputDelimiter}`, "m"); var rendererPath = join(_dirname, "../src/renderer.rb"); function detectRunner(root) { try { accessSync(join(root, "bin/rails")); return "ruby bin/rails runner"; } catch { return "ruby"; } } async function renderErbFile(cwd, filename, code, options) { const { engine = "", runner = "", env, ...execOptions } = options; const path = relative(cwd, filename); try { const [cmd, ...cmdArgs] = runner.split(" "); const args = [...cmdArgs, rendererPath, outputDelimiter, engine].filter((x) => x); debug(`rendering ${path}`); const { stdout } = await execa(cmd, args, { input: code, cwd, env: { DISABLE_SPRING: "1", ...env }, ...execOptions }); const matches = stdout.match(renderedOutputRegex); if (!matches) throw new Error(`No output when rendering ${filename}. Is the file valid?`); debug(`rendered ${path}`); return matches[1]; } catch (error) { debug(`failed to render ERB file ${path}`, error); throw error; } } function parseId(id) { const [filename, rawQuery] = id.split("?", 2); const query = parseQuery(rawQuery); if (query.erb !== void 0 || filename.endsWith(".erb")) query.erb = true; return { filename: filename.split(".erb")[0], query }; } function ErbPlugin(options = {}) { let root; let plugins; return { name: "erb-plugin", enforce: "pre", configResolved(config) { plugins = config.plugins.filter((p) => p.name !== "vite:import-analysis"); root = process.env.VITE_RUBY_ROOT || config.root; if (!options.runner) options.runner = detectRunner(root); debug(`running renderer with '${options.runner}'`); }, async transform(code, id, ssr) { const { filename, query } = parseId(id); if (!query.erb) return; code = await renderErbFile(root, `${filename}.erb`, code, options); for (const plugin of plugins) { const transform = plugin.transform; const transformFn = typeof transform === "object" ? transform.handler : transform; if (typeof transformFn === "function") { const result = await transformFn.call(this, code, filename, ssr); if (result) code = typeof result === "object" ? result.code || "" : result; } } return code; } }; } export { ErbPlugin as default };