UNPKG

rasengan

Version:

The modern React Framework

266 lines (263 loc) 9.95 kB
import path, { resolve } from 'path'; import fs from 'fs'; import { loadModuleSSR } from '../config/utils/load-modules.js'; import { detectRuntime, resolveBuildOptions } from '../../server.js'; import { renderIndexHTML } from '../../server/build/rendering.js'; import { createVirtualModule } from '../../server/virtual/index.js'; import { pathToFileURL } from 'url'; import { preRenderApp } from '../../server/node/index.js'; function loadRasenganGlobal() { return { name: 'vite-plugin-rasengan-config', async config() { let version = ''; try { const rasenganPkgPath = resolve(process.cwd(), 'node_modules/rasengan/package.json'); if (fs.existsSync(rasenganPkgPath)) { const raw = fs.readFileSync(rasenganPkgPath, { encoding: 'utf-8' }); version = JSON.parse(raw).version || ''; } } catch { } const rasenganConfig = { version, ssr: true, }; return { define: { ['Rasengan']: JSON.stringify(rasenganConfig), }, }; }, }; } function rasenganConfigPlugin() { const virtualModuleId = 'virtual:rasengan-config'; const resolvedVirtualModuleId = '\0' + virtualModuleId; return { name: 'vite-plugin-rasengan-config', resolveId(id) { if (id === virtualModuleId) { return resolvedVirtualModuleId; } }, async load(id) { if (id === resolvedVirtualModuleId) { // if (command !== "build") return; const configPath = resolve(process.cwd(), 'rasengan.config.js'); if (!fs.existsSync(configPath)) { throw new Error(`Configuration file not found at: ${configPath}`); } const rasenganConfig = await (await loadModuleSSR(configPath)).default; const partialConfig = { server: rasenganConfig.server ?? {}, redirects: rasenganConfig.redirects ? await rasenganConfig.redirects() : [], }; return ` export const __RASENGAN_CONFIG__ = ${JSON.stringify(partialConfig)}; `; } }, }; } function flatRoutesPlugin() { const { id: virtualModuleId, resolvedId } = createVirtualModule('router'); return { name: 'vite-plugin-rasengan-router', resolveId(id) { if (id === virtualModuleId) { return resolvedId; } }, async load(id) { if (id === resolvedId) { return ` import { flatRoutes } from 'rasengan'; const Router = flatRoutes(() => { return import.meta.glob( [ '/src/app/_routes/**/layout.{js,ts,jsx,tsx}', '/src/app/_routes/**/*.page.{md,mdx,js,ts,jsx,tsx}', ], ); }); export default Router; `; } }, }; } function buildOutputInformation() { const { id: virtualModuleId, resolvedId } = createVirtualModule('build-info'); return { name: 'vite-plugin-rasengan-build-info', resolveId(id) { if (id === virtualModuleId) { return resolvedId; } }, async load(id) { if (id === resolvedId) { return ` export const resolveBuildOptions = (buildPath) => { return { buildDirectory: buildPath, manifestPathDirectory: 'client/.vite', assetPathDirectory: 'client/assets', entryServerPath: 'server/entry.server.js', }; }; `; } }, }; } /** * This plugin is responsible for fixing the path of the C drive on Windows. */ const fixCPathPlugin = () => { return { name: 'vite-plugin-rasengan-fix-c-path', resolveId(source) { if (/^c:[\\/]/i.test(source)) { const fullPath = path.resolve(source.replace(/^c:/i, 'C:')); return pathToFileURL(fullPath).href; } return null; }, enforce: 'pre', }; }; export const Adapters = { VERCEL: 'vercel', DEFAULT: '', }; /** * This plugin is responsible for building the app. * @param param0 * @returns */ export function rasengan({ adapter = { name: Adapters.DEFAULT, prepare: async () => { } }, } = {}) { let config; let viteConfig; const buildOptions = resolveBuildOptions({}); return { name: 'vite-plugin-rasengan', async config() { // load rasengan.config.js const configPath = resolve(process.cwd(), 'rasengan.config.js'); if (!fs.existsSync(configPath)) { throw new Error(`Configuration file not found at: ${configPath}`); } const rasenganConfigHandler = await (await loadModuleSSR(configPath)).default; config = await rasenganConfigHandler(); }, async load(id) { if (id === 'virtual:rasengan-config') { return ` export const __RASENGAN_CONFIG__ = ${JSON.stringify(config)}; `; } }, configResolved(resolvedConfig) { viteConfig = resolvedConfig; }, async writeBundle(_) { const modulePaths = ['template.jsx', 'template.tsx'].map((file) => { return path.posix.join(process.cwd(), 'src', file); }); const modulePath = modulePaths.find((modulePath) => { return fs.existsSync(modulePath); }); const module = await this.load({ id: modulePath }); // SPA mode only if (!config.ssr) { if (!config.prerender) { // Generate the template.js file into the dist/assets fs.writeFileSync(path.posix.join(process.cwd(), buildOptions.buildDirectory, buildOptions.assetPathDirectory, 'template.js'), module.code, 'utf-8'); } } }, async closeBundle() { // We check here if the environment is client has been built because it's the // last environment to be built in the Vite build process if (this.environment.name === 'client') { // Generate a config.json file into the dist/client/assets or dist/assets const minimizedConfig = { buildOptions, ssr: config.ssr, prerender: !!config.prerender, redirects: await config.redirects(), }; fs.writeFileSync(path.posix.join(process.cwd(), buildOptions.buildDirectory, config.ssr || config.prerender ? buildOptions.clientPathDirectory : '', buildOptions.assetPathDirectory, 'config.json'), JSON.stringify(minimizedConfig), 'utf-8'); // Enable the generation of spa-fallback.html during pre-rendering // Only if every pages are not generated // @default to false - we assume that all pages are not generated let enableIndexFallback = false; // Handling the prerendering if (config.prerender) { let routes = []; const buildOptions = resolveBuildOptions({ serverPathDirectory: 'prerender', }); if (typeof config.prerender === 'object') { routes = config.prerender.routes || []; } const outDir = `${process.cwd()}/static`; const { isIndexPrerendered } = await preRenderApp({ build: buildOptions, outDir, routes, }); enableIndexFallback = isIndexPrerendered; } // Check if SPA or SSG mode is enabled if (!config.ssr || config.prerender) { // Load the template.js file let templatePath = ''; if (config.prerender) { templatePath = path.posix.join(process.cwd(), buildOptions.buildDirectory, 'prerender', 'template.js'); } else { templatePath = path.posix.join(process.cwd(), buildOptions.buildDirectory, buildOptions.assetPathDirectory, 'template.js'); } const Template = (await import(templatePath)).default; // Render the index.html file await renderIndexHTML(Template, { rootPath: process.cwd(), config, enableIndexFallback, }); } // Detect runtime environment const runtime = detectRuntime(); console.log(`Detected runtime: ${runtime}`); if (runtime !== 'local' && runtime !== 'unknown') { // Prepare the app for deployment await prepareToDeploy(adapter); } } }, apply: 'build', }; } const prepareToDeploy = async (adapter) => { // Preparing app for deployment switch (adapter.name) { case Adapters.VERCEL: { await adapter.prepare(); break; } default: break; } }; export const plugins = [ // fixCPathPlugin(), loadRasenganGlobal(), flatRoutesPlugin(), ];