UNPKG

@casadi/casadi-wasm

Version:

CasADi — symbolic framework for algorithmic differentiation and numerical optimization, compiled to WebAssembly. Runs in Node.js with on-demand solver plugins (ipopt, fatrop, sundials, ...).

99 lines (91 loc) 4.82 kB
// // MIT No Attribution // // Copyright (C) 2010-2023 Joel Andersson, Joris Gillis, Moritz Diehl, KU Leuven. // // Permission is hereby granted, free of charge, to any person obtaining a copy of this // software and associated documentation files (the "Software"), to deal in the Software // without restriction, including without limitation the rights to use, copy, modify, // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // // // Browser loader for the SWIG -wasm-js `casadi.js` wrapper. // // `casadi.js` is generated for Node (CommonJS): its first lines do // const createWasm = require('./casadi_wasm.js'); // const __path = require('path'); // module.exports = async function createcasadi() { ... __dirname ... } // ...and the remaining ~25k lines are plain ECMAScript. Rather than // patch the generator, we evaluate it here inside a tiny CommonJS // sandbox that supplies exactly those three node-isms, so the same // unmodified file runs in a browser. // // Why this is NOT a CORS problem: everything is fetched same-origin // from the page's own server. The only requirement is that the page // is *served over http(s)*, not opened as a file:// URL -- browsers // refuse to fetch() a sibling .wasm over file://. See README.md. // // Usage (see _template.html): // <script src="_casadi_browser.js"></script> // <script src="rosenbrock.js"></script> // <script>loadCasadi("./").then(M => example(M, console.log));</script> async function loadCasadi(base) { base = base || "./"; if (!base.endsWith("/")) base += "/"; // Most common mistake: opening the page as a file:// URL. Browsers // reject fetch() of local files with an opaque "NetworkError", so // detect it up front and explain. if (typeof location !== "undefined" && location.protocol === "file:") { throw new Error( "this page must be served over http(s), not opened as a file:// URL " + "(browsers block fetch() of local files). From this folder run: " + "`python3 -m http.server 8000` then open http://localhost:8000/"); } // Evaluate a CommonJS module fetched from `base` inside a sandbox. const evalCjs = async (file, requireFn) => { let resp; try { resp = await fetch(base + file); } catch (e) { throw new Error( `could not fetch ${base + file} (${e.message || e}). Is the page ` + `served over http, and are the casadi build artifacts (casadi.js, ` + `casadi_wasm.js, casadi_wasm.wasm, casadi_wasm.data) present at ` + `"${base}"? See README.md.`); } if (!resp.ok) throw new Error( `${base + file}: ${resp.status} ${resp.statusText} -- artifact missing ` + `at "${base}"? Symlink/copy them in or pass ?casadi=<url>. See README.md.`); const src = await resp.text(); const factory = new Function( "module", "exports", "require", "__dirname", "__filename", src + "\n;return module.exports;"); const module = { exports: {} }; return factory(module, module.exports, requireFn, base.replace(/\/$/, ""), base + file); }; // 1) The emscripten core. It auto-detects the browser environment // (no `process`) and will fetch casadi_wasm.{wasm,data} via the // locateFile we pass from the wrapper -- so it never touches the // `require` we hand it; we still provide one that errors loudly. const createWasm = await evalCjs("casadi_wasm.js", (p) => { throw new Error("casadi_wasm.js required unexpected module: " + p); }); // 2) The SWIG wrapper. Resolve its two requires; `path` only needs // `join` (used by the wrapper's locateFile). const requireForWrapper = (p) => { if (p === "./casadi_wasm.js" || p.endsWith("/casadi_wasm.js")) return createWasm; if (p === "path") return { join: (...a) => a.filter(Boolean).join("/").replace(/\/{2,}/g, "/") }; throw new Error("casadi.js required unexpected module: " + p); }; const createcasadi = await evalCjs("casadi.js", requireForWrapper); return await createcasadi(); } // Also export for bundlers / module contexts that load this file via require/import. if (typeof module !== "undefined" && module.exports) module.exports = { loadCasadi };