UNPKG

houdini-adapter-static

Version:

The adapter for deploying your Houdini application as a single-page application without a server component

67 lines (66 loc) 2.97 kB
// @ts-nocheck import { fs } from 'houdini'; import path from 'node:path'; import React from 'react'; import ReactDOM from 'react-dom/server'; // in order to prepare the app as a single-page app, we have 2 create 2 additional files: // - an index.js that imports the application and calls React.render. This file needs to be built by vite so it's passed with the includePaths option for an adapter // - an index.html containing the static shell that wraps the application. const adapter = async ({ outDir, config: { runtimeDir } }) => { // the first thing we need to do is pull out the rendered html file into the root of the outDir await fs.copyFile(path.join(outDir, 'assets', runtimeDir, 'temp', 'spa-shell', 'index.html'), path.join(outDir, 'index.html')); try { await fs.rmdir(path.join(outDir, 'assets', runtimeDir)); } catch { } }; // make sure we include the app entry point in the bundle adapter.includePaths = ({ config: { runtimeDir } }) => ({ shell: path.join(runtimeDir, 'temp', 'spa-shell', 'index.html'), }); // we dont want any server artifacts to be generated adapter.disableServer = true; adapter.pre = async ({ config, outDir, conventions }) => { // before we do anything, we need to ensure the user isn't using a local API if (config.localSchema) { throw new Error("Houdini's SPA adapter cannot be used if your project relies on a local schema"); } process.env.HOUDINI_SECONDARY_BUILD = 'true'; const { build } = await import('vite'); const shellDir = conventions.temp_dir(config, 'spa-shell'); // before we can import and render the user's index file, we need to compile it with vite await build({ build: { emptyOutDir: false, ssr: true, rollupOptions: { output: { dir: shellDir, entryFileNames: '[name].js', }, }, lib: { entry: { // the shell could be defined as tsx or jsx so just strip the extension shell: conventions.router_index_path(config).replace('.jsx', ''), }, formats: ['es'], }, }, }); process.env.HOUDINI_SECONDARY_BUILD = 'false'; // now we can import the bundled shell const { default: App } = await import(path.join(shellDir, 'shell.js')); // render the index.jsx file to generate the static html that // we can use to wrap the ajvascript application let shellContents = ReactDOM.renderToStaticMarkup(React.createElement(App, { children: [ React.createElement('div', { id: 'app', }), ], })).replace('</head>', "<script type='module' src='virtual:houdini/static-entry'></script></head>"); // write the shell to the outDir await fs.writeFile(path.join(shellDir, 'index.html'), shellContents); }; export default adapter;