├── .github └── workflows │ └── deploy.yml ├── .gitignore ├── .vscode ├── extensions.json ├── resolve_npm_imports.json └── settings.json ├── README.md ├── eslintrc.js ├── package.json ├── packages ├── remix-app │ ├── app │ │ ├── entry.client.tsx │ │ ├── entry.server.tsx │ │ ├── root.tsx │ │ └── routes │ │ │ ├── index.tsx │ │ │ └── rust-demo.tsx │ ├── package.json │ ├── remix.config.js │ └── server.ts └── rust_functions │ ├── .gitignore │ ├── Cargo.toml │ ├── noop.js │ ├── package.json │ └── src │ ├── lib.rs │ └── utils.rs ├── patches └── @remix-run+dev+1.5.1.patch ├── public └── favicon.ico └── turbo.json /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: 🤸🏽 Integrate & Deploy 2 | on: [push] 3 | permissions: 4 | actions: write 5 | contents: read 6 | 7 | jobs: 8 | lint: 9 | name: 🤸🏽 lint, build & deploy 10 | runs-on: ubuntu-22.04 11 | permissions: 12 | actions: write 13 | id-token: write # Needed for auth with Deno Deploy 14 | contents: read # Needed to clone the repository 15 | steps: 16 | - name: 🛡 Cancel Previous Runs 17 | uses: styfle/cancel-workflow-action@0.9.1 18 | 19 | - name: 🐑 Clone Git Repo 20 | uses: actions/checkout@v3 21 | 22 | - name: 🦀 Setup Node 23 | uses: actions/setup-node@v3 24 | with: 25 | node-version: 16 26 | 27 | - name: ⛓ Install Node deps 28 | uses: bahmutov/npm-install@v1 29 | with: 30 | useLockFile: false 31 | 32 | - name: 🦕 Setup Deno 33 | uses: denoland/setup-deno@v1 34 | with: 35 | deno-version: v1.22 36 | 37 | - name: 🔬 Lint 38 | run: npm run lint 39 | 40 | - name: 🦀 Install wasm-ack 41 | uses: jetli/wasm-pack-action@v0.3.0 42 | 43 | - name: 💿 Remix Build 44 | run: npm run build 45 | # TODO: vendor..? 46 | 47 | # TODO fork action so only upload public & build dirs 48 | # then skip this step 49 | - name: 🗑️ Throw out deps 50 | run: rm -rf ./packages/remix-app/node_modules 51 | 52 | - name: 🗑️ Throw out more deps 53 | run: rm -rf ./node_modules 54 | 55 | - name: 📂 Make deno and remix dir 56 | run: mkdir -p ./deno 57 | 58 | - name: 📝 Copy Remix build files into outut dir 59 | run: cp -R ./packages/remix-app/{build,public} ./deno 60 | - name: 📝 Copy Rust WASM package files into output dir 61 | run: mkdir -p ./deno/rust_functions/build/ && cp -R ./packages/rust_functions/build/browser ./deno/rust_functions/build 62 | 63 | - name: 🔍 List file contents of root 64 | run: tree ./ 65 | 66 | - name: 🔍 List file contents of deno dir 67 | run: tree ./deno 68 | 69 | - name: 🗄 Archive built ouput 70 | uses: actions/upload-artifact@v3 71 | with: 72 | name: Deno Build Package 73 | path: ./deno 74 | 75 | - name: 🚛 Ship It 76 | uses: denoland/deployctl@v1 77 | with: 78 | project: "remix-air-metal-stack" 79 | entrypoint: "./build/index.js" 80 | root: "./deno" 81 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # We don't want lockfiles in stacks, as people could use a different package manager 2 | # This part will be removed by `remix.init` 3 | package-lock.json 4 | yarn.lock 5 | pnpm-lock.yaml 6 | pnpm-lock.yml 7 | 8 | node_modules 9 | 10 | /.cache 11 | /build 12 | /public/build 13 | .env 14 | 15 | /cypress/screenshots 16 | /cypress/videos 17 | /postgres-data 18 | 19 | /app/styles/tailwind.css 20 | 21 | .DS_Store 22 | 23 | // Don't include Rust compiled files 24 | */target/ 25 | */__test__/* 26 | */npm/* 27 | */.cache/* 28 | packages/remix-app/public/build/ 29 | packages/remix-app/build/ 30 | packages/remix-app/.cache 31 | */.turbo/* 32 | turbo-build.log -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "denoland.vscode-deno" 4 | ] 5 | } -------------------------------------------------------------------------------- /.vscode/resolve_npm_imports.json: -------------------------------------------------------------------------------- 1 | { 2 | "// This import map is used solely for the denoland.vscode-deno extension.": "", 3 | "// Remix does not support import maps.": "", 4 | "// Dependency management is done through `npm` and `node_modules/` instead.": "", 5 | "// Deno-only dependencies may be imported via URL imports (without using import maps).": "", 6 | 7 | "imports": { 8 | "// `@remix-run/deno` code is already a Deno module, so just get types for it directly from `node_modules/`": "", 9 | "@remix-run/deno": "../node_modules/@remix-run/deno/index.ts", 10 | "@remix-run/dev/server-build": "https://esm.sh/@remix-run/dev@1.5.0/server-build", 11 | "@remix-run/react": "https://esm.sh/@remix-run/react@1.5.0", 12 | "react": "https://esm.sh/react@17.0.2", 13 | "react-dom": "https://esm.sh/react-dom@17.0.2", 14 | "react-dom/server": "https://esm.sh/react-dom@17.0.2/server" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "deno.enable": true, 3 | "deno.importMap": "./.vscode/resolve_npm_imports.json", 4 | "deno.lint": true 5 | } 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Remix + Deno + Rust -> Webassembly - The Air Metal Stack 2 | 3 | Welcome to the Air Metal Stack for Remix! 🦕 + 🦀 4 | This stack is a good choice if you want to run on Deno, deploy to Deno Deploy, and use Rust compiled to WASM for certain functions. 5 | 6 | This is a monorepo, with a package for Rust compiled to WASM called `rust_functions`, and a package for your Remix app called `remix-app`. Both of these get built using Turborepo. 7 | 8 | There is a [demo](https://remix-air-metal-stack.deno.dev/) where you can see WASM running both on the worker via an action and on the client with an alert popup. 9 | 10 | For more, check out the [Remix docs](https://remix.run/docs), the [wasm-pack docs](https://rustwasm.github.io/wasm-pack/), and the [Rust page](https://www.rust-lang.org/). 11 | 12 | ## Install 13 | 14 | ```sh 15 | npx create-remix@latest --template benwis/air-metal-stack 16 | ``` 17 | 18 | ## Managing dependencies 19 | 20 | Read about [how we recommend to manage dependencies for Remix projects using Deno](https://github.com/remix-run/remix/blob/main/decisions/0001-use-npm-to-manage-npm-dependencies-for-deno-projects.md). 21 | 22 | - ✅ You should use `npm` to install NPM packages 23 | ```sh 24 | npm install react 25 | ``` 26 | ```ts 27 | import { useState } from "react"; 28 | ``` 29 | - ✅ You may use inlined URL imports or [deps.ts](https://deno.land/manual/examples/manage_dependencies#managing-dependencies) for Deno modules. 30 | ```ts 31 | import { copy } from "https://deno.land/std@0.138.0/streams/conversion.ts"; 32 | ``` 33 | - ❌ Do not use [import maps](https://deno.land/manual/linking_to_external_code/import_maps). 34 | 35 | ## Setting Up Rust 36 | 37 | 1. Install the Rust language and it's associated tools. You only need to run this once, as it installs globally. If you already have Rust installed, you can skip this step. 38 | ```sh 39 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh 40 | ``` 41 | 42 | 2. Install wasm-pack to wrap your compiled WASM code in a TS wrapper. The command for Mac and Linux is below. If you're on Windows, visit [this link](https://rustwasm.github.io/wasm-pack/installer/#) for an exe. You only need to run this once, as it installs globally. If you already have wasm-pack installed, you can skip this step. 43 | ```sh 44 | curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh 45 | ``` 46 | If you have issues with a lack of precompiled binaries for your platform(on an M1 Mac for example), you can just have cargo compile and install it with the below command. 47 | ```sh 48 | cargo install wasm-pack 49 | ``` 50 | 51 | 3. Install cargo-watch to allow the Rust code to compile on changes in dev mode 52 | ```sh 53 | cargo install cargo-watch 54 | ``` 55 | ## Development 56 | 57 | From your terminal in the project root: 58 | 59 | ```sh 60 | npm run build 61 | npm run dev 62 | ``` 63 | 64 | This starts your app in development mode, rebuilding TS and Rust assets on file changes. 65 | 66 | ### Type hints 67 | 68 | This template provides type hinting to VS Code via a [dedicated import map](./.vscode/resolve_npm_imports.json). 69 | 70 | To get types in another editor, use an extension for Deno that supports import maps and point your editor to `./.vscode/resolve_npm_imports.json`. 71 | 72 | For more, see [our decision doc for interop between Deno and NPM](https://github.com/remix-run/remix/blob/main/decisions/0001-use-npm-to-manage-npm-dependencies-for-deno-projects.md#vs-code-type-hints). 73 | 74 | ## Production 75 | 76 | First, build your app for production: 77 | 78 | ```sh 79 | npm run build 80 | ``` 81 | 82 | Then run the app in production mode: 83 | 84 | ```sh 85 | npm start 86 | ``` 87 | 88 | ## Deployment 89 | 90 | Building the Deno app (`npm run build`) results in two outputs: 91 | 92 | - `packages/remix-app/build/` (server bundle) 93 | - `packages/remix-app/public/build/` (browser bundle) 94 | - `packages/rust_functions/build/browser` (WASM browser bundle) 95 | 96 | You can deploy these bundles to any host that runs Deno, but here we'll focus on deploying to [Deno Deploy](https://deno.com/deploy). 97 | 98 | ## Setting up Deno Deploy 99 | 100 | 1. [Sign up](https://dash.deno.com/signin) for Deno Deploy. 101 | 102 | 2. [Create a new Deno Deploy project](https://dash.deno.com/new) for this app. 103 | 104 | 3. We use a Github Action to deploy our project's build artifacts to Deno Deploy. To enable this functionality, you must go to your project's settings in Deno and link your Github repo in manual mode. 105 | 106 | 4. Add a DENO_ENV environment variable to your Deno Deploy project with a value of `production`. This allows us to know when we're running in production and correctly resolve the path to the WASM files. 107 | 108 | 109 | ### Deploying to Deno Deploy 110 | 111 | After you've set up Deno Deploy, simply push to your Github repo. It should push your changes over to Deno Deploy. Check the Action in your Github Account, or the Deno Deploy project page for confirmation 112 | 113 | ### Changing Things 114 | - If you'd like to change the name of the Rust crate, be careful to change it in the following places 115 | - `packages/remix-app/server.ts` 116 | - `packages/remix-app/routes/rust-demo.tsx` 117 | - `packages/remix-app/entry.client.tsx` 118 | - `packages/remix-app/entry.server.tsx` 119 | - `packages/remix-app/package.json` 120 | 121 | ### Notes 122 | 123 | - Remix assumes that the public, build, and rust_functions folders will be in the root of the project on Deno Deploy. Changing that structure may lead to errors in production. Caution is advised. -------------------------------------------------------------------------------- /eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | // This tells ESLint to load the config from the package `eslint-config-custom` 4 | extends: ["custom"], 5 | settings: { 6 | next: { 7 | rootDir: ["apps/*/"], 8 | }, 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "air-metal-stacl", 3 | "version": "0.0.1", 4 | "private": true, 5 | "sideEffects": false, 6 | "workspaces": [ 7 | "config/*", 8 | "packages/*" 9 | ], 10 | "scripts": { 11 | "build": "npx --yes turbo run build", 12 | "dev": "npx --yes turbo run dev --parallel", 13 | "lint": "npx --yes turbo run lint", 14 | "typecheck": "npx --yes turbo run typecheck", 15 | "format": "prettier --write \"**/*.{ts,tsx,js,jsx,md}\"", 16 | "postinstall": "patch-package", 17 | "deploy": "cd packages && deployctl deploy --prod --include=remix-app,rust_functions --exclude=node_modules --project=remix-air-metal-stack ./remix-app/build/index.js" 18 | }, 19 | "devDependencies": { 20 | "patch-package": "^6.4.7", 21 | "prettier": "^2.6.2", 22 | "prisma": "^3.14.0", 23 | "typescript": "^4.7.2" 24 | }, 25 | "engines": { 26 | "npm": ">=7.0.0", 27 | "node": ">=14.0.0" 28 | }, 29 | "packageManager": "npm@8.5.5" 30 | } 31 | -------------------------------------------------------------------------------- /packages/remix-app/app/entry.client.tsx: -------------------------------------------------------------------------------- 1 | import { RemixBrowser } from "@remix-run/react"; 2 | import * as React from "react"; 3 | import { hydrateRoot } from "react-dom/client"; 4 | import init from "../../rust_functions/build/browser/rust_functions" 5 | import wasm from "../../rust_functions/build/browser/rust_functions_bg.wasm" 6 | 7 | init(wasm).then(() => { 8 | hydrateRoot(document, ); 9 | }) 10 | -------------------------------------------------------------------------------- /packages/remix-app/app/entry.server.tsx: -------------------------------------------------------------------------------- 1 | import type { EntryContext } from "@remix-run/deno"; 2 | import { RemixServer } from "@remix-run/react"; 3 | import * as React from "react"; 4 | import { renderToString } from "react-dom/server"; 5 | 6 | export default function handleRequest( 7 | request: Request, 8 | responseStatusCode: number, 9 | responseHeaders: Headers, 10 | remixContext: EntryContext, 11 | ) { 12 | const markup = renderToString( 13 | , 14 | ); 15 | 16 | responseHeaders.set("Content-Type", "text/html"); 17 | 18 | return new Response("" + markup, { 19 | status: responseStatusCode, 20 | headers: responseHeaders, 21 | }); 22 | } 23 | -------------------------------------------------------------------------------- /packages/remix-app/app/root.tsx: -------------------------------------------------------------------------------- 1 | import type { MetaFunction } from "@remix-run/deno"; 2 | import { 3 | Links, 4 | LiveReload, 5 | Meta, 6 | Outlet, 7 | Scripts, 8 | ScrollRestoration, 9 | } from "@remix-run/react"; 10 | import * as React from "react"; 11 | 12 | export const meta: MetaFunction = () => ({ 13 | charset: "utf-8", 14 | title: "Air Metal Stack Demo", 15 | viewport: "width=device-width,initial-scale=1", 16 | }); 17 | 18 | export default function App() { 19 | return ( 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /packages/remix-app/app/routes/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import {Link} from "@remix-run/react"; 3 | export default function Index() { 4 | return ( 5 |
6 |

Welcome to Remix

7 | 39 |
40 | ); 41 | } 42 | -------------------------------------------------------------------------------- /packages/remix-app/app/routes/rust-demo.tsx: -------------------------------------------------------------------------------- 1 | import type { ActionFunction } from "@remix-run/deno"; 2 | import { json } from "@remix-run/deno"; 3 | import { Form, useActionData, Link } from "@remix-run/react"; 4 | 5 | import { greet, add } from "../../../rust_functions/build/browser/rust_functions.js"; 6 | import * as React from "react"; 7 | 8 | export const action: ActionFunction = async ({ request }) => { 9 | const formData = await request.formData(); 10 | const { left_operand, operator, right_operand } = 11 | Object.fromEntries(formData); 12 | console.log(Object.fromEntries(formData)); 13 | let result = 0; 14 | switch (operator) { 15 | case "+": 16 | result = add(Number(left_operand), Number(right_operand)); 17 | console.log("result", result); 18 | return json({ 19 | result, 20 | }); 21 | default: 22 | // Implement other operators 23 | return json({ 24 | result: "🤷🏾", 25 | }); 26 | } 27 | }; 28 | 29 | export default function RustDemo() { 30 | const data = useActionData(); 31 | // Calls the greet function once on page load. This code can only be run on the browser since it calls alert() 32 | React.useEffect(() => { 33 | greet(); 34 | }, []); 35 | 36 | return ( 37 |
42 |
43 |

47 | Rust Calculator 48 |

49 |
50 | 57 | 68 | 75 |
76 | 77 | 86 |
90 | {data?.result ? data?.result : ""} 91 |
92 | 97 | Back 98 | 99 |
100 |
101 | ); 102 | } 103 | -------------------------------------------------------------------------------- /packages/remix-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "remix-air-metal", 3 | "private": true, 4 | "sideEffects": false, 5 | "scripts": { 6 | "build": "remix build", 7 | "dev": "run-p dev:*", 8 | "dev:deno": "cross-env NODE_ENV=development deno run --unstable --watch --allow-net --allow-read --allow-env ./build/index.js", 9 | "dev:remix": "remix watch", 10 | "format": "deno fmt --ignore=node_modules,build,public/build", 11 | "lint": "deno lint --ignore=node_modules,build,public/build", 12 | "start": "cross-env NODE_ENV=production deno run --unstable --allow-net --allow-read --allow-env ./build/index.js" 13 | }, 14 | "dependencies": { 15 | "@remix-run/deno": "^1.5.1", 16 | "@remix-run/react": "^1.5.1", 17 | "react": "^18.1.0", 18 | "react-dom": "^18.1.0", 19 | "rust_functions": "file:./packages/rust_functions" 20 | }, 21 | "devDependencies": { 22 | "@remix-run/dev": "^1.5.1", 23 | "cross-env": "^7.0.3", 24 | "npm-run-all": "^4.1.5", 25 | "patch-package": "^6.4.7" 26 | }, 27 | "engines": { 28 | "node": ">=14" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/remix-app/remix.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | serverBuildTarget: "deno", 3 | server: "./server.ts", 4 | /* 5 | If live reload causes page to re-render without changes (live reload is too fast), 6 | increase the dev server broadcast delay. 7 | 8 | If live reload seems slow, try to decrease the dev server broadcast delay. 9 | */ 10 | devServerBroadcastDelay: 300, 11 | ignoredRouteFiles: ["**/.*"], 12 | // appDirectory: "app", 13 | // assetsBuildDirectory: "public/build", 14 | // serverBuildPath: "build/index.js", 15 | // publicPath: "/build/", 16 | }; 17 | -------------------------------------------------------------------------------- /packages/remix-app/server.ts: -------------------------------------------------------------------------------- 1 | import { serve } from "https://deno.land/std@0.128.0/http/server.ts"; 2 | import { createRequestHandlerWithStaticFiles } from "@remix-run/deno"; 3 | // Import path interpreted by the Remix compiler 4 | import * as build from "@remix-run/dev/server-build"; 5 | 6 | // Import the WASM init function from your Rust JS wrapper 7 | import init from "../rust_functions/build/browser/rust_functions.js"; 8 | 9 | 10 | const go = async () => { 11 | 12 | // You'll need to read the WASM file from the build directory 13 | if (Deno.env.get("DENO_ENV") == "production") { 14 | await init(Deno.readFile('./rust_functions/build/browser/rust_functions_bg.wasm')); 15 | } else { 16 | await init(Deno.readFile('../rust_functions/build/browser/rust_functions_bg.wasm')); 17 | } 18 | const remixHandler = createRequestHandlerWithStaticFiles({ 19 | build, 20 | mode: Deno.env.get("NODE_ENV"), 21 | getLoadContext: () => ({}), 22 | }); 23 | 24 | const port = Number(Deno.env.get("PORT")) || 8000; 25 | console.log(`Listening on http://localhost:${port}`); 26 | serve(remixHandler, { port }); 27 | } 28 | go(); 29 | -------------------------------------------------------------------------------- /packages/rust_functions/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | 13 | # MSVC Windows builds of rustc generate these, which store debugging information 14 | *.pdb -------------------------------------------------------------------------------- /packages/rust_functions/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-functions" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [lib] 7 | crate-type = ["cdylib"] 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [dependencies] 11 | wasm-bindgen = "0.2.79" -------------------------------------------------------------------------------- /packages/rust_functions/noop.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benwis/air-metal-stack/5da41bee3474f2443fa69d4fec463a7c2240b646/packages/rust_functions/noop.js -------------------------------------------------------------------------------- /packages/rust_functions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rust_functions", 3 | "version": "1.0.0", 4 | "license": "MIT", 5 | "scripts": { 6 | "build": "npm run build:browser && npm run build:node", 7 | "build:browser": "wasm-pack build --target web --out-dir ./build/browser && rimraf ./build/browser/package.json", 8 | "build:node": "wasm-pack build --target nodejs --out-dir ./build/node && rimraf ./build/node/package.json", 9 | "dev": "cargo watch -i .gitignore -i \"pkg/*\" -s \"wasm-pack build --target web --out-dir ./build/browser && wasm-pack build --target nodejs --out-dir ./build/node\"" 10 | }, 11 | "sideEffects": false, 12 | "files": [ 13 | "build" 14 | ], 15 | "types": "./build/browser/rust_functions.d.ts", 16 | "exports": { 17 | ".": { 18 | "browser": "./build/browser/rust_functions.js", 19 | "deno": "./build/browser/rust_functions.js", 20 | "node": "./build/node/rust_functions.js" 21 | }, 22 | "./binary.wasm": { 23 | "browser": "./build/browser/rust_functions_bg.wasm", 24 | "deno": "./build/browser/rust_functions.js", 25 | "node": "./noop.js" 26 | } 27 | }, 28 | "devDependencies": { 29 | "rimraf": "^3.0.2" 30 | } 31 | } -------------------------------------------------------------------------------- /packages/rust_functions/src/lib.rs: -------------------------------------------------------------------------------- 1 | use wasm_bindgen::prelude::*; 2 | 3 | // When the `wee_alloc` feature is enabled, use `wee_alloc` as the global 4 | // allocator. 5 | #[cfg(feature = "wee_alloc")] 6 | #[global_allocator] 7 | static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; 8 | 9 | #[wasm_bindgen] 10 | extern "C" { 11 | fn alert(s: &str); 12 | } 13 | 14 | #[wasm_bindgen] 15 | pub fn add(a: i32, b: i32) -> i32 { 16 | a + b 17 | } 18 | 19 | #[wasm_bindgen] 20 | pub fn greet() { 21 | alert("Welcome to the Air Metal Stack!\nThis alert comes from browser-side WASM!"); 22 | } 23 | -------------------------------------------------------------------------------- /packages/rust_functions/src/utils.rs: -------------------------------------------------------------------------------- 1 | pub fn set_panic_hook() { 2 | // When the `console_error_panic_hook` feature is enabled, we can call the 3 | // `set_panic_hook` function at least once during initialization, and then 4 | // we will get better error messages if our code ever panics. 5 | // 6 | // For more details see 7 | // https://github.com/rustwasm/console_error_panic_hook#readme 8 | #[cfg(feature = "console_error_panic_hook")] 9 | console_error_panic_hook::set_once(); 10 | } 11 | -------------------------------------------------------------------------------- /patches/@remix-run+dev+1.5.1.patch: -------------------------------------------------------------------------------- 1 | diff --git a/node_modules/@remix-run/dev/compiler/loaders.js b/node_modules/@remix-run/dev/compiler/loaders.js 2 | index 99850a3..a5af2b4 100644 3 | --- a/node_modules/@remix-run/dev/compiler/loaders.js 4 | +++ b/node_modules/@remix-run/dev/compiler/loaders.js 5 | @@ -59,6 +59,7 @@ const loaders = { 6 | ".ts": "ts", 7 | ".tsx": "tsx", 8 | ".ttf": "file", 9 | + ".wasm": "file", 10 | ".wav": "file", 11 | ".webm": "file", 12 | ".webmanifest": "file", 13 | diff --git a/node_modules/@remix-run/dev/modules.d.ts b/node_modules/@remix-run/dev/modules.d.ts 14 | index 4bc9a62..ce8e35c 100644 15 | --- a/node_modules/@remix-run/dev/modules.d.ts 16 | +++ b/node_modules/@remix-run/dev/modules.d.ts 17 | @@ -69,6 +69,10 @@ declare module "*.ttf" { 18 | let asset: string; 19 | export default asset; 20 | } 21 | +declare module "*.wasm" { 22 | + let asset: string; 23 | + export default asset; 24 | +} 25 | declare module "*.wav" { 26 | let asset: string; 27 | export default asset; 28 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benwis/air-metal-stack/5da41bee3474f2443fa69d4fec463a7c2240b646/public/favicon.ico -------------------------------------------------------------------------------- /turbo.json: -------------------------------------------------------------------------------- 1 | { 2 | "pipeline": { 3 | "build": { 4 | "dependsOn": ["^build"], 5 | "outputs": ["build/**", "public/build/**", ".cache/**"] 6 | }, 7 | "dev": { 8 | "dependsOn": ["^build"], 9 | "cache": false 10 | }, 11 | "lint": { 12 | "outputs": [] 13 | }, 14 | "typecheck": { 15 | "outputs": ["tsconfig.tsbuildinfo"] 16 | } 17 | } 18 | } 19 | --------------------------------------------------------------------------------