Skip to content

Instantly share code, notes, and snippets.

@rxnlabs
Last active May 29, 2024 17:28
Show Gist options
  • Save rxnlabs/3386bcce53c77064c64411e55d02e46f to your computer and use it in GitHub Desktop.
Save rxnlabs/3386bcce53c77064c64411e55d02e46f to your computer and use it in GitHub Desktop.
Custom RollupJS configuration file to support multiple entry points using a Glob import and also excluding entries using a Glob import. Outputs all of the entries to a custom directory and supports exporting as an IIFE (unlike https://github.com/alfredosalzillo/rollup-plugin-multi-input and https://www.npmjs.com/package/@rollup/plugin-multi-entry)…
{
"name": "custom-rollup-js-config-multi-entry-glob",
"version": "0.1.0",
"description": "package.json for custom RollupJS configuration that supports glob'ing entries from a directory or multiple directories",
"author": "rxnlabs",
"license": "GPL-2.0-or-later",
"scripts": {
"dev": "npm run scripts -- --watch",
"build": "npm run scripts",
"scripts": "rollup --config"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-swc": "^0.3.0",
"@rollup/plugin-terser": "^0.4.4",
"fast-glob": "^3.3.2",
"rollup": "^4.17.2",
"rollup-plugin-wp-resolve": "^1.0.9",
},
"dependencies": {}
}
// rollup.config.mjs (rollup.config.js file writtin in ES6 using the .mjs file extension so it's treated like a ES6 module)
/**
* @file
* This script is a custom configuration file for RollupJS. This config's main feature is the ability
* to import multiple entries by leveraging Glob patterns and regex file matching, essentially
* enlarging the base functionality of RollupJS. Also, it supports
* excluding certain entries from being bundled using similar Glob patterns.
*
* Furthermore, this configuration is designed in a way to output
* multiple bundled files into a specific directory. Each output file's
* name is the same name as it's input ('entry') file, hence maintaining
* a direct correlation between source and bundled files. It also generates a minified version
* of the entry as a separate file with the .min.js as the file extension.
*
* RollupJS doesn't natively provide this capability but this should work with almost any current rollupjs configuration
* while also being able to output code in iife format, so it can execute in browsers.
* - RollupJS plugin https://www.npmjs.com/package/@rollup/plugin-multi-entry does not do this since it bundles all entries into a single file instead of each entry also outputting into a distinct file
* - RollupJS plugin https://github.com/alfredosalzillo/rollup-plugin-multi-input does this but only for esm moduels and umd modules. Iife is not supported.
* - Preservermodules https://rollupjs.org/configuration-options/#output-preservemodules does not do this
* - Preservermodulesroot https://rollupjs.org/configuration-options/#output-preservemodulesroot does not do this
*/
// rollup config.js
import {rollup} from "rollup";
// when importing node_modules package into custom code created by us, RollupJS will look for that import in the node_modules folder if a full file path is not given when the module is imported
import resolve from '@rollup/plugin-node-resolve';
// convert CommonJS modules to es6, so they can be added to the bundle
import commonjs from '@rollup/plugin-commonjs';
// used to convert .jsx files and code to js and replaces the use of babel to transpile es6 code to older browsers (used when building custom blocks or extending the block editor using React components)
import swc from '@rollup/plugin-swc';
// used to for file minification
import terser from '@rollup/plugin-terser';
// resolve wordpress external dependencies, similar to the dependy-extraction-webpack-plugin https://github.com/WordPress/gutenberg/tree/trunk/packages/dependency-extraction-webpack-plugin
import wpResolve from 'rollup-plugin-wp-resolve';
// used to import multiple entry files to this custom Rollup config using a glob pattern
import fg from 'fast-glob';
import path from 'node:path';
/**
* Transform the entry file so that the output file is named the same file name as the entry file but remove the original root folder from the final file output directory.
*/
const createEntryOutput = (entryFiles, removeRelativePath, outputDir, createMinificationFiles) => {
return entryFiles.map((relativeFilePath) => {
let outputFile = relativeFilePath;
// remove the relative file folder path from the beginning of the file in case we don't want the exact file structure
if (typeof removeRelativePath === 'string' && outputFile.startsWith(removeRelativePath)) {
outputFile = outputFile.split(removeRelativePath)[1];
}
// output the file into a new output directory
outputFile = ltrim(outputFile, '/');
if (typeof outputDir === 'string' && outputDir.length) {
outputFile = `${ltrim(outputDir, '/')}/${outputFile}`;
}
// create an entry output object with the name of the entry file appended with the text .min to specify that this is a minimized file
const fileExtension = path.extname(relativeFilePath);
const bundleFileWithoutExtension = outputFile.slice(0, outputFile.length - fileExtension.length);
// use the file output path as the name of the exported variable in the iife in case the output file exports a value
// see https://rollupjs.org/configuration-options/#output-name
const iifeVarName = bundleFileWithoutExtension.replace(/[\/\.]/g, '_').toUpperCase();
let minifiedOutputFile = '';
if (createMinificationFiles && bundleFileWithoutExtension.length) {
minifiedOutputFile = `${bundleFileWithoutExtension}.min${fileExtension}`;
}
return {
entry: relativeFilePath,
output: outputFile,
min_output: minifiedOutputFile,
name: iifeVarName
};
});
};
/**
* Custom, lightweight JS version of PHP's ltrim function
*/
const ltrim = (str, charToRemove) => {
while (str.startsWith(charToRemove)) {
str = str.slice(charToRemove.length);
}
return str;
}
// specify these imports/requires as global modules so RollupJS will not try to compile their code into the bundle.
// specify the name of the global variable that the import will be transformed into when the file is bundled, which will also refer to the name of the variable in the global scope
const globalsTransform = {
jquery: '$',
react: 'window.React',
'react-dom': 'window.ReactDOM',
wp: 'window.wp',
};
// increase the time in milliseconds for a build delay to give the filesystem a chance for the file content to be fully written to disk.
// Decreases chances RollupJS will output an empty file
// see https://rollupjs.org/configuration-options/#watch-builddelay
const buildDelay = 500;
// get the list of entry files using a glob and exclude files using a glob. Exclude files in the partials folder and in the vendor folder
const entryFiles = fg.sync(['src/js/**/*.js', '!src/js/**/edit.js', '!src/js/partials/**/*.js', '!src/js/vendor/**/*.js']);
// create minification version of the entries
let createMinificationFiles = false;
const entryOutputFiles = createEntryOutput(entryFiles, 'src/', 'build', createMinificationFiles);
// add multiple files to to the rollup config so multiple bundles are built as individual files
let rollupConfig = entryOutputFiles.map((entryOutputFile) => {
let outputConfig = [
{
file: entryOutputFile.output,
format: 'iife',
name: entryOutputFile.name,
globals: globalsTransform,
sourcemap: true,
}
];
if (entryOutputFile.min_output.length) {
// do a deep clone of the config object so we can add the terser() plugin to the minify config
let minifyConfig = structuredClone( outputConfig[0] );
minifyConfig.file = entryOutputFile.min_output;
minifyConfig.plugins = minifyConfig.plugins || [];
minifyConfig.plugins.push(terser());
outputConfig.push(minifyConfig);
}
return {
input: entryOutputFile.entry,
output: outputConfig,
external: [Object.keys(globalsTransform)],
plugins: [
wpResolve(),
resolve({ extensions: ['.js', '.ts', '.jsx'] }),
swc({ swc: {'jsc': { 'parser': {'syntax': 'ecmascript', 'jsx': true}}, 'minify': false }, exclude: ['node_modules/**'] }),
commonjs()
],
watch: {
clearScreen: false,
exclude: 'node_modules/**',
include: 'src/js/**/*.js',
buildDelay: buildDelay,
}
};
});
// add any additional configs to the rollup export
const additionalConfigs = [
/*{
input: '',
output: {},
plugins: [],
watch: {
clearScreen: false,
exclude: 'node_modules/**'
},
}*/
];
rollupConfig = rollupConfig.concat(additionalConfigs).flat();
export default rollupConfig;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment