From bfd96ac012cec1c6990823d4489ebea79601a605 Mon Sep 17 00:00:00 2001 From: Max Proske Date: Sun, 11 Dec 2022 18:10:14 -0800 Subject: [PATCH] Fix `with-webassembly` example and convert to Typescript (#43677) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fixes `with-webassembly` example unable to build with the current `next.config.js`. https://github.com/vercel/next.js/issues/29362#issuecomment-932767530 - Converted example to TypeScript ```js // Before config.output.webassemblyModuleFilename = 'static/wasm/[modulehash].wasm' // After config.output.webassemblyModuleFilename = isServer && !dev ? '../static/wasm/[modulehash].wasm' : 'static/wasm/[modulehash].wasm' ``` ``` > Build error occurred Error: Export encountered errors on following paths: / at /Users/max/dev/next.js/examples/with-webassembly/node_modules/next/dist/export/index.js:408:19 at process.processTicksAndRejections (node:internal/process/task_queues:95:5) at async Span.traceAsyncFn (/Users/max/dev/next.js/examples/with-webassembly/node_modules/next/dist/trace/trace.js:79:20) at async /Users/max/dev/next.js/examples/with-webassembly/node_modules/next/dist/build/index.js:1342:21 at async Span.traceAsyncFn (/Users/max/dev/next.js/examples/with-webassembly/node_modules/next/dist/trace/trace.js:79:20) at async /Users/max/dev/next.js/examples/with-webassembly/node_modules/next/dist/build/index.js:1202:17 at async Span.traceAsyncFn (/Users/max/dev/next.js/examples/with-webassembly/node_modules/next/dist/trace/trace.js:79:20) at async Object.build [as default] (/Users/max/dev/next.js/examples/with-webassembly/node_modules/next/dist/build/index.js:65:29)  ELIFECYCLE  Command failed with exit code 1. ``` ## Documentation / Examples - [X] Make sure the linting passes by running `pnpm build && pnpm lint` - [X] The "examples guidelines" are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md) --- examples/with-webassembly/README.md | 2 ++ .../components/RustComponent.tsx | 24 +++++++++++++++++++ examples/with-webassembly/next.config.js | 13 +++++++--- examples/with-webassembly/package.json | 16 +++++++++---- examples/with-webassembly/pages/api/edge.js | 11 --------- examples/with-webassembly/pages/api/edge.ts | 16 +++++++++++++ examples/with-webassembly/pages/index.js | 24 ------------------- examples/with-webassembly/pages/index.tsx | 15 ++++++++++++ examples/with-webassembly/tsconfig.json | 20 ++++++++++++++++ examples/with-webassembly/wasm.d.ts | 3 +++ 10 files changed, 101 insertions(+), 43 deletions(-) create mode 100644 examples/with-webassembly/components/RustComponent.tsx delete mode 100644 examples/with-webassembly/pages/api/edge.js create mode 100644 examples/with-webassembly/pages/api/edge.ts delete mode 100644 examples/with-webassembly/pages/index.js create mode 100644 examples/with-webassembly/pages/index.tsx create mode 100644 examples/with-webassembly/tsconfig.json create mode 100644 examples/with-webassembly/wasm.d.ts diff --git a/examples/with-webassembly/README.md b/examples/with-webassembly/README.md index b2f9822c7fd2e0c..e846c711927e056 100644 --- a/examples/with-webassembly/README.md +++ b/examples/with-webassembly/README.md @@ -32,4 +32,6 @@ To compile `src/add.rs` to `add.wasm` run: npm run build-rust # or yarn build-rust +# or +pnpm build-rust ``` diff --git a/examples/with-webassembly/components/RustComponent.tsx b/examples/with-webassembly/components/RustComponent.tsx new file mode 100644 index 000000000000000..6acf05307bf666f --- /dev/null +++ b/examples/with-webassembly/components/RustComponent.tsx @@ -0,0 +1,24 @@ +import type { AddModuleExports } from '../wasm' +import dynamic from 'next/dynamic' + +interface RustComponentProps { + number: Number +} + +const RustComponent = dynamic({ + loader: async () => { + // Import the wasm module + // @ts-ignore + const exports = (await import('../add.wasm')) as AddModuleExports + const { add_one: addOne } = exports + + // Return a React component that calls the add_one method on the wasm module + return ({ number }: RustComponentProps) => ( +
+ <>{addOne(number)} +
+ ) + }, +}) + +export default RustComponent diff --git a/examples/with-webassembly/next.config.js b/examples/with-webassembly/next.config.js index 4d5f3d98c8b8c9d..42ea82bf4049fce 100644 --- a/examples/with-webassembly/next.config.js +++ b/examples/with-webassembly/next.config.js @@ -1,7 +1,12 @@ /** @type {import('next').NextConfig} */ -module.exports = { - webpack(config) { - config.output.webassemblyModuleFilename = 'static/wasm/[modulehash].wasm' +const nextConfig = { + webpack(config, { isServer, dev }) { + // Use the client static directory in the server bundle and prod mode + // Fixes `Error occurred prerendering page "/"` + config.output.webassemblyModuleFilename = + isServer && !dev + ? '../static/wasm/[modulehash].wasm' + : 'static/wasm/[modulehash].wasm' // Since Webpack 5 doesn't enable WebAssembly by default, we should do it manually config.experiments = { ...config.experiments, asyncWebAssembly: true } @@ -9,3 +14,5 @@ module.exports = { return config }, } + +module.exports = nextConfig diff --git a/examples/with-webassembly/package.json b/examples/with-webassembly/package.json index fe8da1928db97d3..aeee0eac806674a 100644 --- a/examples/with-webassembly/package.json +++ b/examples/with-webassembly/package.json @@ -1,14 +1,20 @@ { "private": true, - "dependencies": { - "next": "latest", - "react": "^18.2.0", - "react-dom": "^18.2.0" - }, "scripts": { "dev": "next", "build": "next build", "build-rust": "rustc --target wasm32-unknown-unknown -O --crate-type=cdylib src/add.rs -o add.wasm", "start": "next start" + }, + "dependencies": { + "next": "latest", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@types/node": "^18.11.10", + "@types/react": "^18.0.26", + "@types/react-dom": "^18.0.9", + "typescript": "^4.9.3" } } diff --git a/examples/with-webassembly/pages/api/edge.js b/examples/with-webassembly/pages/api/edge.js deleted file mode 100644 index f724fd32280f5e1..000000000000000 --- a/examples/with-webassembly/pages/api/edge.js +++ /dev/null @@ -1,11 +0,0 @@ -import add_module from '../../add.wasm?module' - -const instance$ = WebAssembly.instantiate(add_module) - -export default async function edgeExample() { - const { exports } = await instance$ - const number = exports.add_one(10) - return new Response(`got: ${number}`) -} - -export const config = { runtime: 'experimental-edge' } diff --git a/examples/with-webassembly/pages/api/edge.ts b/examples/with-webassembly/pages/api/edge.ts new file mode 100644 index 000000000000000..0e9a30908ae409a --- /dev/null +++ b/examples/with-webassembly/pages/api/edge.ts @@ -0,0 +1,16 @@ +import type { AddModuleExports } from '../../wasm' +// @ts-ignore +import addWasm from '../../add.wasm?module' + +const module$ = WebAssembly.instantiate(addWasm) + +export default async function handler() { + const instance = (await module$) as any + const exports = instance.exports as AddModuleExports + const { add_one: addOne } = exports + const number = addOne(10) + + return new Response(`got: ${number}`) +} + +export const config = { runtime: 'experimental-edge' } diff --git a/examples/with-webassembly/pages/index.js b/examples/with-webassembly/pages/index.js deleted file mode 100644 index ba2b622f49f1d4c..000000000000000 --- a/examples/with-webassembly/pages/index.js +++ /dev/null @@ -1,24 +0,0 @@ -import { withRouter } from 'next/router' -import dynamic from 'next/dynamic' -import Link from 'next/link' - -const RustComponent = dynamic({ - loader: async () => { - // Import the wasm module - const rustModule = await import('../add.wasm') - // Return a React component that calls the add_one method on the wasm module - return (props) =>
{rustModule.add_one(props.number)}
- }, -}) - -const Page = ({ router: { query } }) => { - const number = parseInt(query.number || 30) - return ( -
- - + -
- ) -} - -export default withRouter(Page) diff --git a/examples/with-webassembly/pages/index.tsx b/examples/with-webassembly/pages/index.tsx new file mode 100644 index 000000000000000..6d6aba712f39194 --- /dev/null +++ b/examples/with-webassembly/pages/index.tsx @@ -0,0 +1,15 @@ +import { useRouter } from 'next/router' +import Link from 'next/link' +import RustComponent from '../components/RustComponent' + +export default function Page() { + const { query } = useRouter() + const number = parseInt(query.number as string) || 30 + + return ( +
+ + + +
+ ) +} diff --git a/examples/with-webassembly/tsconfig.json b/examples/with-webassembly/tsconfig.json new file mode 100644 index 000000000000000..5b3c8783e426628 --- /dev/null +++ b/examples/with-webassembly/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "wasm.d.ts"], + "exclude": ["node_modules"] +} diff --git a/examples/with-webassembly/wasm.d.ts b/examples/with-webassembly/wasm.d.ts new file mode 100644 index 000000000000000..db4e6b84e3ea33b --- /dev/null +++ b/examples/with-webassembly/wasm.d.ts @@ -0,0 +1,3 @@ +export interface AddModuleExports { + add_one(number: Number): Number +}