I'm working on a NextJS project that leverages a wasm package via npm; specifically this is duckdb-wasm
.
duckdb-wasm
needs to initialize from a set of bundles (e.g. based on browser capability). this can be done with JSDelivr or by specifying the bundles in your code (see here: https://www.npmjs.com/package/@duckdb/duckdb-wasm#Instantiation).
JSDelivr runs into a CORS issue when deployed and so I'm trying to get the second option (i.e. webpack via manual specification) working with NextJS. Unfortunately, this seems to run into problems: TypeError: url.replace is not a function
.
Here's the _app.tsx for reference:
import "../styles/globals.css";
import * as duckdb from "@duckdb/duckdb-wasm";
import duckdb_wasm_coi from "@duckdb/duckdb-wasm/dist/duckdb-coi.wasm";
import duckdb_wasm_eh from "@duckdb/duckdb-wasm/dist/duckdb-eh.wasm";
import duckdb_wasm from "@duckdb/duckdb-wasm/dist/duckdb-mvp.wasm";
import {
DuckDBConnectionProvider,
DuckDBPlatform,
DuckDBProvider,
} from "@duckdb/react-duckdb";
import type { AppProps } from "next/app";
let manualBundles: duckdb.DuckDBBundles;
const logger = new duckdb.ConsoleLogger(duckdb.LogLevel.WARNING);
manualBundles = {
mvp: {
mainModule: duckdb_wasm,
mainWorker: new URL(
"@duckdb/duckdb-wasm/dist/duckdb-browser-mvp.worker.js",
import.meta.url
).toString(),
},
eh: {
mainModule: duckdb_wasm_eh,
mainWorker: new URL(
"@duckdb/duckdb-wasm/dist/duckdb-browser-eh.worker.js",
import.meta.url
).toString(),
},
coi: {
mainModule: duckdb_wasm_coi,
mainWorker: new URL(
"@duckdb/duckdb-wasm/dist/duckdb-browser-coi.worker.js",
import.meta.url
).toString(),
pthreadWorker: new URL(
"@duckdb/duckdb-wasm/dist/duckdb-browser-coi.pthread.worker.js",
import.meta.url
).toString(),
},
};
function App({ Component, pageProps }: AppProps) {
return (
<DuckDBPlatform logger={logger} bundles={manualBundles}>
<DuckDBProvider>
<DuckDBConnectionProvider>
<Component {...pageProps} />
</DuckDBConnectionProvider>
</DuckDBProvider>
</DuckDBPlatform>
);
}
export default App;
And the nextjs.config.js:
const withTM = require("next-transpile-modules")(["@duckdb/react-duckdb"]);
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
//swcMinify: true,
webpack: (config, options) => {
config.output.webassemblyModuleFilename = "static/wasm/[modulehash].wasm";
config.module.rules.push({
test: /.*\.wasm$/,
type: "asset/resource",
generator: {
filename: "static/wasm/[name].[contenthash][ext]",
},
});
config.experiments = {
asyncWebAssembly: true,
...config.experiments,
};
return config;
},
};
module.exports = withTM(nextConfig);
One way to work around this is to bring the definition of the bundles into a useEffect
but this isn't desirable for a number of reasons, for example the rendering of the child components needs to block on the value of bundles.