this filter plugin intercepts all entities and makes them sequentially pass through three resolvers,
so that an absolute path to the resource can be acquired in the best possible way (citation needed).
Important
you must include the resolverPlugin somewhere in your esbuild build-options,
otherwise this plugin will not be able to resolve to absolute paths on its own,
and all efforts of this plugin will pretty much be for naught.
details
an explanation of the operations performed by each of the three resolvers follows:
it is through this resolver that things like import-maps
and the deno-package-resolver get inserted into the entry-point resources,
and then seeded into their dependencies.
2. plugin-data inheritor
a fundamental problem (design choice?) with esbuild is that resource's pluginDatadoes not propagate to the child-dependencies,
when esbuild's native resolvers and loaders process the resource.
this problem of stripping away valuable pluginData can also occur in external plugins that are not designed mindfully.
as a consequence, we would not be able to embed import-maps and runtime-package-resolvers that can be picked up by dependencies.
thus, to counteract this issue, this resolver inspects the pluginData of every resolved entity, stores it in a dictionary for book-keeping,
and then, when a dependency-resource comes along with its plugin-data stripped away (i.e. args.pluginData === undefined),
this resolver inserts its dependent's (args.importer) plugin-data.
in effect, this resolver permits inheritance of plugin-data when it is stripped away.
3. absolute-path resolution
finally, we come to the resolver which implicitly calls the namespaced resolvers of the resolverPlugin,
which is a pipeline that can resolve import-maps,
deno-packages,
node-packages (node_modules),
and perform generic path-joining, in order to get the absolute path (doesn't have to be filesystem path) to the resource.
most resolvers of the resolverPlugin require pluginData to be useful.
which is why we have to ensure its availability and inheritance through the two prior resolvers,
in order for the resolverPlugin to be effective.
Tip
while the placement order of the resolverPlugin does not matter (invariant behavior),
placing it at the front would reduce the number of redundant onResolve callbacks received by this plugin,
which then need to be bounced-back to prevent an indefinite onResolve recursion.
you may wonder how this plugin does not spiral into an endless recursion of onResolve callbacks,
when it uses the "capture-all" filter that spans throughout all namespaces, meanwhile using build.resove().
that's because upon the interception of a new entity, we insert a unique symbol marker into the pluginData,
which when detected again, halts the plugin from processing it further (by returning undefined).
moreover, this plugin validates that the args.namespace it receives must be one of [undefined, "", "file"]
(see defaultEsbuildNamespaces), otherwise it will terminate processing it any further.
this check is put in place to prevent this plugin from treading into the territory of other plugins' namespaces,
which would potentially ruin their logic and pluginData.
this filter plugin intercepts all entities and makes them sequentially pass through three resolvers, so that an absolute path to the resource can be acquired in the best possible way (citation needed).
you must include the resolverPlugin somewhere in your esbuild build-options, otherwise this plugin will not be able to resolve to absolute paths on its own, and all efforts of this plugin will pretty much be for naught.
details
an explanation of the operations performed by each of the three resolvers follows:
1. initial plugin-data injector
this resolver simply inserts the user's EntryPluginSetupConfig.initialPluginData to all entry-points which lack an
args.pluginData
. but when EntryPluginSetupConfig.forceInitialPluginData istrue
, the entry-points with existing pluginData will be overwritten.it is through this resolver that things like import-maps and the deno-package-resolver get inserted into the entry-point resources, and then seeded into their dependencies.
2. plugin-data inheritor
a fundamental problem (design choice?) with esbuild is that resource's
pluginData
does not propagate to the child-dependencies, when esbuild's native resolvers and loaders process the resource. this problem of stripping away valuablepluginData
can also occur in external plugins that are not designed mindfully.as a consequence, we would not be able to embed import-maps and runtime-package-resolvers that can be picked up by dependencies.
thus, to counteract this issue, this resolver inspects the
pluginData
of every resolved entity, stores it in a dictionary for book-keeping, and then, when a dependency-resource comes along with its plugin-data stripped away (i.e.args.pluginData === undefined
), this resolver inserts its dependent's (args.importer
) plugin-data. in effect, this resolver permits inheritance of plugin-data when it is stripped away.3. absolute-path resolution
finally, we come to the resolver which implicitly calls the namespaced resolvers of the resolverPlugin, which is a pipeline that can resolve import-maps, deno-packages, node-packages (
node_modules
), and perform generic path-joining, in order to get the absolute path (doesn't have to be filesystem path) to the resource.most resolvers of the resolverPlugin require
pluginData
to be useful. which is why we have to ensure its availability and inheritance through the two prior resolvers, in order for the resolverPlugin to be effective.while the placement order of the resolverPlugin does not matter (invariant behavior), placing it at the front would reduce the number of redundant
onResolve
callbacks received by this plugin, which then need to be bounced-back to prevent an indefiniteonResolve
recursion.you may wonder how this plugin does not spiral into an endless recursion of
onResolve
callbacks, when it uses the "capture-all" filter that spans throughout all namespaces, meanwhile usingbuild.resove()
.that's because upon the interception of a new entity, we insert a unique
symbol
marker into thepluginData
, which when detected again, halts the plugin from processing it further (by returningundefined
).moreover, this plugin validates that the
args.namespace
it receives must be one of[undefined, "", "file"]
(see defaultEsbuildNamespaces), otherwise it will terminate processing it any further. this check is put in place to prevent this plugin from treading into the territory of other plugins' namespaces, which would potentially ruin their logic andpluginData
.