richardtallent / vite-plugin-singlefile

Vite plugin for inlining JavaScript and CSS resources
MIT License
808 stars 53 forks source link

Feature request: Inline CSS & SVG files, but defer/re-order JS files #89

Closed CedarMist closed 7 months ago

CedarMist commented 7 months ago

Hi

I've been trying to get my ideal bundling setup so the whole app loads with as few connections as possible, ideally we have index.html which has the CSS and SVG resources inlined in <head>, then the static HTML which shows the outline of the app with a nice loading message (it's a few hundred kb if you're on a slow connection just wait), and then at the end of <body> the <script> tag includes all inlined JS.

The problem I have right now is that the <script> tag is in <head> before the <style>, so rather than having a smooth loading experience on a slow connection it shows a blank white page while the browser reads the response, then it loads the CSS & HTML placeholder.

With higher latency and/or low bandwidth connections (4G, some hotel wifis, roaming, on-plane wifi etc.) I've calculated that the ordering of <style> then <body>..placeholder then <script> is absolutely optimal - browser opens connection & data gets downloaded as quick as possible in the order it's needed.

However, I can't figure out a way of achieving this without writing a script to manually re-process the .html file after it's been built.

richardtallent commented 7 months ago

Hi,

This plugin isn't really intended to do anything like that. You might be able to use the code for this plugin as a starting point for manipulating the output of rollup, but it's not a use case that will be built into this plugin.

CedarMist commented 7 months ago

I'll just use the hacky script as a workaround in the meantime then

import { promises as fs } from 'fs';

async function main() {
    let scripts = [];
    let html = (await fs.readFile('dist/index.html')).toString('utf-8');
    const rx = /(<script[^>]*>.*?<\/script>)/gms;
    const blah = html.matchAll(rx);
    for( const m of blah ) {
        scripts.push(m[0]);
        html = html.replace(m[0], '');
    }
    html = html.replace('</body>', scripts.join('') + '</body>');
    console.log(html);
}

Promise.all([main()]);