'is it possible to run a rollup plugin without an input file (in a multi bundle instance)?

I have a situation where I am bundling multiple files via rollup cli an example of this is available in documentation.

I export an array of bundles like so:

export default [
    {
        input: 'packages/A/index.js',
        output: {
            name: '[name]Bundle',
            file: '[name].umd.js',
            format: 'umd'
        }
    },
    {
        input: 'packages/B/index.js',
        output: {
            name: '[name]Bundle',
            file: '[name].umd.js',
            format: 'umd'
        }
    }
];

And then I have a function that adds a common config to each bundle (think plugins) so something like:

import path from "path";

import alias from '@rollup/plugin-alias';
import resolve from '@rollup/plugin-node-resolve';


const augment = configs => {

    const generateAlias = (symbol, location) => {
        return {
            find: symbol,
            replacement: path.resolve(process.cwd(), location)
        };
    }

    const entries = [
        generateAlias("@", "src/"),
        generateAlias("~", "src/assets/scss/"),
        generateAlias("#", "src/assets/img/"),
        generateAlias("%", "config/"),
    ];

    const plugins = [
        alias({
            entries
        }),
        resolve({ browser: true }),
    ];

    return configs.map(entry => {
        return {
            ...entry,
            plugins
        }
    });
}

export {
    augment
}

And the I wrap the above exported array in augment like:

const bundles = [/* above example */];

export default augment(bundles);

Now this all works fine, but I have two plugins that I don't actually want to apply to each bundle I just want to run them once all the bundles have built, those two plugins are; rollup-plugin-serve and rollup-plugin-workbox now neither of these actually have anything to do with any of the files being bundled, and make no sense to instantiate more than once.

What I would like to do as part of the augment function to to append them to the end of the returned array something like:

const exportedArray = configs.map(/* function from above */);

exportedArray.push(...[
        {
                plugins: [
                        serve({
                                host,
                                port,
                                contentBase: 'dist/'
                        })
                ]
        }
]);

return exportedArray;

And this would not contain an input, I have tried the above and rollup complains, the alternative option would be to add these plugins to the final bundle on the array, I just prefer the idea of instantiating them without them being tied to a particular bundle.



Solution 1:[1]

Rollup needs an input file to work, but that doesn't mean you cannot simply throw away the result of the file. You can make an empty file empty.js (or maybe add some comment in it) and use that as the input. Just leave out the output properties so that the result is not written anywhere.

---EDIT---

a bit like this:

export default {
    input: './empty.js',
    plugins: [{
        name: 'test plugin',
        load() {
            console.log('test plugin')
        }
    }]
}

---EDIT---

Alternatively you can write to a dummy file and remove it again

const fs = require('fs')

export default {
    input: './empty.js',
    output: {
        file: './dummy',
    },
    plugins: [{
        name: 'test plugin',
        writeBundle(options) {
            fs.unlinkSync(options.file)
        }
    }]
}

Solution 2:[2]

I faced a similar problem in our own rollup plugin. We use rollup to generate many bundles from many inputs. However, since the build takes a long time, we determine which inputs need to be built and which is unchanged. So there may be a situation where the rollup runs without any input.

The only solution is to create an empty file, which will not be written to disk as a result.

Config has no any input:

const rollupOptions: rollup.RollupOptions = {
    output: {
        dir: 'some-dir',
    },
    plugins: [buildManager(entryFiles)],
}

Plugin buildManager creates inputs on buildStart hook:

export function buildManager(entryFiles: string[]): rollup.Plugin {
    const emptyRunId = 'no-input-for-this-run';

    return {
        name: 'build-manager',
        buildStart() {
            for (const entryFile of entryFiles) {
                // if some condition met, emit chunk (~ create an input)
            }

            // if the condition does not met even once, no chunk is created, but rollup needs at least one input
            this.emitFile({
                id: emptyRunId,
                fileName: emptyRunId,
                type: 'chunk',
            });
        },
        resolveId(source) {
            if (source === emptyRunId) {
                // The leading \0 instructs other plugins and rollup not to try to resolve, load or transform this module
                return `\0${emptyRunId}`;
            }
            return null;
        },
        load(id) {
            if (id.includes(emptyRunId)) {
                // some random code to avoid rollup empty chunk warning
                return 'export const empty = true;';
            }

            return null;
        },
        generateBundle(options, bundle) {
            // remove empty chunk from final bundle to not write that file
            if (bundle[emptyRunId]) {
                delete bundle[emptyRunId];
            }
        },
    };
}

So it could be easily used as standalone plugin to handle empty run.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Marek Dorda