There is a project SlickGrid that have multiple files that are all written as iife (it was originally built with jQuery namespace). Most files are optional and the user can choose which feature they are interested (ie slick.contextmenu.js
, slick.headermenu.js
, …) by loading whichever file(s) and every time they do it simply extends the Slick
object on the window object.
I’d like to keep the iife for the users who still want to use standalone <script>
loading but also want to provide ESM in a separate build folder. I think that I can achieve this with esbuild by writing an esbuild plugin with onResolve
. I manage to make it work but it’s not the most elegant, I’d like help to find a better solution
import {
Event as SlickEvents,
EventData as SlickEventData,
EditorLock as SlickEditorLock,
Utils as SlickUtils,
} from './slick.core.js';
import { Draggable as SlickDraggable, MouseWheel as SlickMouseWheel, Resizable as SlickResizable } from './slick.interactions.js';
// I would like to avoid having the write the following lines
// for iife, pull from window.Slick object but for ESM use SlickX
const SlickEvent = window.Slick ? Slick.Event : SlickEvents;
const EventData = window.Slick ? Slick.EventData : SlickEventData;
const EditorLock = window.Slick ? Slick.EditorLock : SlickEditorLock;
const Utils = window.Slick ? Slick.Utils : SlickUtils;
const Draggable = window.Slick ? Slick.Draggable : SlickDraggable;
const MouseWheel = window.Slick ? Slick.MouseWheel : SlickMouseWheel;
const Resizable = window.Slick ? Slick.Resizable : SlickResizable;
// ...
// use the code
const options = Utils.extend(true, {}, defaults, options);
So I wrote a custom plugin that will use either window.Slick
for iife when found OR will use the named import for ESM. Running a build for ESM is roughly the same but without using the plugin since we want to bundle everything into 1 file. However one thing to note is that for iife, we’ll still build every single into separate files (I still use bundle
for the import but they are removed by the plugin), but for ESM we bundle everything into a single file and let tree shaking do the rest for ESM users.
import { build } from 'esbuild';
const myPlugin = {
name: 'my-plugin',
setup(build) {
build.onResolve({ filter: /..(js|ts)$/ }, args => {
if (args.kind !== 'entry-point') {
return { path: args.path + '.js', namespace: 'import-ns' }
}
})
build.onLoad({ filter: /.*/, namespace: 'import-ns' }, (args) => {
return {
contents: `// do nothing`,
loader: 'js',
};
})
}
};
build({
entryPoints: ['slick.grid.js'],
color: true,
bundle: true,
minify: false,
target: 'es2015',
sourcemap: false,
logLevel: 'error',
format: 'iife',
// globalName: 'Slick',
outfile: 'dist/iife/slick.grid.js',
plugins: [myPlugin],
});
So this approach seems to work but is not very elegant, ideally it would be great if I could get the named imports and replace them directly in the code somehow and avoid having to write all these extra lines (ie: const SlickEvent = window.Slick ? Slick.Event : SlickEvents;
)