UNPKG

@metamodern/svelte-render

Version:

A friendly Jamstack-focused build tool for Svelte apps

151 lines (121 loc) 3.68 kB
import { resolve, dirname } from 'path'; import { existsSync } from 'fs'; import { mkdir, rm, readFile, writeFile, copyFile, } from 'fs/promises'; import { totalist } from 'totalist'; import { uid } from 'uid'; import { makeBundle } from './make-bundle.js'; import { renderHtml } from './render-html.js'; import { runParallel } from './run-parallel.js'; export const render = async (context, { src = 'src', assets = 'assets', dist = 'dist', entry = 'index.svelte', client = 'client.js', noCss = false, development = false, before, onRender, after, ...options } = {}) => { const args = { src, assets, dist, entry, client, development, noCss, ...options, }; const buildId = uid(6); const asyncTasks = []; // make sure `dist` exists await mkdir(resolve(context, dist), { recursive: true }); // run `before` hook if (before) { await before(context, args); } // push build `client` script if (client) { asyncTasks.push(['generateClientScript', async () => { const clientInput = resolve(context, src, client); const clientOutput = resolve(context, dist, `client-${buildId}.js`); const clientBundle = await makeBundle(clientInput, { development, ssr: false, cssOutput: noCss ? false : `style-${buildId}.css`, ...options, }); await clientBundle.write({ format: 'iife', file: clientOutput, }); await clientBundle.close(); }]); } // push build `entry` script (for pre-rendered HTML in production) if (!development) { asyncTasks.push(['generateEntryComponent', async () => { const entryInput = resolve(context, src, entry); const cache = resolve(context, `./.svelte-render/entry-${buildId}.js`); const entryBundle = await makeBundle(entryInput, { development: false, ssr: true, cssOutput: false, ...options, }); await entryBundle.write({ format: 'es', file: cache, }); const component = (await import(cache)).default; await Promise.all([ entryBundle.close(), rm(cache, { force: true }), ]); return component; }]); } // push check for custom HTML template asyncTasks.push(['readCustomTemplate', async () => { const customTemplate = resolve(context, src, 'template.html'); if (existsSync(customTemplate)) { return readFile(customTemplate, 'utf8'); } return false; }]); // push copy `assets` directory to `dist` const assetsDir = resolve(context, assets); if (existsSync(assetsDir)) { asyncTasks.push(['copyAssetsDir', async () => { const copyEach = async (filename, srcPath) => { const destPath = resolve(context, dist, filename); await mkdir(dirname(destPath), { recursive: true }); await copyFile(srcPath, destPath); }; await totalist(assetsDir, copyEach); }]); } // push `onRender` hook if (onRender) { asyncTasks.push(['onRender', async () => { await onRender(context, args); }]); } // run `asyncTasks` in parallel const taskResults = await runParallel(asyncTasks); const template = taskResults.get('readCustomTemplate'); const component = taskResults.get('generateEntryComponent'); // output index.html (with pre-rendered HTML in production) const htmlOutput = resolve(context, dist, 'index.html'); const htmlString = renderHtml({ buildId, template, component, noCss, noClient: !client, }); await writeFile(htmlOutput, htmlString); // run `after` hook if (after) { await after(context, args); } return 0; };