evanw / esbuild

An extremely fast bundler for the web
https://esbuild.github.io/
MIT License
38.08k stars 1.14k forks source link

Extend the behavior of the file loader #3835

Open JeanMeche opened 3 months ago

JeanMeche commented 3 months ago

The file loader works fine as it returns the path of the file I want (with the correct target path).

I'd like to extend that behavior by adding some additional properties, for example I my case, form images I would want to retrieve also the dimensions of images :

export function createImageLoaderWithAttributePlugin(): Plugin {
  return {
    name: 'image-loader-import-attributes',
    setup(build: PluginBuild) {
      build.onLoad({ filter: /\.(png|jpg|jpeg|gif|webp)$/ }, async (args) => {
        const loader = args.with.loader as Loader | undefined;
        if (!loader) {
          return undefined;
        }

        const image = await readFile(args.path);

        const dimensions = imageSize(image);
        const contents = `export default "${args.path}";
         export const width = ${dimensions.width};
         export const height = ${dimensions.height};
         `;
        return {
          contents,
          loader: 'file'
        };
      })
    },
  };
} 

When usin this plugin, I do get the correct path, but the additional properties are striped.

hyrious commented 3 months ago

The file loader is not aimed to return the full path to that file, but to add the file to assets and copy the file to dist folder. Anyway, to generate 2 modules from 1 file (a.png → file-a.png + module-width+height), you can re-export the file in the custom loader:

await build({
  entryPoints: ['src/index.js'],
  bundle: true,
  outdir: 'dist',
  loader: { '.png': 'file' },
  plugins: [{
    name: 'cutsom-file-loader',
    setup({ onLoad }) {
      onLoad({ filter: /\.png$/ }, args => {
        // If the user writes 'import a, {mtime} from "./a.png" with { loader: "custom" }',
        if (args.with.loader == 'custom') {
          // Return a module that re-exports itself and add additional properties.
          // Note this triggers the default loader .png=file configured above.
          let contents = `export {default} from ${JSON.stringify(args.path)}
                          export const mtime = ${+statSync(args.path).mtime}`
          return {contents, loader: 'js'}
        }
      })
    }
  }]
})

This trick is common to see in many cases: