Giter VIP home page Giter VIP logo

Comments (2)

evanw avatar evanw commented on May 10, 2024

It's definitely possible to allow modules to resolve to multiple loaders. That's exactly what the WebAssembly example plugin does. Your problems are due to the way your code works.

import { ReactComponent as ImgSupplier } from 'assets/images/my-image.svg';

For this one, you are passing external: true to esbuild. That tells esbuild to exclude that import path from the bundle (i.e. it will be loaded at run-time, not at compile-time). That is already documented here. That's why your onLoad callback isn't running at compile-time. If you wanted that path to be internal to the bundle then you shouldn't be setting external: true for that path.

.my-class {
    background-image: url('/assets/images/my-image.svg');
}

For this one, your code is deliberately falling back to esbuild's default behavior (the part of your code that says anything else falls back on the default (file) loader). The default file loader takes the path and reads it from the file system. I'm guessing there is no file present at the absolute path /assets/images/my-image.svg, which is why esbuild is saying Could not resolve "/assets/images/my-image.svg". That is not a problem with esbuild. Perhaps you meant to use a relative path instead? Relative paths start with ./ while absolute paths start with /.

from esbuild.

trapgar avatar trapgar commented on May 10, 2024

Thanks, that pointed me in the right direction. For posterity the final code looked like below:

const { readFile: read } = require('node:fs/promises');
const path = require('node:path');

const JS_IMPORTS = ['import-statement', 'require-call', 'dynamic-import', 'require-resolve'];

/** Converts `my-image.svg` to `MyImage` */
const filename = fn => {
    return `${fn.split('-').map(s => `${s[0].toUpperCase()}${s.substring(1)}`).join('')}`.replace(/\.svg$/, '').replace(/^(\d)/, '_$1');
};

/**
 * esbuild plugin that converts SVG files to import-able ES modules.
 * @param {{}} options
 * @returns {import('esbuild').Plugin}
 */
module.exports = (options = {}) => {
    return {
        name: 'svg-resolver',
        setup(build) {
            build.onResolve({ filter: /\.svg$/ }, async args => {
                if (args.namespace !== 'svg-component' && JS_IMPORTS.includes(args.kind))
                    return { path: path.join(process.cwd(), 'src', args.path), namespace: 'svg-component' };
                else if (!args.path.startsWith(path.join(process.cwd(), 'src')))
                    return { path: path.join(process.cwd(), 'src', args.path), namespace: 'file' };
                else
                    return { path: args.path, namespace: 'file' };
            });

            build.onLoad({ filter: /.*/, namespace: 'svg-component' }, async (args) => {
                const fn = args.path.split('\\').at(-1);

                const fname = `Svg${filename(fn)}`;
                const contents =
`import React from 'react';
import contents from ${JSON.stringify(args.path)};

function ${fname}(props) {
    return (
        <img src={contents} {...props} />
    );
}

export { ${fname} as ReactComponent };

export default contents;
`;

                return {
                    contents,
                    loader: 'jsx',
                    resolveDir: process.cwd()
                };
            });

            build.onLoad({ filter: /.*/, namespace: 'svg-file' }, async (args) => {
                const fn = args.path;
                const svg = await read(args.path, { encoding: 'utf8' });
                const sanitized = svg.match(/<svg[^]+?<\/svg>/)?.[0];

                if (!sanitized)
                    return { errors: [{ pluginName: 'svgr', location: args.path, text: `${fn} is not a valid svg.` }] };

                return { contents: sanitized, loader: 'file' };
            });
        }
    };
};

My intention was to make this as close as possible to the react-scripts implementation, so it has an implicit root directory of 'src'.

from esbuild.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.