oven-sh / bun

Incredibly fast JavaScript runtime, bundler, test runner, and package manager – all in one
https://bun.sh
Other
73.24k stars 2.69k forks source link

`bun test` fails whenever it encounters an SVG #3673

Open bnussman-akamai opened 1 year ago

bnussman-akamai commented 1 year ago

What is the problem this feature would solve?

I would like bun test to not fail if it encounters an SVG. Currently it fails to prase.

Screenshot 2023-07-18 at 1 40 53 PM

What is the feature you are proposing to solve the problem?

You'd know better than me but Bun should be able to recognize an SVG and handle it accordingly.

I don't really care how an SVG is handleled. It could just return a null component for all I care.

What alternatives have you considered?

Jest or Vitest have ways to handle SVGs when testing react components.

mkarajohn commented 1 year ago

This is one of the fist things I encountered when I attempted to run my tests. Hope this gets resolved

colinhacks commented 1 year ago

So you're importing an .svg here? Are you using some plugin to make this work with your current test runner? This doesn't appear to be supported in jest by default.

mkarajohn commented 1 year ago

Yep, I am using vite-plugin-svgr in order to be able to load .svg files as React components, e.g. import { ReactComponent as Logo } from "./logo.svg"; Vitest, which I use, picks up vite plugins so it can handle .svgs too.

The same "issue" is there when running bun build as well. I guess bun needs some similar plugin in order to handle the .svg the same way?

adriandmitroca commented 1 year ago

This is the first blocking issue when migrating from Create React App.

TkDodo commented 1 year ago

seeing this too. import is:

import { ReactComponent as MyLogo } from './my-logo.svg'

yields:

SyntaxError: Import named 'ReactComponent' not found in module '/path/to/repo/my-logo.svg'.

We're using svgr to import svgs as React Components.

wldyslw commented 11 months ago

I tried to use bun as test runner while webpack drives main application.

In order to mimic svg loading behavior accepted in our project (same as discussed in this issue, namely import { ReactComponent as Icon } from 'whatever.svg') I decided to write a simple plugin and came up with this solution. Don't forget to add @svgr/core, @svgr/plugin-jsx and @svgr/plugin-svgo as dependencies, if you wanna use it.

# bunfig.toml
[test]
preload = ["./bunSvgPlugin.ts"]
// bunSvgPlugin.ts
import { plugin } from 'bun';

plugin({
    name: 'SVG',
    async setup(build) {
        const { transform } = await import('@svgr/core');
        const { readFileSync } = await import('fs');

        build.onLoad({ filter: /\.(svg)$/ }, async args => {
            const text = readFileSync(args.path, 'utf8');
            const contents = await transform(
                text,
                {
                    icon: true,
                    exportType: 'named',
                    plugins: ['@svgr/plugin-svgo', '@svgr/plugin-jsx'],
                },
                { componentName: 'ReactComponent' }
            );

            return {
                contents,
                loader: 'js', // not sure why js and not jsx, but it works only this way
            };
        });
    },
});
Amatewasu commented 11 months ago

Thank you a lot @wldyslw for your script, it works great!

It breaks string import of svg though (like in import PalletSvgUrl from '../../../assets/pallet-90deg.svg'; , I personally import in both ways...). I suggest adding the following line:

contents += `\nexport default (() => ReactComponent())();`;

So here you script with the small modification:

// bunSvgPlugin.ts
import { plugin } from 'bun';
import { writeFileSync } from 'fs';

/**
 * The code comes from: https://github.com/oven-sh/bun/issues/3673#issuecomment-1735840376
 */

plugin({
    name: 'SVG',
    async setup(build) {
        const { transform } = await import('@svgr/core');
        const { readFileSync } = await import('fs');

        build.onLoad({ filter: /\.(svg)$/ }, async args => {
            const text = readFileSync(args.path, 'utf8');
            let contents = await transform(
                text,
                {
                    icon: true,
                    exportType: 'named',
                    plugins: ['@svgr/plugin-svgo', '@svgr/plugin-jsx'],

                },
                { componentName: 'ReactComponent' }
            );

            contents += `\nexport default (() => ReactComponent())();`;

            return {
                contents,
                loader: 'js', // not sure why js and not jsx, but it works only this way
            };
        });
    },
});
Amatewasu commented 11 months ago

By the way, did you achieve to build your project using Bun instead of webpack? (I am not sure if it is the right place but it is quite related)

I wanted to give a try but the SVGs loadings fail.

Here are my files:

// build.ts
import Bun from 'bun';
import svgPlugin from './bunSvgPlugin';

console.log('Building...')

const result = await Bun.build({
    entrypoints: ['./src/index.tsx'],
    outdir: './dist',
    plugins: [
        svgPlugin
    ]
});

if (result.success){
    console.log('Build success!');
} else {
    console.error('Build failed!');

    // console.log(result);

    for (const log of result.logs){
        console.log(log)
    }
}
// bunSvgPlugin.ts
import { plugin } from 'bun';
import { writeFileSync } from 'fs';

/**
 * The code comes from: https://github.com/oven-sh/bun/issues/3673#issuecomment-1735840376
 */

const pluginsParams = {
    name: 'SVG',
    async setup(build) {
        const { transform } = await import('@svgr/core');
        const { readFileSync } = await import('fs');

        build.onLoad({ filter: /\.(svg)$/ }, async args => {
            const text = readFileSync(args.path, 'utf8');
            let contents = await transform(
                text,
                {
                    icon: true,
                    exportType: 'named',
                    plugins: ['@svgr/plugin-svgo', '@svgr/plugin-jsx'],

                },
                { componentName: 'ReactComponent' }
            );

            contents += `\nexport default (() => ReactComponent())();`;

            return {
                contents,
                loader: 'js', // not sure why js and not jsx, but it works only this way
            };
        });
    },
}

plugin(pluginsParams);

export default pluginsParams;

And here's the output:

X@X:~/X(feature/bun-test)$ bun run build.ts
Building...
Build failed!

error: Unexpected <

const ReactComponent = props => <svg xmlns="http://www.w3.org/2000/svg" fill="none" strokeLinecap="square" strok
                                ^
X/src/assets/X.svg:2:33 64

error: Unexpected <

const ReactComponent = props => <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" {...props}><pat
                                ^
X/src/assets/X.svg:2:33 64

error: Unexpected <

const ReactComponent = props => <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" {...props}><pat
                                ^
X/src/assets/X.svg:2:33 64

error: Unexpected <

const ReactComponent = props => <svg xmlns="http://www.w3.org/2000/svg" fill="none" strokeLinecap="square" strok
                                ^
X/src/assets/Xn.svg:2:33 64

error: Unexpected <

const ReactComponent = props => <svg xmlns="http://www.w3.org/2000/svg" fill="none" strokeLinecap="square" strok
                                ^
X/src/assets/X.svg:2:33 64

error: Unexpected <

const ReactComponent = props => <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 40

...

(i replaced personal data by X)

Amatewasu commented 11 months ago

The build does not fail if we change loader: 'js' to loader: 'jsx' (but then the tests fail).

wldyslw commented 11 months ago

@Amatewasu no, we have to stick to webpack. But in this situation the most straightforward solution is to change loader depending on command you're running. You can do that with, for instance, environmental variables, but I'm not that good at Bun and there's might be a better solution

samuellawerentz commented 4 weeks ago

Still facing this error, and we are not able to use bun test. I import svg as import { ReactComponent as MyLogo } from './my-logo.svg'

Getting this error SyntaxError: Import named 'ReactComponent' not found in module '/path/to/repo/my-logo.svg'.

@TkDodo did you find a way to get past this?

The suggested way above still does not work for me for some reason.