UNPKG

vite-plugin-ruby

Version:

Convention over configuration for using Vite in Ruby apps

281 lines (274 loc) 10.4 kB
// ../node_modules/.pnpm/tsup@7.2.0_typescript@4.9.5/node_modules/tsup/assets/esm_shims.js import { fileURLToPath } from "url"; import path from "path"; var getFilename = () => fileURLToPath(import.meta.url); var getDirname = () => path.dirname(getFilename()); var __dirname = /* @__PURE__ */ getDirname(); // src/index.ts import { basename, posix, resolve as resolve2 } from "path"; import { existsSync, readFileSync as readFileSync2 } from "fs"; import createDebugger2 from "debug"; // src/utils.ts import { readFileSync } from "fs"; // src/constants.ts var APP_ENV = process.env.RAILS_ENV || process.env.RACK_ENV || process.env.APP_ENV; var ENV_PREFIX = "VITE_RUBY"; var ALL_ENVS_KEY = "all"; var KNOWN_CSS_EXTENSIONS = [ "css", "less", "sass", "scss", "styl", "stylus", "pcss", "postcss" ]; var KNOWN_ENTRYPOINT_TYPES = [ "html", "jsx?", "tsx?", ...KNOWN_CSS_EXTENSIONS ]; var ENTRYPOINT_TYPES_REGEX = new RegExp( `\\.(${KNOWN_ENTRYPOINT_TYPES.join("|")})(\\?.*)?$` ); // src/utils.ts function slash(path3) { return path3.replace(/\\/g, "/"); } function isObject(value) { return Object.prototype.toString.call(value) === "[object Object]"; } function screamCase(key) { return key.replace(/([a-z])([A-Z])/g, "$1_$2").toUpperCase(); } function configOptionFromEnv(optionName) { return process.env[`${ENV_PREFIX}_${screamCase(optionName)}`]; } function booleanOption(value) { if (value === "true") return true; if (value === "false") return false; return value; } function loadJsonConfig(filepath) { return JSON.parse(readFileSync(filepath, { encoding: "utf8", flag: "r" })); } function cleanConfig(object) { Object.keys(object).forEach((key) => { const value = object[key]; if (value === void 0 || value === null) delete object[key]; else if (isObject(value)) cleanConfig(value); }); return object; } // src/config.ts import { join, relative, resolve } from "path"; import glob from "fast-glob"; var defaultConfig = loadJsonConfig(resolve(__dirname, "../default.vite.json")); function filterEntrypointsForRollup(entrypoints) { return entrypoints.filter(([_name, filename]) => ENTRYPOINT_TYPES_REGEX.test(filename)); } function filterEntrypointAssets(entrypoints) { return entrypoints.filter(([_name, filename]) => !ENTRYPOINT_TYPES_REGEX.test(filename)); } function resolveEntrypointFiles(projectRoot2, sourceCodeDir, config2) { const inputGlobs = config2.ssrBuild ? [config2.ssrEntrypoint] : [`~/${config2.entrypointsDir}/**/*`, ...config2.additionalEntrypoints]; const entrypointFiles = glob.sync(resolveGlobs(projectRoot2, sourceCodeDir, inputGlobs)); if (config2.ssrBuild) { if (entrypointFiles.length === 0) throw new Error(`No SSR entrypoint available, please create \`${config2.ssrEntrypoint}\` to do an SSR build.`); else if (entrypointFiles.length > 1) throw new Error(`Expected a single SSR entrypoint, found: ${entrypointFiles}`); return entrypointFiles.map((file) => ["ssr", file]); } return entrypointFiles.map((filename) => { let name = relative(sourceCodeDir, filename); if (name.startsWith("..")) name = relative(projectRoot2, filename); return [name, filename]; }); } function resolveGlobs(projectRoot2, sourceCodeDir, patterns) { return patterns.map( (pattern) => slash(resolve(projectRoot2, pattern.replace(/^~\//, `${sourceCodeDir}/`))) ); } function configFromEnv() { const envConfig = {}; Object.keys(defaultConfig).forEach((optionName) => { const envValue = configOptionFromEnv(optionName); if (envValue !== void 0) envConfig[optionName] = envValue; }); return envConfig; } function loadConfiguration(viteMode, projectRoot2, userConfig) { const envConfig = configFromEnv(); const mode = envConfig.mode || APP_ENV || viteMode; const filePath = join(projectRoot2, envConfig.configPath || defaultConfig.configPath); const multiEnvConfig = loadJsonConfig(filePath); const fileConfig = { ...multiEnvConfig[ALL_ENVS_KEY], ...multiEnvConfig[mode] }; return coerceConfigurationValues({ ...defaultConfig, ...fileConfig, ...envConfig, mode }, projectRoot2, userConfig); } function coerceConfigurationValues(config2, projectRoot2, userConfig) { var _a, _b, _c, _d, _e, _f, _g, _h; const port = config2.port = parseInt(config2.port); const https = config2.https = ((_a = userConfig.server) == null ? void 0 : _a.https) || booleanOption(config2.https); const fs = { allow: [projectRoot2], strict: (_d = (_c = (_b = userConfig.server) == null ? void 0 : _b.fs) == null ? void 0 : _c.strict) != null ? _d : true }; const server = { fs, host: config2.host, https, port, strictPort: true }; if (booleanOption(config2.skipProxy)) server.origin = ((_e = userConfig.server) == null ? void 0 : _e.origin) || `${https ? "https" : "http"}://${config2.host}:${config2.port}`; const hmr = (_g = (_f = userConfig.server) == null ? void 0 : _f.hmr) != null ? _g : {}; if (typeof hmr === "object" && !hmr.hasOwnProperty("clientPort")) { hmr.clientPort || (hmr.clientPort = port); server.hmr = hmr; } const root = join(projectRoot2, config2.sourceCodeDir); const ssrEntrypoint = (_h = userConfig.build) == null ? void 0 : _h.ssr; config2.ssrBuild = Boolean(ssrEntrypoint); if (typeof ssrEntrypoint === "string") config2.ssrEntrypoint = ssrEntrypoint; const outDir = relative(root, config2.ssrBuild ? config2.ssrOutputDir : join(config2.publicDir, config2.publicOutputDir)); const base = resolveViteBase(config2); const entrypoints = resolveEntrypointFiles(projectRoot2, root, config2); return { ...config2, server, root, outDir, base, entrypoints }; } function resolveViteBase({ assetHost, base, publicOutputDir }) { if (assetHost && !assetHost.startsWith("http")) assetHost = `//${assetHost}`; return [ ensureTrailingSlash(assetHost || base || "/"), publicOutputDir ? ensureTrailingSlash(slash(publicOutputDir)) : "" ].join(""); } function ensureTrailingSlash(path3) { return path3.endsWith("/") ? path3 : `${path3}/`; } // src/manifest.ts import path2 from "path"; import { promises as fsp } from "fs"; import createDebugger from "debug"; var debug = createDebugger("vite-plugin-ruby:assets-manifest"); function assetsManifestPlugin() { let config2; let viteRubyConfig; async function fingerprintRemainingAssets(ctx, bundle, manifest) { const remainingAssets = filterEntrypointAssets(viteRubyConfig.entrypoints); for (const [filename, absoluteFilename] of remainingAssets) { const content = await fsp.readFile(absoluteFilename); const ref = ctx.emitFile({ name: path2.basename(filename), type: "asset", source: content }); const hashedFilename = ctx.getFileName(ref); manifest.set(path2.relative(config2.root, absoluteFilename), { file: hashedFilename, src: filename }); } } return { name: "vite-plugin-ruby:assets-manifest", apply: "build", enforce: "post", configResolved(resolvedConfig) { config2 = resolvedConfig; viteRubyConfig = config2.viteRuby; }, async generateBundle(_options, bundle) { if (!config2.build.manifest) return; const manifestDir = typeof config2.build.manifest === "string" ? path2.dirname(config2.build.manifest) : ".vite"; const fileName = `${manifestDir}/manifest-assets.json`; const manifest = /* @__PURE__ */ new Map(); await fingerprintRemainingAssets(this, bundle, manifest); debug({ manifest, fileName }); this.emitFile({ fileName, type: "asset", source: JSON.stringify(Object.fromEntries(manifest), null, 2) }); } }; } // src/index.ts var projectRoot = configOptionFromEnv("root") || process.cwd(); var watchAdditionalPaths = []; function ViteRubyPlugin() { return [ { name: "vite-plugin-ruby", config, configureServer }, assetsManifestPlugin() ]; } var debug2 = createDebugger2("vite-plugin-ruby:config"); function config(userConfig, env) { var _a, _b, _c; const config2 = loadConfiguration(env.mode, projectRoot, userConfig); const { assetsDir, base, outDir, server, root, entrypoints, ssrBuild } = config2; const isLocal = config2.mode === "development" || config2.mode === "test"; const rollupOptions = (_a = userConfig.build) == null ? void 0 : _a.rollupOptions; let rollupInput = rollupOptions == null ? void 0 : rollupOptions.input; if (typeof rollupInput === "string") rollupInput = { [rollupInput]: rollupInput }; const build = { emptyOutDir: (_c = (_b = userConfig.build) == null ? void 0 : _b.emptyOutDir) != null ? _c : ssrBuild || isLocal, sourcemap: !isLocal, ...userConfig.build, assetsDir, manifest: !ssrBuild, outDir, rollupOptions: { ...rollupOptions, input: { ...rollupInput, ...Object.fromEntries(filterEntrypointsForRollup(entrypoints)) }, output: { ...outputOptions(assetsDir, ssrBuild), ...rollupOptions == null ? void 0 : rollupOptions.output } } }; const envDir = userConfig.envDir || projectRoot; debug2({ base, build, envDir, root, server, entrypoints: Object.fromEntries(entrypoints) }); watchAdditionalPaths = resolveGlobs(projectRoot, root, config2.watchAdditionalPaths || []); const alias = { "~/": `${root}/`, "@/": `${root}/` }; return cleanConfig({ resolve: { alias }, base, envDir, root, server, build, viteRuby: config2 }); } function configureServer(server) { server.watcher.add(watchAdditionalPaths); return () => server.middlewares.use((req, res, next) => { if (req.url === "/index.html" && !existsSync(resolve2(server.config.root, "index.html"))) { res.statusCode = 404; const file = readFileSync2(resolve2(__dirname, "dev-server-index.html"), "utf-8"); res.end(file); } next(); }); } function outputOptions(assetsDir, ssrBuild) { const outputFileName = (ext) => ({ name }) => { const shortName = basename(name).split(".")[0]; return posix.join(assetsDir, `${shortName}-[hash].${ext}`); }; return { assetFileNames: ssrBuild ? void 0 : outputFileName("[ext]"), entryFileNames: ssrBuild ? void 0 : outputFileName("js") }; } export { ViteRubyPlugin as default, projectRoot }; //# sourceMappingURL=index.js.map