sveltejs / sapper

The next small thing in web development, powered by Svelte
https://sapper.svelte.dev
MIT License
7.01k stars 435 forks source link

Sapper uses a generateBundle hook after custom plugins #1765

Open siebeneicher opened 3 years ago

siebeneicher commented 3 years ago

This issue appears after upgrading to 0.29.x. I used the gzipPlugin() in my sapper projects rollup plugin configuration for my client like this:

export default {
    client: {
        input: config.client.input(),
        output: config.client.output(),
        plugins: [
            aliases,
            replaces,
            replacesRe,
            ...
            gzipPlugin()
        ]
    ]
}

Its supposed to compress my output files and generate the client.12345.js.gz file alongside the generate source file client.12345.js. The plugin still generates the compressed file, but the content is not the same as the source, after the whole rollup bundling is finished. It seems, that in sapper/dist/create_manifest_data.js a plugin named 'sapper-css-injection' is using the generateBundle rollup hook to overwrite some placeholders, in order to inject CSS properly. The generateBundle hook seems to be pushed after the gzipPlugin and its own generateBundle hook, therefore the source is overwritten and saved, after the gzipPlugin has compressed it, leading to different content in both files.

In best case sapper would not use the generateBundle hook.

Workaround: I have implemented a custom compression plugin, which uses the rollup writeBundle hook, being executed after all generateBundle hooks have been finished:

const compressPlugin = {
    name: 'my-compress-plugin',
    // writeBundle is used after generateBundle (sapper calls it, and somehow its not ordered properly)
    writeBundle: (opts, bundle) => {
        const src = config.client.output().dir+'/'

        const brotliSettings = {
            extension: 'br',
            skipLarger: true,
            mode: 1, // 0 = generic, 1 = text, 2 = font (WOFF2)
            quality: 10, // 0 - 11,
            lgwin: 12 // default
        };

        const gzipSettings = {
            level: 9,       // BEST = 9
        }

        fs.readdirSync(src).forEach(async (file) => {
            if (file.endsWith('.js') || file.endsWith('.css') || file.endsWith('.html')) {
                const content = fs.readFileSync(src + file)

                // brotli
                fs.writeFileSync(src + file + '.br', compress(content, brotliSettings));

                // gzip
                fs.writeFileSync(src + file + '.gz', await gzip(content, gzipSettings))
            }
        });

        return
    }
}