Open ggoodman opened 3 years ago
This sounds like a good idea to me. Thanks for the idea!
Just to give a concrete example on situations where this makes all difference, we use the jquery.tree plugin, and its source is a mess in the sense that some of the files would use require("jquery")
while others would assume jQuery is defined. To make things worse, it also bundles its own node_modules/jquery, which is used by esbuild
instead of the one available in the project's node_modules. That means I must replace both set of files. In one set I must replace require("jquery")
with require("${ require.resolve('jquery') }"
while for the other set I must prepend import jQuery from "${ jQueryFullPath }"
.
All of those files live under the same jqtree module path, but I can't prepend the jQuery import to all of them, that's why an exclude
/ignore
param would be very helpful.
I've created a generic plugin to handle what I get from webpack loaders such as imports-loader, exports-loader and more.
It was simpler, but I had to complicate it a little bit in order to support those exclusions directly in the plugin, while the implementation would get much simpler if esbuild would support exclusions itself:
const fs = require('fs');
let id = 0;
// plugin to allow general source modification operations such as prepend, append and replacements
// this basically takes care of the imports and exports loader from Webpack and more.
module.exports = options => options.map(([filter,
{ prepend, append, replacements, loader, excludes }]) => ({
name: `source-modifier-${ ++id }`,
setup(build) {
build.onLoad({ filter }, async args => {
let contents = await fs.promises.readFile(args.path, 'utf8');
let processed = excludes && processExcludes(contents, excludes, args.path);
contents = processed || processContents(contents, replacements, prepend, append);
const result = { contents };
if (loader) result.loader = loader;
return result;
});
}
}));
function processExcludes(contents, excludes, path) {
for (let [filter, { replacements, append, prepend }] of excludes) {
if (filter.test(path)) return processContents(contents, replacements, prepend, append);
}
}
function processContents(contents, replacements, prepend, append) {
if (replacements) for (const [a, b] of replacements)
contents = contents.replace(a, b);
if (prepend || append)
contents = [prepend || '', contents, append || ''].join("\n");
return contents;
}
// I use it like this:
const sourceModifierPlugin = require('./esbuild-plugins/source-modifier.js');
const jqueryFullPath = require.resolve('jquery');
const buildConfig = {
// ...
plugins: {
//...
...sourceModifierPlugin([
[ /jqtree.*\.js/, {
prepend: `import jQuery from "${ jqueryFullPath }"`,
excludes: [
[/(tree\.jquery|dragAndDropHandler)\.js/, { replacements: [
[ 'require("jquery")', `require("${ jqueryFullPath }");` ],
] } ]
],
} ],
]),
}
}
I'd rather keep the plugin simpler and provide a single exclude/ignore option that I'd forward to esbuild. Then I'd replace 'require("jquery")' for all sources under jqtree (it won't hurt for those files that assume jQuery is defined) and skip the jQuery import for those 2 files using var jQuery = require("jquery")
;
Having a exclude
filter in plugins would be really useful; I noticed a lot of our bundling time was spent inside our custom resolver, which we only use for third-party package resolution.
Excluding our own packages from this set would speed up the bundling significantly, but since Go doesn't support negative lookaheads the final regex to exclude a few paths becomes a monstrosity - and loses the speed benefits.
Description
I would like to propose the addition of an
exclude
orignore
option during plugin hook registration that would act as the inverse of the currentfilter
option. Currently, it is impossible to say something like 'excludenode_modules
' for this plugin. If I don't want a plugin hook to apply to a file, I must register the hook andreturn null
when I observe anargs.path
that would normally be excluded. For largenode_modules
graphs, this is every expensive and could be avoided.I think that this would be a very valuable addition considering the intended simplifications that the Go team made to their regular expression support. I suspect that with JavaScript's regular expression grammar, we could express a
node_modules
exclusion with something like a negative lookahead / lookbehind assertion but this would fail on the go side.