"use strict";(self.webpackChunkspeice_io=self.webpackChunkspeice_io||[]).push([["4384"],{52736:function(e,t,n){n.r(t),n.d(t,{assets:function(){return h},contentTitle:function(){return a},default:function(){return d},frontMatter:function(){return r},metadata:function(){return s},toc:function(){return c}});var s=n(58595),i=n(85893),o=n(50065);let r={slug:"2018/09/isomorphic-apps",title:"Isomorphic desktop apps with Rust",date:new Date("2018-09-15T12:00:00.000Z"),authors:["bspeice"],tags:[]},a="Setting the Stage",h={authorsImageUrls:[void 0]},c=[];function l(e){let t={a:"a",blockquote:"blockquote",code:"code",em:"em",h1:"h1",img:"img",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,o.a)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsxs)(t.p,{children:["I both despise Javascript and am stunned by its success doing some really cool things. It's\n",(0,i.jsx)(t.a,{href:"https://www.destroyallsoftware.com/talks/the-birth-and-death-of-javascript",children:"this duality"})," that's\nled me to a couple of (very) late nights over the past weeks trying to reconcile myself as I\nbootstrap a simple desktop application."]}),"\n",(0,i.jsxs)(t.p,{children:["See, as much as\n",(0,i.jsx)(t.a,{href:"https://webassembly.org/docs/faq/#is-webassembly-trying-to-replace-javascript",children:"Webassembly isn't trying to replace Javascript"}),",\n",(0,i.jsx)(t.strong,{children:"I want Javascript gone"}),". There are plenty of people who don't share my views, and they are\nprobably nicer and more fun at parties. But I cringe every time \"Webpack\" is mentioned, and I think\nit's hilarious that the\n",(0,i.jsx)(t.a,{href:"https://ecma-international.org/publications/standards/Ecma-402.htm",children:"language specification"}),"\ndramatically outpaces anyone's\n",(0,i.jsx)(t.a,{href:"https://kangax.github.io/compat-table/es2016plus/",children:"actual implementation"}),". The answer to this\nconundrum is of course to recompile code from newer versions of the language to older versions ",(0,i.jsx)(t.em,{children:"of\nthe same language"})," before running. At least ",(0,i.jsx)(t.a,{href:"https://babeljs.io/",children:"Babel"})," is a nice tongue-in-cheek reference."]}),"\n",(0,i.jsxs)(t.p,{children:["Yet for as much hate as ",(0,i.jsx)(t.a,{href:"https://electronjs.org/",children:"Electron"})," receives, it does a stunningly good job at solving a really hard\nproblem: ",(0,i.jsx)(t.em,{children:"how the hell do I put a button on the screen and react when the user clicks it"}),"? GUI\nprogramming is hard, straight up. But if browsers are already able to run everywhere, why don't we\ntake advantage of someone else solving the hard problems for us? I don't like that I have to use\nJavascript for it, but I really don't feel inclined to whip out good ol' ",(0,i.jsx)(t.a,{href:"https://wxwidgets.org/",children:"wxWidgets"}),"."]}),"\n",(0,i.jsxs)(t.p,{children:["Now there are other native solutions (",(0,i.jsx)(t.a,{href:"https://github.com/LeoTindall/libui-rs/",children:"libui-rs"}),", ",(0,i.jsx)(t.a,{href:"https://github.com/PistonDevelopers/conrod",children:"conrod"}),", ",(0,i.jsx)(t.a,{href:"https://github.com/kenz-gelsoft/wxRust",children:"oh hey wxWdidgets again!"}),"), but\nthose also have their own issues with distribution, styling, etc. With Electron, I can\n",(0,i.jsx)(t.code,{children:"yarn create electron-app my-app"})," and just get going, knowing that packaging/upgrades/etc. are built\nin."]}),"\n",(0,i.jsxs)(t.p,{children:["My question is: given recent innovations with WASM, ",(0,i.jsx)(t.em,{children:"are we Electron yet"}),"?"]}),"\n",(0,i.jsx)(t.p,{children:"No, not really."}),"\n",(0,i.jsxs)(t.p,{children:["Instead, ",(0,i.jsx)(t.strong,{children:"what would it take to get to a point where we can skip Javascript in Electron apps?"})]}),"\n",(0,i.jsx)(t.p,{children:"Truth is, WASM/Webassembly is a pretty new technology and I'm a total beginner in this area. There\nmay already be solutions to the issues I discuss, but I'm totally unaware of them, so I'm going to\ntry and organize what I did manage to discover."}),"\n",(0,i.jsxs)(t.p,{children:["I should also mention that the content and things I'm talking about here are not intended to be\nprescriptive, but more \"if someone else is interested, what do we already know doesn't work?\" ",(0,i.jsx)(t.em,{children:"I\nexpect everything in this post to be obsolete within two months."})," Even over the course of writing\nthis, ",(0,i.jsx)(t.a,{href:"https://mnt.io/2018/08/28/from-rust-to-beyond-the-asm-js-galaxy/",children:"a separate blog post"})," had\nto be modified because ",(0,i.jsx)(t.a,{href:"https://github.com/WebAssembly/binaryen/pull/1642",children:"upstream changes"})," broke a\n",(0,i.jsx)(t.a,{href:"https://github.com/rustwasm/wasm-bindgen/pull/787",children:"Rust tool"})," the post tried to use. The post\nultimately\n",(0,i.jsx)(t.a,{href:"https://mnt.io/2018/08/28/from-rust-to-beyond-the-asm-js-galaxy/#comment-477",children:"got updated"}),", ",(0,i.jsx)(t.strong,{children:"but\nall this happened within the span of a week."})," Things are moving quickly."]}),"\n",(0,i.jsxs)(t.p,{children:["I'll also note that we're going to skip ",(0,i.jsx)(t.a,{href:"http://asmjs.org/",children:"asm.js"})," and ",(0,i.jsx)(t.a,{href:"https://kripken.github.io/emscripten-site/",children:"emscripten"}),". Truth be told, I couldn't get\neither of these to output anything, and so I'm just going to say\n",(0,i.jsx)(t.a,{href:"https://en.wikipedia.org/wiki/Here_be_dragons",children:"here be dragons."})," Everything I'm discussing here\nuses the ",(0,i.jsx)(t.code,{children:"wasm32-unknown-unknown"})," target."]}),"\n",(0,i.jsxs)(t.p,{children:["The code that I ",(0,i.jsx)(t.em,{children:"did"})," get running is available\n",(0,i.jsx)(t.a,{href:"https://github.com/speice-io/isomorphic-rust",children:"over here"}),". Feel free to use it as a starting point,\nbut I'm mostly including the link as a reference for the things that were attempted."]}),"\n",(0,i.jsx)(t.h1,{id:"an-example-running-application",children:"An Example Running Application"}),"\n",(0,i.jsxs)(t.p,{children:["So, I did ",(0,i.jsx)(t.em,{children:"technically"})," get a running application:"]}),"\n",(0,i.jsx)(t.p,{children:(0,i.jsx)(t.img,{alt:"Electron app using WASM",src:n(8312).Z+"",width:"800",height:"319"})}),"\n",(0,i.jsx)(t.p,{children:"...which you can also try out if you want:"}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-sh",children:"git clone https://github.com/speice-io/isomorphic-rust.git\ncd isomorphic_rust/percy\nyarn install && yarn start\n"})}),"\n",(0,i.jsx)(t.p,{children:"...but I wouldn't really call it a \"high quality\" starting point to base future work on. It's mostly\nthere to prove this is possible in the first place. And that's something to be proud of! There's a\nhuge amount of engineering that went into showing a window with the text \"It's alive!\"."}),"\n",(0,i.jsx)(t.p,{children:"There's also a lot of usability issues that prevent me from recommending anyone try Electron and\nWASM apps at the moment, and I think that's the more important thing to discuss."}),"\n",(0,i.jsx)(t.h1,{id:"issue-the-first-complicated-toolchains",children:"Issue the First: Complicated Toolchains"}),"\n",(0,i.jsxs)(t.p,{children:["I quickly established that ",(0,i.jsx)(t.a,{href:"https://github.com/rustwasm/wasm-bindgen",children:"wasm-bindgen"}),' was necessary to "link" my Rust code to Javascript. At\nthat point you\'ve got an Electron app that starts an HTML page which ultimately fetches your WASM\nblob. To keep things simple, the goal was to package everything using ',(0,i.jsx)(t.a,{href:"https://webpack.js.org/",children:"webpack"})," so that I could just\nload a ",(0,i.jsx)(t.code,{children:"bundle.js"})," file on the page. That decision was to be the last thing that kinda worked in\nthis process."]}),"\n",(0,i.jsxs)(t.p,{children:["The first issue\n",(0,i.jsx)(t.a,{href:"https://www.reddit.com/r/rust/comments/98lpun/unable_to_load_wasm_for_electron_application/",children:"I ran into"}),"\nwhile attempting to bundle everything via ",(0,i.jsx)(t.code,{children:"webpack"})," is a detail in the WASM spec:"]}),"\n",(0,i.jsxs)(t.blockquote,{children:["\n",(0,i.jsxs)(t.p,{children:["This function accepts a Response object, or a promise for one, and ... ",(0,i.jsxs)(t.strong,{children:["[if > it] does not match\nthe ",(0,i.jsx)(t.code,{children:"application/wasm"})," MIME type"]}),", the returned promise will be rejected with a TypeError;"]}),"\n",(0,i.jsx)(t.p,{children:(0,i.jsx)(t.a,{href:"https://webassembly.org/docs/web/#additional-web-embedding-api",children:"WebAssembly - Additional Web Embedding API"})}),"\n"]}),"\n",(0,i.jsxs)(t.p,{children:["Specifically, if you try and load a WASM blob without the MIME type set, you'll get an error. On the\nweb this isn't a huge issue, as the server can set MIME types when delivering the blob. With\nElectron, you're resolving things with a ",(0,i.jsx)(t.code,{children:"file://"})," URL and thus can't control the MIME type:"]}),"\n",(0,i.jsx)(t.p,{children:(0,i.jsx)(t.img,{alt:"TypeError: Incorrect response MIME type. Expected 'application/wasm'.",src:n(363).Z+"",width:"795",height:"301"})}),"\n",(0,i.jsx)(t.p,{children:"There are a couple of solutions depending on how far into the deep end you care to venture:"}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsx)(t.li,{children:"Embed a static file server in your Electron application"}),"\n",(0,i.jsxs)(t.li,{children:["Use a ",(0,i.jsx)(t.a,{href:"https://electronjs.org/docs/api/protocol",children:"custom protocol"})," and custom protocol handler"]}),"\n",(0,i.jsx)(t.li,{children:"Host your WASM blob on a website that you resolve at runtime"}),"\n"]}),"\n",(0,i.jsxs)(t.p,{children:["But all these are pretty bad solutions and defeat the purpose of using WASM in the first place.\nInstead, my workaround was to\n",(0,i.jsxs)(t.a,{href:"https://github.com/webpack/webpack/issues/7918",children:["open a PR with ",(0,i.jsx)(t.code,{children:"webpack"})]})," and use regex to remove\ncalls to ",(0,i.jsx)(t.code,{children:"instantiateStreaming"})," in the\n",(0,i.jsx)(t.a,{href:"https://github.com/speice-io/isomorphic-rust/blob/master/percy/build.sh#L21-L25",children:"build script"}),":"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-sh",children:'cargo +nightly build --target=wasm32-unknown-unknown && \\\n wasm-bindgen "$WASM_DIR/debug/$WASM_NAME.wasm" --out-dir "$APP_DIR" --no-typescript && \\\n # Have to use --mode=development so we can patch out the call to instantiateStreaming\n "$DIR/node_modules/webpack-cli/bin/cli.js" --mode=development "$APP_DIR/app_loader.js" -o "$APP_DIR/bundle.js" && \\\n sed -i \'s/.*instantiateStreaming.*//g\' "$APP_DIR/bundle.js"\n'})}),"\n",(0,i.jsxs)(t.p,{children:["Once that lands, the\n",(0,i.jsx)(t.a,{href:"https://github.com/speice-io/isomorphic-rust/blob/master/percy_patched_webpack/build.sh#L24-L27",children:"build process"}),"\nbecomes much simpler:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-sh",children:'\ncargo +nightly build --target=wasm32-unknown-unknown && \\\n wasm-bindgen "$WASM_DIR/debug/$WASM_NAME.wasm" --out-dir "$APP_DIR" --no-typescript && \\\n "$DIR/node_modules/webpack-cli/bin/cli.js" --mode=production "$APP_DIR/app_loader.js" -o "$APP_DIR/bundle.js"\n'})}),"\n",(0,i.jsxs)(t.p,{children:["But we're not done yet! After we compile Rust into WASM and link WASM to Javascript (via\n",(0,i.jsx)(t.code,{children:"wasm-bindgen"})," and ",(0,i.jsx)(t.code,{children:"webpack"}),"), we still have to make an Electron app. For this purpose I used a\nstarter app from ",(0,i.jsx)(t.a,{href:"https://electronforge.io/",children:"Electron Forge"}),", and then a\n",(0,i.jsxs)(t.a,{href:"https://github.com/speice-io/isomorphic-rust/blob/master/percy/package.json#L8",children:[(0,i.jsx)(t.code,{children:"prestart"})," script"]}),"\nto actually handle starting the application."]}),"\n",(0,i.jsxs)(t.p,{children:["The\n",(0,i.jsx)(t.a,{href:"https://github.com/speice-io/isomorphic-rust/blob/master/percy/package.json#L8",children:"final toolchain"}),"\nlooks something like this:"]}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.code,{children:"yarn start"})," triggers the ",(0,i.jsx)(t.code,{children:"prestart"})," script"]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.code,{children:"prestart"})," checks for missing tools (",(0,i.jsx)(t.code,{children:"wasm-bindgen-cli"}),", etc.) and then:","\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsxs)(t.li,{children:["Uses ",(0,i.jsx)(t.code,{children:"cargo"})," to compile the Rust code into WASM"]}),"\n",(0,i.jsxs)(t.li,{children:["Uses ",(0,i.jsx)(t.code,{children:"wasm-bindgen"})," to link the WASM blob into a Javascript file with exported symbols"]}),"\n",(0,i.jsxs)(t.li,{children:["Uses ",(0,i.jsx)(t.code,{children:"webpack"})," to bundle the page start script with the Javascript we just generated","\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsxs)(t.li,{children:["Uses ",(0,i.jsx)(t.code,{children:"babel"})," under the hood to compile the ",(0,i.jsx)(t.code,{children:"wasm-bindgen"})," code down from ES6 into something\nbrowser-compatible"]}),"\n"]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)(t.li,{children:["The ",(0,i.jsx)(t.code,{children:"start"})," script runs an Electron Forge handler to do some sanity checks"]}),"\n",(0,i.jsx)(t.li,{children:"Electron actually starts"}),"\n"]}),"\n",(0,i.jsx)(t.p,{children:'...which is complicated. I think more work needs to be done to either build a high-quality starter\napp that can manage these steps, or another tool that "just handles" the complexity of linking a\ncompiled WASM file into something the Electron browser can run.'}),"\n",(0,i.jsx)(t.h1,{id:"issue-the-second-wasm-tools-in-rust",children:"Issue the Second: WASM tools in Rust"}),"\n",(0,i.jsx)(t.p,{children:"For as much as I didn't enjoy the Javascript tooling needed to interface with Rust, the Rust-only\nbits aren't any better at the moment. I get it, a lot of projects are just starting off, and that\nleads to a fragmented ecosystem. Here's what I can recommend as a starting point:"}),"\n",(0,i.jsxs)(t.p,{children:["Don't check in your ",(0,i.jsx)(t.code,{children:"Cargo.lock"})," files to version control. If there's a disagreement between the\nversion of ",(0,i.jsx)(t.code,{children:"wasm-bindgen-cli"})," you have installed and the ",(0,i.jsx)(t.code,{children:"wasm-bindgen"})," you're compiling with in\n",(0,i.jsx)(t.code,{children:"Cargo.lock"}),", you get a nasty error:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{children:"it looks like the Rust project used to create this wasm file was linked against\na different version of wasm-bindgen than this binary:\n\nrust wasm file: 0.2.21\n this binary: 0.2.17\n\nCurrently the bindgen format is unstable enough that these two version must\nexactly match, so it's required that these two version are kept in sync by\neither updating the wasm-bindgen dependency or this binary.\n"})}),"\n",(0,i.jsxs)(t.p,{children:["Not that I ever managed to run into this myself (",(0,i.jsx)(t.em,{children:"coughs nervously"}),")."]}),"\n",(0,i.jsxs)(t.p,{children:['There are two projects attempting to be "application frameworks": ',(0,i.jsx)(t.a,{href:"https://chinedufn.github.io/percy/",children:"percy"})," and ",(0,i.jsx)(t.a,{href:"https://github.com/DenisKolodin/yew",children:"yew"}),". Between those,\nI managed to get ",(0,i.jsx)(t.a,{href:"https://github.com/speice-io/isomorphic-rust/tree/master/percy",children:"two"}),"\n",(0,i.jsx)(t.a,{href:"https://github.com/speice-io/isomorphic-rust/tree/master/percy_patched_webpack",children:"examples"})," running\nusing ",(0,i.jsx)(t.code,{children:"percy"}),", but was unable to get an\n",(0,i.jsx)(t.a,{href:"https://github.com/speice-io/isomorphic-rust/tree/master/yew",children:"example"})," running with ",(0,i.jsx)(t.code,{children:"yew"}),' because\nof issues with "missing modules" during the ',(0,i.jsx)(t.code,{children:"webpack"})," step:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-sh",children:"ERROR in ./dist/electron_yew_wasm_bg.wasm\nModule not found: Error: Can't resolve 'env' in '/home/bspeice/Development/isomorphic_rust/yew/dist'\n @ ./dist/electron_yew_wasm_bg.wasm\n @ ./dist/electron_yew_wasm.js\n @ ./dist/app.js\n @ ./dist/app_loader.js\n"})}),"\n",(0,i.jsxs)(t.p,{children:["If you want to work with the browser APIs directly, your choices are ",(0,i.jsx)(t.a,{href:"https://crates.io/crates/percy-webapis",children:"percy-webapis"})," or ",(0,i.jsx)(t.a,{href:"https://crates.io/crates/stdweb",children:"stdweb"})," (or\neventually ",(0,i.jsx)(t.a,{href:"https://crates.io/crates/web-sys",children:"web-sys"}),"). See above for my ",(0,i.jsx)(t.code,{children:"percy"})," examples, but when I tried\n",(0,i.jsxs)(t.a,{href:"https://github.com/speice-io/isomorphic-rust/tree/master/stdweb",children:["an example with ",(0,i.jsx)(t.code,{children:"stdweb"})]}),", I was\nunable to get it running:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-sh",children:"ERROR in ./dist/stdweb_electron_bg.wasm\nModule not found: Error: Can't resolve 'env' in '/home/bspeice/Development/isomorphic_rust/stdweb/dist'\n @ ./dist/stdweb_electron_bg.wasm\n @ ./dist/stdweb_electron.js\n @ ./dist/app_loader.js\n"})}),"\n",(0,i.jsxs)(t.p,{children:["At this point I'm pretty convinced that ",(0,i.jsx)(t.code,{children:"stdweb"})," is causing issues for ",(0,i.jsx)(t.code,{children:"yew"})," as well, but can't\nprove it."]}),"\n",(0,i.jsxs)(t.p,{children:["I did also get a ",(0,i.jsx)(t.a,{href:"https://github.com/speice-io/isomorphic-rust/tree/master/minimal",children:"minimal example"}),"\nrunning that doesn't depend on any tools besides ",(0,i.jsx)(t.code,{children:"wasm-bindgen"}),'. However, it requires manually\nwriting "',(0,i.jsx)(t.code,{children:"extern C"}),'" blocks for everything you need from the browser. Es no bueno.']}),"\n",(0,i.jsxs)(t.p,{children:["Finally, from a tools and platform view, there are two up-and-coming packages that should be\nmentioned: ",(0,i.jsx)(t.a,{href:"https://crates.io/crates/js-sys",children:"js-sys"})," and ",(0,i.jsx)(t.a,{href:"https://crates.io/crates/web-sys",children:"web-sys"}),". Their purpose is to be fundamental building blocks that exposes\nthe browser's APIs to Rust. If you're interested in building an app framework from scratch, these\nshould give you the most flexibility. I didn't touch either in my research, though I expect them to\nbe essential long-term."]}),"\n",(0,i.jsx)(t.p,{children:"So there's a lot in play from the Rust side of things, and it's just going to take some time to\nfigure out what works and what doesn't."}),"\n",(0,i.jsx)(t.h1,{id:"issue-the-third-known-unknowns",children:"Issue the Third: Known Unknowns"}),"\n",(0,i.jsxs)(t.p,{children:["Alright, so after I managed to get an application started, I stopped there. It was a good deal of\neffort to chain together even a proof of concept, and at this point I'd rather learn ",(0,i.jsx)(t.a,{href:"https://www.typescriptlang.org/",children:"Typescript"}),"\nthan keep trying to maintain an incredibly brittle pipeline. Blasphemy, I know..."]}),"\n",(0,i.jsx)(t.p,{children:"The important point I want to make is that there's a lot unknown about how any of this holds up\noutside proofs of concept. Things I didn't attempt:"}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsx)(t.li,{children:"Testing"}),"\n",(0,i.jsx)(t.li,{children:"Packaging"}),"\n",(0,i.jsx)(t.li,{children:"Updates"}),"\n",(0,i.jsx)(t.li,{children:"Literally anything related to why I wanted to use Electron in the first place"}),"\n"]}),"\n",(0,i.jsx)(t.h1,{id:"what-it-would-take",children:"What it Would Take"}),"\n",(0,i.jsx)(t.p,{children:"Much as I don't like Javascript, the tools are too shaky for me to recommend mixing Electron and\nWASM at the moment. There's a lot of innovation happening, so who knows? Someone might have an\napplication in production a couple months from now. But at the moment, I'm personally going to stay\naway."}),"\n",(0,i.jsx)(t.p,{children:"Let's finish with a wishlist then - here are the things that I think need to happen before\nElectron/WASM/Rust can become a thing:"}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsxs)(t.li,{children:["Webpack still needs some updates. The necessary work is in progress, but hasn't landed yet\n(",(0,i.jsx)(t.a,{href:"https://github.com/webpack/webpack/pull/7983",children:"#7983"}),")"]}),"\n",(0,i.jsxs)(t.li,{children:["Browser API libraries (",(0,i.jsx)(t.code,{children:"web-sys"})," and ",(0,i.jsx)(t.code,{children:"stdweb"}),") need to make sure they can support running in\nElectron (see module error above)"]}),"\n",(0,i.jsxs)(t.li,{children:["Projects need to stabilize. There's talk of ",(0,i.jsx)(t.code,{children:"stdweb"})," being turned into a Rust API\n",(0,i.jsx)(t.a,{href:"https://github.com/rustwasm/team/issues/226#issuecomment-418475778",children:"on top of web-sys"}),", and percy\n",(0,i.jsx)(t.a,{href:"https://github.com/chinedufn/percy/issues/24",children:"moving to web-sys"}),", both of which are big changes"]}),"\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.code,{children:"wasm-bindgen"}),' is great, but still in the "move fast and break things" phase']}),"\n",(0,i.jsxs)(t.li,{children:['A good "boilerplate" app would dramatically simplify the start-up costs;\n',(0,i.jsx)(t.a,{href:"https://github.com/chentsulin/electron-react-boilerplate",children:"electron-react-boilerplate"})," comes to\nmind as a good project to imitate"]}),"\n",(0,i.jsx)(t.li,{children:"More blog posts/contributors! I think Electron + Rust could be cool, but I have no idea what I'm\ndoing"}),"\n"]})]})}function d(e={}){let{wrapper:t}={...(0,o.a)(),...e.components};return t?(0,i.jsx)(t,{...e,children:(0,i.jsx)(l,{...e})}):l(e)}},8312:function(e,t,n){n.d(t,{Z:function(){return s}});let s=n.p+"assets/images/electron-percy-wasm-9ccb2be15a9bed6da44486afc266bad5.png"},363:function(e,t,n){n.d(t,{Z:function(){return s}});let s=n.p+"assets/images/incorrect-MIME-type-a977835e8dcbfdb20fdda3c67ee4f76c.png"},50065:function(e,t,n){n.d(t,{Z:function(){return a},a:function(){return r}});var s=n(67294);let i={},o=s.createContext(i);function r(e){let t=s.useContext(o);return s.useMemo(function(){return"function"==typeof e?e(t):{...t,...e}},[t,e])}function a(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:r(e.components),s.createElement(o.Provider,{value:t},e.children)}},58595:function(e){e.exports=JSON.parse('{"permalink":"/2018/09/isomorphic-apps","source":"@site/blog/2018-09-15-isomorphic-apps/index.mdx","title":"Isomorphic desktop apps with Rust","description":"I both despise Javascript and am stunned by its success doing some really cool things. It\'s","date":"2018-09-15T12:00:00.000Z","tags":[],"readingTime":9.905,"hasTruncateMarker":true,"authors":[{"name":"Bradlee Speice","socials":{"github":"https://github.com/bspeice"},"key":"bspeice","page":null}],"frontMatter":{"slug":"2018/09/isomorphic-apps","title":"Isomorphic desktop apps with Rust","date":"2018-09-15T12:00:00.000Z","authors":["bspeice"],"tags":[]},"unlisted":false,"lastUpdatedAt":1731188450000,"prevItem":{"title":"A case study in heaptrack","permalink":"/2018/10/case-study-optimization"},"nextItem":{"title":"Primitives in Rust are weird (and cool)","permalink":"/2018/09/primitives-in-rust-are-weird"}}')}}]);