kleisauke / wasm-vips

libvips for the browser and Node.js, compiled to WebAssembly with Emscripten.
https://kleisauke.github.io/wasm-vips/
MIT License
463 stars 25 forks source link

setup wasm-vips for next.js/react #50

Closed serafimsanvol closed 11 months ago

serafimsanvol commented 12 months ago

First of all would like to thank for your job, it's awesome that people making possible projects like this one. I think it has bright future.

So I was working with node.js sharp library, wrapper around libvips. Found out that everything now can be done even without backend, found out about your project and started trying to work but stuck. I have no problems when using it on backend (I didn't try much because it's not what I wanted). But it produces different errors each time I'm trying to make it work with next.js

For example I'm constantly getting this error: B7735319-586E-46FA-B617-03712A19B44A

Link to my project: https://github.com/serafimsanvol/my-app/blob/main/src/app/page.tsx

It's just basic next starter project so I'm sure that there is something with library configuration but I don't know what are workarounds here, can you help/explain/tell me that it's bug and actually unexpected behaviour?

On different project with slightly different setup and set headers as required it shows different error:

E7FA1AAE-6227-44E9-BC4B-0BC504D551CB

Any help/suggestions appreciated, thanks

kleisauke commented 11 months ago

It looks like Next.js is encountering some issues when attempting to bundle wasm-vips' ES6 modules, related to the new URL('./', import.meta.url) syntax.

If you want to enable wasm-vips solely for server-side rendering (SSR), you can use this config: next.config.js:

/** @type {import('next').NextConfig} */
const nextConfig = {
    webpack: (config) => {
        // Disable evaluating of `import.meta.*` syntax
        // https://webpack.js.org/configuration/module/#moduleparserjavascriptimportmeta
        config.module.parser.javascript.importMeta = false;

        // Disable parsing of `new URL()` syntax
        // https://webpack.js.org/configuration/module/#moduleparserjavascripturl
        config.module.parser.javascript.url = false;

        // Alternatively, to bundle the CommonJS module:
        // Ensure "require" has a higher priority when matching export conditions.
        // https://webpack.js.org/configuration/resolve/#resolveconditionnames
        //config.resolve.conditionNames = ['require'];

        return config;
    }
}

module.exports = nextConfig;

To use wasm-vips in client-side environments, it remains essential to opt-in to a cross-origin isolated state and serve vips.js, vips.wasm and vips.worker.js from the same directory. Assuming these files are in the public/ directory, you could do this: next.config.js:

/** @type {import('next').NextConfig} */
const nextConfig = {
    async headers() {
        return [
            {
                source: '/:path*',
                headers: [
                    {
                        key: 'Cross-Origin-Embedder-Policy',
                        value: 'require-corp'
                    },
                    {
                        key: 'Cross-Origin-Opener-Policy',
                        value: 'same-origin'
                    }
                ]
            }
        ]
    },
    webpack: (config) => {
        // Ensure "require" has a higher priority when matching export conditions.
        // https://webpack.js.org/configuration/resolve/#resolveconditionnames
        config.resolve.conditionNames = ['require'];

        return config;
    }
}

module.exports = nextConfig;

src/app/page.tsx:

'use client';
import { useEffect } from 'react';
import Vips from 'wasm-vips';

export default function Home() {
  useEffect(() => {
    Vips({
      // Disable dynamic modules
      dynamicLibraries: [],
      // Workers needs to import the unbundled version of `vips.js`
      mainScriptUrlOrBlob: './vips.js',
      // wasm-vips is served from the public directory
      locateFile: (fileName, scriptDirectory) => fileName,
    }).then((vips) => {
      console.log('libvips version:', vips.version());
    });
  }, []);

  return (<h1>Hello wasm-vips!</h1>)
}
serafimsanvol commented 11 months ago

@kleisauke, thanks for your help! Now it's working for me

kleisauke commented 11 months ago

Great! I'll close, please feel free to re-open if questions remain.