stream icon indicating copy to clipboard operation
stream copied to clipboard

Feature Request: Given a multi-entry-point configuration, output multiple chunks with known names as generated by Rollup

Open reubenrybnik opened this issue 3 years ago • 4 comments

  • @rollup/stream Version: 2.0.0

Feature Use Case

I'm using Rollup to bundle node dependencies specifically (not an entire app), and to do that efficiently it is useful to define multiple named entry points in a single config. With this approach, dependencies of dependencies which are sometimes shared between dependencies and thus can benefit from having multiple entry points that are evaluated together are also efficiently output as separate chunks as necessary via code splitting. The current examples, however, don't appear to support this output well since with them it seems like I can only have one file output and I need to know what it's called. To be able to do this efficiently, I believe that I need to have a mode for this plugin in which it wraps the files in Vinyl streams itself to provide multiple file outputs and preserve the names of those files.

The reason I'm not using gulp-rollup for this is that it seems to imply that I'd need to load every file that Rollup might need in gulp.src, which seems inefficient, particularly because I'd ideally also want to avoid pulling in dev dependencies. I could maybe avoid providing any source files at all using this option, but that seems to be discouraged 🤣

The rollup.config.js that I'm currently using for this is below:

var replace = require('@rollup/plugin-replace');
var nodeResolve = require('@rollup/plugin-node-resolve');
var commonjs = require('@rollup/plugin-commonjs');
var babel = require('@rollup/plugin-babel');

var config = {
    // the entry points for each item to bundle
    input: {
        '@date-io/moment': './node_modules/@date-io/moment/build/index.js'
        '@material-ui/core': './node_modules/@material-ui/core/esm/index.js',
        '@material-ui/lab/Autocomplete': './node_modules/@material-ui/lab/esm/Autocomplete/index.js',
        '@material-ui/pickers': './node_modules/@material-ui/pickers/index.js',
        'react': './node_modules/react/index.js',
        'react/jsx-runtime': './node_modules/react/jsx-runtime.js',
        'react/jsx-dev-runtime': './node_modules/react/jsx-dev-runtime.js',
        'react-dom': './node_modules/react-dom/index.js',
        'react-infinite-scroll-hook': './node_modules/react-infinite-scroll-hook/dist/index.js',
        'react-merge-refs': './node_modules/react-merge-refs/dist/index.js'
        'redux': './node_modules/redux/es/redux.js',
        'react-redux': './node_modules/react-redux/es/index.js',
        'redux-thunk': './node_modules/redux-thunk/es/index.js',
    },

    plugins: [
        // replace node environment variables with appropriate values
        replace({
            preventAssignment: true,
            values: {
                'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
            }
        }),

        // resolve named dependencies using node resolution rules
        nodeResolve.nodeResolve({
            browser: true
        }),

        // convert CommonJS modules to ES6 so Rollup will understand them
        commonjs(),

        // use babel to read and transpile modules, which may contain stuff like JSX
        babel.babel({
            babelHelpers: 'bundled',
            extensions: [
                '.js',
                '.jsx'
            ]
        })
    ],

    output: {
        // prefix dependency chunk bundles with __chunk_ to prevent conflicts
        chunkFileNames: '__chunk_[name]/js/index.js',

        // output all entry point bundles as AMD modules to js/index.js under the module's path
        entryFileNames: '[name]/js/index.js',

        // output modules in AMD format - our codebase is old and we'd like to eventually move
        // away from this but we can't yet
        format: 'amd',

        // use ES interop
        interop: 'auto'
    }
};

// reference entry point dependencies by name rather than relative path
config.external = Object.keys(config.input);

module.exports = config;

Feature Proposal

It would be nice if this plugin offered an option to provide multiple chunks from Rollup as separate outputs in a format that's easy to consume since it seems like such output isn't available in the current version. I'm interested in potentially contributing a change that accomplishes this goal back to the main repository if this is a feature that maintainers are interested in adding or possibly documentation that shows how to use this plugin to accomplish such a goal if this feature already exists and I missed it.

I've put together a proposed implementation at https://github.com/rollup/stream/compare/master...reubenrybnik:output-vinyl-stream that is comprised of an optional flag which when specified causes this plugin's output to be changed to a Vinyl object stream to which the separate named files provided by Rollup are written. I'm currently using this implementation in a private commercial repository. This works for my use case but may not be the best implementation of this feature from maintainers' perspective, and I'm happy to discuss potential alternatives in that case.

If this approach is accepted, I believe my current proposed implementation of adding an option to switch to this output mode and making the Vinyl dependencies optional would make this a non-breaking change. However, I'm interested in knowing maintainers' views on whether it might be better to move away from the current pattern of using vinyl-source-stream to convert the output to a single Vinyl item. In particular, I'm not sure whether there are any use cases in which this output is not immediately converted to Vinyl or whether maintainers are interested in supporting such use cases. I'm also interested in views on whether it would be better to switch to this mode using an option as in my current implementation or whether it might be better to provide a separate function property off of the main export instead (i.e. something like webpackStream.vinyl(config) instead of webpackStream(config, { outputVinyl: true })). I'd also welcome any other feedback you'd like to provide on this approach.

Thanks in advance for your consideration of this request!

reubenrybnik avatar Apr 24 '21 19:04 reubenrybnik