<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Super Code Bros]]></title><description><![CDATA[It's... Two Brothers. Coding!]]></description><link>https://supercodebros.dev/</link><image><url>https://supercodebros.dev/favicon.png</url><title>Super Code Bros</title><link>https://supercodebros.dev/</link></image><generator>Ghost 2.9</generator><lastBuildDate>Thu, 21 Jan 2021 20:26:08 GMT</lastBuildDate><atom:link href="https://supercodebros.dev/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[CodeSwing for VSCode]]></title><description><![CDATA[If you've spent any time tooling around with frontend snippets, animations or buttons, you have likely heard about CodePen.. what of CodeSwing?]]></description><link>https://supercodebros.dev/codeswing-for-vscode/</link><guid isPermaLink="false">Ghost__Post__6008a309c95e72107b4d188a</guid><category><![CDATA[Setup]]></category><category><![CDATA[VSCode]]></category><category><![CDATA[CodePen]]></category><category><![CDATA[IDE]]></category><dc:creator><![CDATA[Evan Pacholski]]></dc:creator><pubDate>Thu, 21 Jan 2021 20:18:32 GMT</pubDate><media:content url="https://adampacholski.com/supercodebros/content/images/2021/01/avpswing.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://adampacholski.com/supercodebros/content/images/2021/01/avpswing.jpg" alt="CodeSwing for VSCode"/><p>(now let me see 'ya code SWING)</p><p> I was reading a list of up-and-coming dev tools to look out for in 2021, and came across <a href="https://marketplace.visualstudio.com/items?itemName=codespaces-Contrib.codeswing">CodeSwing</a>. If you've spent any time tooling around with frontend snippets, animations or buttons, you have likely heard about <a href="https://codepen.io/trending?cursor=ZD0wJm89MCZwPTI=">CodePen</a> a playground and community for open-source sharing (and a bit of peacocking π¦) of all sorts of neat stuff, from button animations to css-only art masterpieces. </p><p>If you use <a href="https://code.visualstudio.com/">Visual Studio Code</a> as an editor-of-choice, you're in luck! So don't go all frowny..</p><figure class="kg-card kg-image-card"><img src="https://adampacholski.com/supercodebros/content/images/2021/01/image.png" class="kg-image" alt="CodeSwing for VSCode" srcset="https://adampacholski.com/supercodebros/content/images/size/w600/2021/01/image.png 600w, https://adampacholski.com/supercodebros/content/images/2021/01/image.png 900w" sizes="(min-width: 720px) 720px"/></figure><h3 id="let-s-swing-">Let's swing! </h3><h5 id="and-not-in-the-leave-your-keys-in-the-bowl-sort-of-way-">And not in the leave-your-keys-in-the-bowl sort of way.</h5><p><em>CodeSwing </em>is a Visual Studio Code extension that makes it easy to play around in your code editor without pesky tabbing back and forth to a separate window, but I am loving the ability to make them on my system and then export them to CodePen when I'm done, which is awesome! Hadn't had the pleasure yet, so let's walk through together.</p><h2 id="let-s-get-em-">Let's Get 'em!</h2><p>Here is a repeat of the above link to <a href="https://marketplace.visualstudio.com/items?itemName=codespaces-Contrib.codeswing">CodeSwing</a>, but you can find it easily in the Visual Studio Code extension marketplace tab. </p><figure class="kg-card kg-image-card"><img src="https://adampacholski.com/supercodebros/content/images/2021/01/Screen-Shot-2021-01-20-at-4.26.38-PM.png" class="kg-image" alt="CodeSwing for VSCode" srcset="https://adampacholski.com/supercodebros/content/images/size/w600/2021/01/Screen-Shot-2021-01-20-at-4.26.38-PM.png 600w, https://adampacholski.com/supercodebros/content/images/size/w1000/2021/01/Screen-Shot-2021-01-20-at-4.26.38-PM.png 1000w, https://adampacholski.com/supercodebros/content/images/size/w1600/2021/01/Screen-Shot-2021-01-20-at-4.26.38-PM.png 1600w, https://adampacholski.com/supercodebros/content/images/size/w2400/2021/01/Screen-Shot-2021-01-20-at-4.26.38-PM.png 2400w" sizes="(min-width: 720px) 720px"/></figure><p>You might be thinking, wow... only 6,508 downloads? What's wrong with this thing? The short answer is: <em>nothing. </em>It's just not that popular yet.</p><h2 id="once-you-have-it-installed-">Once you have it installed..</h2><p> You can open it up a number of ways, but the handy details section in the marketplace recommends using the vscode command line (from the bar at the top) like 'zo!</p><figure class="kg-card kg-image-card"><img src="https://adampacholski.com/supercodebros/content/images/2021/01/image-6.png" class="kg-image" alt="CodeSwing for VSCode" srcset="https://adampacholski.com/supercodebros/content/images/size/w600/2021/01/image-6.png 600w, https://adampacholski.com/supercodebros/content/images/size/w1000/2021/01/image-6.png 1000w, https://adampacholski.com/supercodebros/content/images/size/w1600/2021/01/image-6.png 1600w, https://adampacholski.com/supercodebros/content/images/2021/01/image-6.png 2000w" sizes="(min-width: 720px) 720px"/></figure><pre><code class="language-vscode command line">>CodeSwing: New Swing...</code></pre><p>If you want to save your Swing set (of files, get it?) locally, which we'll do here.</p><h3 id="options-maw-options-">options maw, options! - </h3><p>It comes pre-fabbed with several different raw templates like <strong>'Basic: HTML/CSS/JS' </strong>and <strong>'Languages: HTML w/SCSS + TypeScript'</strong></p><figure class="kg-card kg-image-card"><img src="https://adampacholski.com/supercodebros/content/images/2021/01/Screen-Shot-2021-01-20-at-4.44.26-PM.png" class="kg-image" alt="CodeSwing for VSCode" srcset="https://adampacholski.com/supercodebros/content/images/size/w600/2021/01/Screen-Shot-2021-01-20-at-4.44.26-PM.png 600w, https://adampacholski.com/supercodebros/content/images/size/w1000/2021/01/Screen-Shot-2021-01-20-at-4.44.26-PM.png 1000w, https://adampacholski.com/supercodebros/content/images/size/w1600/2021/01/Screen-Shot-2021-01-20-at-4.44.26-PM.png 1600w, https://adampacholski.com/supercodebros/content/images/2021/01/Screen-Shot-2021-01-20-at-4.44.26-PM.png 2066w" sizes="(min-width: 720px) 720px"/></figure><p>But you can also create your own templates as well! </p><figure class="kg-card kg-image-card"><img src="https://adampacholski.com/supercodebros/content/images/2021/01/Screen-Shot-2021-01-20-at-4.47.33-PM.png" class="kg-image" alt="CodeSwing for VSCode" srcset="https://adampacholski.com/supercodebros/content/images/size/w600/2021/01/Screen-Shot-2021-01-20-at-4.47.33-PM.png 600w, https://adampacholski.com/supercodebros/content/images/size/w1000/2021/01/Screen-Shot-2021-01-20-at-4.47.33-PM.png 1000w, https://adampacholski.com/supercodebros/content/images/size/w1600/2021/01/Screen-Shot-2021-01-20-at-4.47.33-PM.png 1600w, https://adampacholski.com/supercodebros/content/images/2021/01/Screen-Shot-2021-01-20-at-4.47.33-PM.png 2394w" sizes="(min-width: 720px) 720px"/></figure><p>It's honestly that simple. Selecting these options will spin up an empty playground not too far from 'zis one, </p><figure class="kg-card kg-image-card"><img src="https://adampacholski.com/supercodebros/content/images/2021/01/Screen-Shot-2021-01-20-at-4.59.06-PM.png" class="kg-image" alt="CodeSwing for VSCode" srcset="https://adampacholski.com/supercodebros/content/images/size/w600/2021/01/Screen-Shot-2021-01-20-at-4.59.06-PM.png 600w, https://adampacholski.com/supercodebros/content/images/size/w1000/2021/01/Screen-Shot-2021-01-20-at-4.59.06-PM.png 1000w, https://adampacholski.com/supercodebros/content/images/size/w1600/2021/01/Screen-Shot-2021-01-20-at-4.59.06-PM.png 1600w, https://adampacholski.com/supercodebros/content/images/size/w2400/2021/01/Screen-Shot-2021-01-20-at-4.59.06-PM.png 2400w" sizes="(min-width: 720px) 720px"/></figure><p>and you're off the minute you touch the ground. It keeps up with you just like a sandbox or codepen does, only this one allows you to save the working snips directly to your system the minute you're done, or export them if you're feeling open-sourcey - I know I am.</p><figure class="kg-card kg-image-card"><img src="https://adampacholski.com/supercodebros/content/images/2021/01/image-1.png" class="kg-image" alt="CodeSwing for VSCode" srcset="https://adampacholski.com/supercodebros/content/images/size/w600/2021/01/image-1.png 600w, https://adampacholski.com/supercodebros/content/images/2021/01/image-1.png 1000w" sizes="(min-width: 720px) 720px"/></figure><h2 id="more-options-">more options..</h2><figure class="kg-card kg-image-card"><img src="https://adampacholski.com/supercodebros/content/images/2021/01/Screen-Shot-2021-01-20-at-5.01.51-PM.png" class="kg-image" alt="CodeSwing for VSCode" srcset="https://adampacholski.com/supercodebros/content/images/size/w600/2021/01/Screen-Shot-2021-01-20-at-5.01.51-PM.png 600w, https://adampacholski.com/supercodebros/content/images/size/w1000/2021/01/Screen-Shot-2021-01-20-at-5.01.51-PM.png 1000w, https://adampacholski.com/supercodebros/content/images/size/w1600/2021/01/Screen-Shot-2021-01-20-at-5.01.51-PM.png 1600w, https://adampacholski.com/supercodebros/content/images/2021/01/Screen-Shot-2021-01-20-at-5.01.51-PM.png 1754w" sizes="(min-width: 720px) 720px"/></figure><p>The Swing-specific command bar in the upper right of your window, from left to right reads:</p><ol><li>Run Swing</li><li>Open Console</li><li>Change Layout </li><li>Add Library</li><li>Dev Tools</li><li>Split</li><li>More Options</li></ol><h3 id="when-you-re-done">When you're done</h3><p>Just run the VsCode Command </p><pre><code class="language-vscode command">>CodeSwing: Export to CodePen</code></pre><p>And Voila!</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://adampacholski.com/supercodebros/content/images/2021/01/Screen-Shot-2021-01-20-at-5.06.31-PM.png" class="kg-image" alt="CodeSwing for VSCode" srcset="https://adampacholski.com/supercodebros/content/images/size/w600/2021/01/Screen-Shot-2021-01-20-at-5.06.31-PM.png 600w, https://adampacholski.com/supercodebros/content/images/size/w1000/2021/01/Screen-Shot-2021-01-20-at-5.06.31-PM.png 1000w, https://adampacholski.com/supercodebros/content/images/size/w1600/2021/01/Screen-Shot-2021-01-20-at-5.06.31-PM.png 1600w, https://adampacholski.com/supercodebros/content/images/size/w2400/2021/01/Screen-Shot-2021-01-20-at-5.06.31-PM.png 2400w" sizes="(min-width: 720px) 720px"><figcaption>Did that just happen?</figcaption></img></figure><p>I think I found my new-years' excuse to begin making more codepens. </p><p/><p>Until Next time, </p><p>See You Space Cowboy... </p><figure class="kg-card kg-image-card"><img src="https://adampacholski.com/supercodebros/content/images/2021/01/image-3.png" class="kg-image" alt="CodeSwing for VSCode" srcset="https://adampacholski.com/supercodebros/content/images/size/w600/2021/01/image-3.png 600w, https://adampacholski.com/supercodebros/content/images/2021/01/image-3.png 720w" sizes="(min-width: 720px) 720px"/></figure>]]></content:encoded></item><item><title><![CDATA[Making a Mario Castle from scratch: Babylon.js (4.2)]]></title><description><![CDATA[Holy moly. Let's talk about how amazing GL rendering for the web can be. If you are anything like me, both you and your clients like flashing lights, pretty pictures, etc.]]></description><link>https://supercodebros.dev/babylon-js-getting-started-and-making-your-first-mario-world-castle/</link><guid isPermaLink="false">Ghost__Post__5fcff0771d8bca7a1384bbbe</guid><category><![CDATA[babylon]]></category><category><![CDATA[animation]]></category><category><![CDATA[Getting Started]]></category><category><![CDATA[GL]]></category><category><![CDATA[rendering]]></category><dc:creator><![CDATA[Evan Pacholski]]></dc:creator><pubDate>Wed, 23 Dec 2020 00:07:40 GMT</pubDate><media:content url="https://adampacholski.com/supercodebros/content/images/2020/12/coverphoto.png" medium="image"/><content:encoded><![CDATA[<img src="https://adampacholski.com/supercodebros/content/images/2020/12/coverphoto.png" alt="Making a Mario Castle from scratch: Babylon.js (4.2)"/><p>Holy moly. Let's talk about how amazing GL rendering for the web can be. If you are anything like me, both you and your clients like flashing lights, pretty pictures, etc. So if this video doesn't arch an eyebrow, feel free to skip to the next post.π€¨</p><p/><figure class="kg-card kg-embed-card"><iframe width="612" height="344" src="https://www.youtube.com/embed/vkLZMHYj8d4?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""/></figure><h2 id="where-to-begin">where to begin?</h2><p>Ok so now, if you're like me, you wonder... um... so... where on earth do I get started? </p><p>Especially since Babylon 4.2 was <em>just</em> released at the time of this post, the answer here is the same as it is with pretty much any NPM package, framework or what-have you. With the docs, laddie, with the docs.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://doc.babylonjs.com/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Home | Babylon.js Documentation</div><div class="kg-bookmark-description">The homepage of Babylon.jsβ documentation page. Start here and get to know the best 3D framework on the web.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://doc.babylonjs.com/android-icon-192x192.png" alt="Making a Mario Castle from scratch: Babylon.js (4.2)"><span class="kg-bookmark-publisher">Babylon.js Documentation</span></img></div></div><div class="kg-bookmark-thumbnail"><img src="https://doc.babylonjs.com/img/defaultImage.png" alt="Making a Mario Castle from scratch: Babylon.js (4.2)"/></div></a></figure><p>First off, I'm going to point out that their tutorials and documents section is <em>phenomenal (now)</em>. Although there have been complaints in the past about inconsistencies within the documentation, I can safely say you can make it all the way through the entry tutorials and get yourself some babyl-action on the screen with pretty much zero guff. If it's your first time using Js to render GL scenes (like me), you'll feel like an animation/game dev's Pinocchio. π¦π©π </p><!--kg-card-begin: html--><div style="with: 100%; display: grid; place-items: center"> <img src="https://i.giphy.com/gOCvwLSvGLmus.gif" alt="Making a Mario Castle from scratch: Babylon.js (4.2)"/> </div><!--kg-card-end: html--><p> You can take your 'hello world' tutorial and shove it, the entry-level docs on Babylon's site have you <em>create your 'hello world', as a scene</em> and show you how to put it into a website right away. They even recently added <a href="https://www.babylonjs.com/reactnative/">support for React native</a>, for all you mobile devs who're wondering.</p><h2 id="baby-steps">baby steps</h2><p>The docs first suggest using the <a href="https://playground.babylonjs.com/#KBS9I5">playground</a> to familiarize yourself with the turf. There is a huge amount of customization, import, export, etc. involved in the playground, and it can be daunting, but I promise it's simpler than it looks to get started. </p><p>You can even record small gifs or videos and download them right away, if you don't want to go through the fun of actually rendering your Babylon scene on a live site. </p><figure class="kg-card kg-image-card"><img src="https://adampacholski.com/supercodebros/content/images/2020/12/image-2.png" class="kg-image" alt="Making a Mario Castle from scratch: Babylon.js (4.2)" srcset="https://adampacholski.com/supercodebros/content/images/size/w600/2020/12/image-2.png 600w, https://adampacholski.com/supercodebros/content/images/2020/12/image-2.png 674w"/></figure><h2 id="seeing-is-believing">seeing is believing</h2><p>So you built yourself a 3d model of an object? Or someone did it for you already? You just want to put it on a page? They took care of that too. <a href="https://doc.babylonjs.com/extensions/babylonViewer">The Babylon Viewer</a> (a customizable prefabbed viewing module) is as easy to drop in as a CDN link. After that you can add a Babylon element with a model attribute as seen below. </p><figure class="kg-card kg-image-card"><img src="https://adampacholski.com/supercodebros/content/images/2020/12/image-3.png" class="kg-image" alt="Making a Mario Castle from scratch: Babylon.js (4.2)" srcset="https://adampacholski.com/supercodebros/content/images/size/w600/2020/12/image-3.png 600w, https://adampacholski.com/supercodebros/content/images/size/w1000/2020/12/image-3.png 1000w, https://adampacholski.com/supercodebros/content/images/2020/12/image-3.png 1261w" sizes="(min-width: 720px) 720px"/></figure><h2 id="yup-it-s-that-easy-">yup. It's that easy.</h2><figure class="kg-card kg-image-card"><img src="https://adampacholski.com/supercodebros/content/images/2020/12/image-4.png" class="kg-image" alt="Making a Mario Castle from scratch: Babylon.js (4.2)" srcset="https://adampacholski.com/supercodebros/content/images/size/w600/2020/12/image-4.png 600w, https://adampacholski.com/supercodebros/content/images/size/w1000/2020/12/image-4.png 1000w, https://adampacholski.com/supercodebros/content/images/2020/12/image-4.png 1282w" sizes="(min-width: 720px) 720px"/></figure><p>You can also install it as an NPM package, if you so choose.</p><pre><code class="language-CLI">npm install --save babylonjs-viewer</code></pre><h2 id="some-basics">some basics</h2><p>Each object to be rendered in the scene has to be first imported or created as a mesh, which is done asynchronously. That means to do any basic editing of the scene in the playground, we'll have to await the import. Say I want to move a house model 'up' the y axis, to put it on an eventual hill..</p><figure class="kg-card kg-image-card"><img src="https://adampacholski.com/supercodebros/content/images/2020/12/image-5.png" class="kg-image" alt="Making a Mario Castle from scratch: Babylon.js (4.2)" srcset="https://adampacholski.com/supercodebros/content/images/size/w600/2020/12/image-5.png 600w, https://adampacholski.com/supercodebros/content/images/size/w1000/2020/12/image-5.png 1000w, https://adampacholski.com/supercodebros/content/images/2020/12/image-5.png 1232w" sizes="(min-width: 720px) 720px"/></figure><p>See that .then chain? you guessed it. </p><p>This code first imports all the individual object meshes from the scene folder in question, then we begin the mathing! That will come later (as there are legions of helpful documents on the actual code one can use to take full advantage of Babylon).</p><p>What if, however, we don't want to use that easy-up Babylon viewer? What if we want to put it in our page the old fashioned way? Skip right to <a href="https://doc.babylonjs.com/start/chap1/first_app">here</a>.</p><figure class="kg-card kg-image-card"><img src="https://adampacholski.com/supercodebros/content/images/2020/12/image-8.png" class="kg-image" alt="Making a Mario Castle from scratch: Babylon.js (4.2)" srcset="https://adampacholski.com/supercodebros/content/images/size/w600/2020/12/image-8.png 600w, https://adampacholski.com/supercodebros/content/images/2020/12/image-8.png 923w" sizes="(min-width: 720px) 720px"/></figure><p>And if you are wondering whether touch screens are supported in the viewer... well... </p><figure class="kg-card kg-image-card"><img src="https://adampacholski.com/supercodebros/content/images/2020/12/image-9.png" class="kg-image" alt="Making a Mario Castle from scratch: Babylon.js (4.2)" srcset="https://adampacholski.com/supercodebros/content/images/size/w600/2020/12/image-9.png 600w, https://adampacholski.com/supercodebros/content/images/size/w1000/2020/12/image-9.png 1000w, https://adampacholski.com/supercodebros/content/images/2020/12/image-9.png 1287w" sizes="(min-width: 720px) 720px"/></figure><h3 id="where-to-go-next">Where to go next</h3><p>After learning the absolute basics, I would still <em>highly</em> recommend the following tutorial about <a href="https://doc.babylonjs.com/start/chap2">building a village</a>, which is of course the next chapter in the docs. I normally dive into a heinously over-scoped project for immersion value and skim the docs as I go for what I need, but for a whole new library, I would definitely say dig into those docs first. They are only <sub>slightly</sub> intimidating, but after some careful poking and prodding...</p><p/><figure class="kg-card kg-image-card"><img src="https://adampacholski.com/supercodebros/content/images/2020/12/image-12.png" class="kg-image" alt="Making a Mario Castle from scratch: Babylon.js (4.2)" srcset="https://adampacholski.com/supercodebros/content/images/size/w600/2020/12/image-12.png 600w, https://adampacholski.com/supercodebros/content/images/size/w1000/2020/12/image-12.png 1000w, https://adampacholski.com/supercodebros/content/images/size/w1600/2020/12/image-12.png 1600w, https://adampacholski.com/supercodebros/content/images/2020/12/image-12.png 1687w" sizes="(min-width: 720px) 720px"/></figure><h2 id="ok-enough-of-amateur-hour-let-s-build-some-throwback-sweetness-">Ok enough of amateur hour. Let's build some throwback sweetness.</h2><p>I started playing with laying foundations with the CreateGround() and CreateGroundFromHeightMap(), meshing some water, and ended up with a platform and a nice little lake. They also have prebuilt skyboxes for a true scene feel, or you can construct and wrap your own if you're feeling frisky! I'll make a few I thought of later, but for now let's use a preset.</p><figure class="kg-card kg-image-card"><img src="https://adampacholski.com/supercodebros/content/images/2020/12/image-13.png" class="kg-image" alt="Making a Mario Castle from scratch: Babylon.js (4.2)" srcset="https://adampacholski.com/supercodebros/content/images/size/w600/2020/12/image-13.png 600w, https://adampacholski.com/supercodebros/content/images/size/w1000/2020/12/image-13.png 1000w, https://adampacholski.com/supercodebros/content/images/size/w1600/2020/12/image-13.png 1600w, https://adampacholski.com/supercodebros/content/images/2020/12/image-13.png 1811w" sizes="(min-width: 720px) 720px"/></figure><h3 id="wait-did-i-see-lava">Wait, did I see lava? </h3><p>Holy smokes, it animates right out of the gate. <em>So fun</em> to play with. I'm super cereal. This and quite a few other bits of free candy are available in the Babylon Libraries (you can read more <a href="https://doc.babylonjs.com/toolsAndResources/assetLibraries">here</a>).</p><figure class="kg-card kg-image-card"><img src="https://adampacholski.com/supercodebros/content/images/2020/12/image-14.png" class="kg-image" alt="Making a Mario Castle from scratch: Babylon.js (4.2)" srcset="https://adampacholski.com/supercodebros/content/images/size/w600/2020/12/image-14.png 600w, https://adampacholski.com/supercodebros/content/images/size/w1000/2020/12/image-14.png 1000w, https://adampacholski.com/supercodebros/content/images/size/w1600/2020/12/image-14.png 1600w, https://adampacholski.com/supercodebros/content/images/2020/12/image-14.png 1781w" sizes="(min-width: 720px) 720px"/></figure><h3 id="let-s-get-a-little-fancier-">Let's get a little fancier...</h3><p>I liked the idea of creating my own textures, which I began with <a href="https://www.gimp.org/">GIMP</a> (my favorite open source image editor). Let's face it, without being <em>paid</em> to do graphic design, I don't have time for Adobe's ceaseless background processes, forceful system occupation, or monthly fee.</p><h3 id="it-began-with-clipart-clipart-">It began with clipart. <em>clipart.</em></h3><figure class="kg-card kg-image-card"><img src="https://adampacholski.com/supercodebros/content/images/2020/12/image-15.png" class="kg-image" alt="Making a Mario Castle from scratch: Babylon.js (4.2)" srcset="https://adampacholski.com/supercodebros/content/images/size/w600/2020/12/image-15.png 600w, https://adampacholski.com/supercodebros/content/images/2020/12/image-15.png 870w" sizes="(min-width: 720px) 720px"/></figure><p>You see, each object you render into field with Babylon can be given a material and texture, and since their playground examples used separately hosted photos (available as part of their free library of stuff-to-use), I Immediately thought of <a href="https://www.flickr.com/">Flickr</a> and <a href="https://imgur.com/">Imgur</a> for my personal textures. <strong><em><strong>Spoiler alert</strong></em> - Flickr's tendency to force the jpeg file format leads to problems if you want to skin with any transparency.</strong></p><p>The general flow of making a basic object is this:</p><ol><li>Define a material, so it exists before you use it when creating the object.</li><li>Create said object, setting it's material property to the one you just made (or any one you have previously defined within global or accessible scope).</li></ol><figure class="kg-card kg-image-card"><img src="https://adampacholski.com/supercodebros/content/images/2020/12/image-16.png" class="kg-image" alt="Making a Mario Castle from scratch: Babylon.js (4.2)" srcset="https://adampacholski.com/supercodebros/content/images/size/w600/2020/12/image-16.png 600w, https://adampacholski.com/supercodebros/content/images/size/w1000/2020/12/image-16.png 1000w, https://adampacholski.com/supercodebros/content/images/2020/12/image-16.png 1501w" sizes="(min-width: 720px) 720px"/></figure><h3 id="a-little-bit-more-of-a-challenge-">A little bit more of a challenge...</h3><p>Say you want to 'skin' an object with different faces? You can do that with a single image by designating the used portion of your 'master texture' photo with a bit of multi-dimensional array love and passing a faceUV array to your created object on construction. Think of U and V as X and Y on your texture image so Babylon knows which part goes where. (Dive deeper <a href="https://doc.babylonjs.com/divingDeeper/materials/using/texturePerBoxFace">here</a>) (Ev's example texture available <a href="https://www.flickr.com/photos/191381826@N04/">here</a>)</p><figure class="kg-card kg-image-card"><img src="https://adampacholski.com/supercodebros/content/images/2020/12/image-17.png" class="kg-image" alt="Making a Mario Castle from scratch: Babylon.js (4.2)" srcset="https://adampacholski.com/supercodebros/content/images/size/w600/2020/12/image-17.png 600w, https://adampacholski.com/supercodebros/content/images/size/w1000/2020/12/image-17.png 1000w, https://adampacholski.com/supercodebros/content/images/2020/12/image-17.png 1502w" sizes="(min-width: 720px) 720px"/></figure><p>Great! Twice more and a few more custom textures, and we have the makin's of a castle, maw.</p><figure class="kg-card kg-image-card"><img src="https://adampacholski.com/supercodebros/content/images/2020/12/image-18.png" class="kg-image" alt="Making a Mario Castle from scratch: Babylon.js (4.2)" srcset="https://adampacholski.com/supercodebros/content/images/size/w600/2020/12/image-18.png 600w, https://adampacholski.com/supercodebros/content/images/size/w1000/2020/12/image-18.png 1000w, https://adampacholski.com/supercodebros/content/images/2020/12/image-18.png 1488w" sizes="(min-width: 720px) 720px"/></figure><p>The next part was fun, making crenellations for the tops of the castle walls. </p><h4 id="wait-do-i-really-have-to-make-each-box-individually-only-slightly-">Wait. do I really have to make each box individually? Only slightly!</h4><p><em>sigh of relief</em> - we are working with JavaScript! and <em>as</em> this is JavaScript, we can do fun stuff like making a 2d array of important box-position-ish information entries and iterate over the whole thing, creating a copy of a single crenellation (that word is weird to me now) for each position set. Let's do that now. After some fiddling with positions (I am a stickler for collisions)...</p><figure class="kg-card kg-image-card"><img src="https://adampacholski.com/supercodebros/content/images/2020/12/crenellateIt.png" class="kg-image" alt="Making a Mario Castle from scratch: Babylon.js (4.2)" srcset="https://adampacholski.com/supercodebros/content/images/size/w600/2020/12/crenellateIt.png 600w, https://adampacholski.com/supercodebros/content/images/size/w1000/2020/12/crenellateIt.png 1000w, https://adampacholski.com/supercodebros/content/images/2020/12/crenellateIt.png 1558w" sizes="(min-width: 720px) 720px"/></figure><p>Neat! But that's wet and ugly. Let's refactor so there's a single 2d array per castle floor, so the headache decreases (and we dry off a bit).</p><figure class="kg-card kg-image-card"><img src="https://adampacholski.com/supercodebros/content/images/2020/12/lilRefactor.png" class="kg-image" alt="Making a Mario Castle from scratch: Babylon.js (4.2)" srcset="https://adampacholski.com/supercodebros/content/images/size/w600/2020/12/lilRefactor.png 600w, https://adampacholski.com/supercodebros/content/images/size/w1000/2020/12/lilRefactor.png 1000w, https://adampacholski.com/supercodebros/content/images/2020/12/lilRefactor.png 1376w" sizes="(min-width: 720px) 720px"/></figure><h3 id="getting-fancy-">getting fancy...</h3><p>See, you can create all SORTS of shapes, not just boxes. What about cylinders, spheres, tauroids, arcs, and slices? Hoooyeah.</p><figure class="kg-card kg-image-card"><img src="https://adampacholski.com/supercodebros/content/images/2020/12/cylinder-worldpipe.png" class="kg-image" alt="Making a Mario Castle from scratch: Babylon.js (4.2)" srcset="https://adampacholski.com/supercodebros/content/images/size/w600/2020/12/cylinder-worldpipe.png 600w, https://adampacholski.com/supercodebros/content/images/size/w1000/2020/12/cylinder-worldpipe.png 1000w, https://adampacholski.com/supercodebros/content/images/size/w1600/2020/12/cylinder-worldpipe.png 1600w, https://adampacholski.com/supercodebros/content/images/2020/12/cylinder-worldpipe.png 1798w" sizes="(min-width: 720px) 720px"/></figure><p>There's a playground for each basic shape, for camera manipulation, light setting, animation, tons of them! That means understanding how it works can happen in real time without too much debugging if you understand JS basics.</p><p>A wave of the ol' GIMP wand, and PRESTO! We have a classic world-pipe.</p><figure class="kg-card kg-image-card"><img src="https://adampacholski.com/supercodebros/content/images/2020/12/image-19.png" class="kg-image" alt="Making a Mario Castle from scratch: Babylon.js (4.2)" srcset="https://adampacholski.com/supercodebros/content/images/size/w600/2020/12/image-19.png 600w, https://adampacholski.com/supercodebros/content/images/size/w1000/2020/12/image-19.png 1000w, https://adampacholski.com/supercodebros/content/images/2020/12/image-19.png 1425w" sizes="(min-width: 720px) 720px"/></figure><p>A few more finishing touches and we'll be ready to show off. Let's deal with making sure our poor camera doesn't sink below the lava-layer and confound the viewer by showing them a bunch of empty back-rendered nonsense. There are a few ways to go about this, actually, but let's focus on the camera for now.</p><p>You get all sorts of neat tooltip info by hovering over any of the constructors or methods in the Babylon playground, including of course various arguments you can pass directly on creation of your camera to adjust it. There are also cool features like auto-rotation behavior for a neat panning effect.</p><figure class="kg-card kg-image-card"><img src="https://adampacholski.com/supercodebros/content/images/2020/12/camera-controls.png" class="kg-image" alt="Making a Mario Castle from scratch: Babylon.js (4.2)" srcset="https://adampacholski.com/supercodebros/content/images/size/w600/2020/12/camera-controls.png 600w, https://adampacholski.com/supercodebros/content/images/size/w1000/2020/12/camera-controls.png 1000w, https://adampacholski.com/supercodebros/content/images/size/w1600/2020/12/camera-controls.png 1600w, https://adampacholski.com/supercodebros/content/images/2020/12/camera-controls.png 1716w" sizes="(min-width: 720px) 720px"/></figure><h3 id="all-together-now-">all together now!</h3><p>Using the easy mode for dropping in a scene as mentioned above is great for most static pages, but as this blog has a seriously layered hosting solution I elected a <a href="https://doc.babylonjs.com/start/chap1/first_app">more thorough way</a> to build the script directly into this page project. Note that CORS can affect your successful load of the scene, but as long as you fix 'yer headers on the hosting origin, or better yet, just host your scenes wherever you plan on putting them up as additional assets, you'll be fine!</p><!--kg-card-begin: html--><script src="https://cdn.babylonjs.com/babylon.js"/> <script src="https://cdn.babylonjs.com/loaders/babylonjs.loaders.min.js"/> <script src="https://code.jquery.com/pep/0.4.3/pep.js"/> <script src="https://www.supercodebros.dev/other_assets/babylon.lava.js"/> <canvas id="renderCanvas" touch-action="none" style="width:100%; height: 80vh;"/> <!--kg-card-end: html--><p>If the above demo didn't load on your browser, or if you just want to poke around, <a href="https://playground.babylonjs.com/#KBS9I5#1155">Here's a link</a> to the playground as I finished it! If you click on the above embed, you can do all sorts of keyboard camera controls like ctrl + arrows, alt + arrows, etc, as I haven't wired the keypresses to any individual animations for this demo.</p><p><br><br>Enjoy fiddling with the camera features, light bouncing, animations, and all the other neat stuff you can do, you have now (hopefully) successfully made your first real scene!</br></br></p><p>If you liked this dive into the freshness, stay tuned! I'm setting my sights on an intro to three.js next.</p><p/><p>Onward and upward, <br>Evan out. <br/></br></p><p/><p/><p/><figure class="kg-card kg-image-card"><img src="https://adampacholski.com/supercodebros/content/images/2020/12/DisgustingPlumpAiredale-max-1mb.gif" class="kg-image" alt="Making a Mario Castle from scratch: Babylon.js (4.2)"/></figure><!--kg-card-begin: html--><script> const canvas = document.getElementById("renderCanvas"); // Get the canvas element const engine = new BABYLON.Engine(canvas, true); // Generate the BABYLON 3D engine const createScene = () => { const scene = new BABYLON.Scene(engine); /**** Set camera and light *****/ // const camera = new BABYLON.ArcRotateCamera("camera", -Math.PI / 1.9, Math.PI / 2.25, 95, new BABYLON.Vector3(15, 15, -10)); const camera = new BABYLON.ArcRotateCamera( "camera", -14.171, 1.1096, 81.4364, new BABYLON.Vector3(6.13, 14.79, -9.68) ); camera.attachControl(canvas, true); camera.useAutoRotationBehavior = true; camera.upperBetaLimit = 1.5904; camera.lowerRadiusLimit = 50.12; camera.upperRadiusLimit = 153; camera.fov = 1.12; const light = new BABYLON.HemisphericLight( "light", new BABYLON.Vector3(1, 2, 0.25) ); // materials:: var sphere = BABYLON.Mesh.CreateGround("ground", 500, 500, 190, scene); // Lava Material creation var lavaMaterial = new BABYLON.LavaMaterial("lava", scene); lavaMaterial.noiseTexture = new BABYLON.Texture( "https://raw.githubusercontent.com/BabylonJS/Babylon.js/master/Playground/textures/cloud.png", scene ); // Set the bump texture lavaMaterial.diffuseTexture = new BABYLON.Texture( "https://raw.githubusercontent.com/BabylonJS/Babylon.js/master/Playground/textures/lava/lavatile.jpg", scene ); // Set the diffuse texture lavaMaterial.speed = 0.05; lavaMaterial.fogColor = new BABYLON.Color3(0.45, 0.15, 0.15); // lavaMaterial.unlit = true; sphere.material = lavaMaterial; // Skybox meshed var skybox = BABYLON.MeshBuilder.CreateBox("skyBox", { size: 800.0 }, scene); var skyboxMaterial = new BABYLON.StandardMaterial("skybox", scene); skyboxMaterial.backFaceCulling = false; skyboxMaterial.reflectionTexture = new BABYLON.CubeTexture( "https://www.supercodebros.dev/other_assets/skybox/skybox", scene ); skyboxMaterial.reflectionTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE; skyboxMaterial.disableLighting = true; skybox.material = skyboxMaterial; // ===========================base======================================== const baseMat = new BABYLON.StandardMaterial("baseMat"); baseMat.diffuseTexture = new BABYLON.Texture( "https://live.staticflickr.com/65535/50734389556_6b5a8a7723_c.jpg", scene ); const base = BABYLON.MeshBuilder.CreateBox("base", {}); base.material = baseMat; base.position.y = 5; base.position.x = 10; base.position.z = -15; base.scaling = new BABYLON.Vector3(65, 15, 40); // ===========================base/END============================================= //=-=-=-=-=-=-=-=-=-=-=-=-=-=-world-pipe=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- const pipeMaterial = new BABYLON.StandardMaterial("pipeMat", scene); pipeMaterial.diffuseTexture = new BABYLON.Texture( "https://live.staticflickr.com/65535/50746209997_21cfac4fbf_w.jpg" ); const pipeFaceUV = []; pipeFaceUV[0] = new BABYLON.Vector4(0, 0, 0, 0); pipeFaceUV[1] = new BABYLON.Vector4(1, 0, 0.25, 1); // x, z swapped to flip image pipeFaceUV[2] = new BABYLON.Vector4(0, 0, 0.24, 1); const faceColors = []; faceColors[0] = new BABYLON.Color4(0.5, 0.5, 0.5, 1); const pipe = BABYLON.MeshBuilder.CreateCylinder("pipe", { height: 1.16, faceUV: pipeFaceUV, faceColors: faceColors, }); pipe.material = pipeMaterial; pipe.scaling = new BABYLON.Vector3(4, 4, 4); pipe.position = new BABYLON.Vector3(37, 15, -29); const pipeTop = BABYLON.MeshBuilder.CreateCylinder("pipeTop", { height: 0.5, diameter: 1.25, faceUV: pipeFaceUV, faceColors: faceColors, }); pipeTop.material = pipeMaterial; pipeTop.scaling = new BABYLON.Vector3(4, 4, 4); pipeTop.position = new BABYLON.Vector3(37, 17, -29); //=-=-=-=-=-=-=-=-=-=-=-=-=-=-world-pipe/END=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- //-------------------Question Blocks--------------------------------------------- const questMat = new BABYLON.StandardMaterial("questMat"); questMat.diffuseTexture = new BABYLON.Texture( "https://live.staticflickr.com/65535/50734493967_793dacf5e2_w.jpg", scene ); const questBlock = BABYLON.MeshBuilder.CreateBox("quest", { size: 3 }); questBlock.material = questMat; questBlock.position.x = -20; questBlock.position.y = 25; questBlock.position.z = -25; const questClone = questBlock.clone("clonedQuest"); questClone.position.x = -25; const questClone1 = questBlock.clone("clonedQuest1"); questClone1.position.x = -30; //-------------------Question Blocks--------------------------------------------- //======================CASTLE-------------------------------------------------- const firstFloorMat = new BABYLON.StandardMaterial("firstFloorMat"); firstFloorMat.diffuseTexture = new BABYLON.Texture( "https://live.staticflickr.com/65535/50734914921_1e98e703f0_c.jpg", scene ); const secondFloorMat = new BABYLON.StandardMaterial("secondFloorMat"); secondFloorMat.diffuseTexture = new BABYLON.Texture( "https://live.staticflickr.com/65535/50735021397_95bb7bf56c_c.jpg", scene ); const topFloorMat = new BABYLON.StandardMaterial("topFloorMat"); topFloorMat.diffuseTexture = new BABYLON.Texture( "https://live.staticflickr.com/65535/50734184658_47d2dbcf8e_c.jpg", scene ); const crenelMat = new BABYLON.StandardMaterial("crenelMat"); crenelMat.diffuseTexture = new BABYLON.Texture( "https://live.staticflickr.com/65535/50744830268_ff7f5ebb2c_w.jpg", scene ); castleFloorfaceUV = []; castleFloorfaceUV[0] = new BABYLON.Vector4(0.0, 0.0, 0.4, 1.0); //rear face castleFloorfaceUV[1] = new BABYLON.Vector4(0.6, 0.0, 1, 1.0); //front face castleFloorfaceUV[2] = new BABYLON.Vector4(0.4, 0, 0.6, 1.0); //right side castleFloorfaceUV[3] = new BABYLON.Vector4(0.4, 0, 0.6, 1.0); //left side castleFloorfaceUV[4] = new BABYLON.Vector4(0.4, 0, 0.6, 1.0); //top side castleFloorfaceUV[5] = new BABYLON.Vector4(0.4, 0, 0.6, 1.0); //bottom side const castleFirstFloor = BABYLON.MeshBuilder.CreateBox("firstfloor", { faceUV: castleFloorfaceUV, wrap: true, }); castleFirstFloor.material = firstFloorMat; castleFirstFloor.scaling = new BABYLON.Vector3(39, 12, 20); castleFirstFloor.position.x = 15; castleFirstFloor.position.y = 18.5; castleFirstFloor.position.z = -12; const castleSecondFloor = BABYLON.MeshBuilder.CreateBox("secondfloor", { faceUV: castleFloorfaceUV, wrap: true, }); castleSecondFloor.material = secondFloorMat; castleSecondFloor.scaling = new BABYLON.Vector3(28, 10, 13); castleSecondFloor.position.x = 15; castleSecondFloor.position.y = 28; castleSecondFloor.position.z = -12; const castleTopFloor = BABYLON.MeshBuilder.CreateBox("topfloor", { faceUV: castleFloorfaceUV, wrap: true, }); castleTopFloor.material = topFloorMat; castleTopFloor.scaling = new BABYLON.Vector3(20, 8, 8.3); castleTopFloor.position.x = 15; castleTopFloor.position.y = 37; castleTopFloor.position.z = -12; const crenel = BABYLON.MeshBuilder.CreateBox("crenel", { size: 2 }); crenel.material = crenelMat; crenel.rotation.y = -Math.PI / 2; crenel.position.x = 5.8; crenel.position.y = 42; crenel.position.z = -9; crenel.scaling = new BABYLON.Vector3(1, 1, 0.7); const places = []; //each entry is an array [rotation, x, y, z]; //top tier const topCrenels = [ [0, 10.5, 42, -8.5], [0, 15.2, 42, -8.5], [0, 20, 42, -8.5], [-Math.PI / 2, 24.2, 42, -9], [-Math.PI / 2, 5.8, 42, -15], [0, 10.5, 42, -15.5], [0, 15.2, 42, -15.5], [0, 20, 42, -15.5], [-Math.PI / 2, 24.2, 42, -15], ]; places.push(...topCrenels); //mid const secondCrenels = [ [-Math.PI / 2, 1.8, 34, -6.2], [-Math.PI / 2, 1.8, 34, -12], [-Math.PI / 2, 1.8, 34, -17.4], [-Math.PI / 2, 28.1, 34, -6.2], [-Math.PI / 2, 28.1, 34, -12], [-Math.PI / 2, 28.1, 34, -17.4], [0, 6, 34, -6.2], [0, 11, 34, -6.2], [0, 17, 34, -6.2], [0, 23, 34, -6.2], [0, 6, 34, -17.8], [0, 11, 34, -17.8], [0, 17, 34, -17.8], [0, 23, 34, -17.8], ]; places.push(...secondCrenels); //bottom const firstCrenels = [ [-Math.PI / 2, -4, 25, -3], [-Math.PI / 2, -4, 25, -8.5], [-Math.PI / 2, -4, 25, -15], [-Math.PI / 2, -4, 25, -21], [-Math.PI / 2, 33.8, 25, -3], [-Math.PI / 2, 33.8, 25, -8.5], [-Math.PI / 2, 33.8, 25, -15], [-Math.PI / 2, 33.8, 25, -21], [0, 15.7, 25, -2.7], [0, 0.6, 25, -2.7], [0, 5.5, 25, -2.7], [0, 10.3, 25, -2.7], [0, 20.8, 25, -2.7], [0, 25.4, 25, -2.7], [0, 30, 25, -2.7], [0, 15.7, 25, -21], [0, 0.6, 25, -21], [0, 5.5, 25, -21], [0, 10.3, 25, -21], [0, 20.8, 25, -21], [0, 25.4, 25, -21], [0, 30, 25, -21], ]; places.push(...firstCrenels); const crenellations = []; for (let i = 0; i < places.length; i++) { crenellations[i] = crenel.createInstance("crenellate" + i); crenellations[i].rotation.y = places[i][0]; crenellations[i].position.x = places[i][1]; crenellations[i].position.y = places[i][2]; crenellations[i].position.z = places[i][3]; } //======================CASTLE/end-------------------------------------------------- //--------------------------------------FlagpoleGoal----------------------------------- const levelMat = new BABYLON.StandardMaterial("levelMat"); levelMat.diffuseTexture = new BABYLON.Texture( "https://live.staticflickr.com/65535/50746063547_f83d319baf_w.jpg", scene ); const poleMat = new BABYLON.StandardMaterial("poleMat"); poleMat.diffuseColor = new BABYLON.Color3(0.8, 0.8, 1); const flagBase = new BABYLON.MeshBuilder.CreateBox("flagBase", scene); flagBase.material = levelMat; flagBase.scaling = new BABYLON.Vector3(3, 3, 3); flagBase.position = new BABYLON.Vector3(-10.5, 14, -21); const flagPole = new BABYLON.MeshBuilder.CreateCylinder( "flagPole", { height: 22, diameter: 0.75 }, scene ); flagPole.material = poleMat; flagPole.position = new BABYLON.Vector3(-10.5, 25, -21); const flagTop = new BABYLON.MeshBuilder.CreateSphere( "flagPoleTop", { diameter: 1.5 }, scene ); const flagTopMat = new BABYLON.StandardMaterial("flagTopMat"); flagTopMat.diffuseColor = new BABYLON.Color3(0.06, 0.59, 0.06); flagTop.material = flagTopMat; flagTop.position = new BABYLON.Vector3(-10.5, 36.5, -21); flagFaceUV = []; flagFaceUV[0] = new BABYLON.Vector4(0.5, 0, 1, 1); //front flagFaceUV[1] = new BABYLON.Vector4(0, 0, 0.5, 1); //rear const victoryFlag = new BABYLON.MeshBuilder.CreateBox( "victoryFlag", { height: 3, width: 3, faceUV: flagFaceUV, depth: 0.00000001 }, scene ); const victoryFlagMat = new BABYLON.StandardMaterial("victoryFlagMat"); victoryFlagMat.diffuseTexture = new BABYLON.Texture("https://www.supercodebros.dev/other_assets/flag-babylon.png"); victoryFlagMat.diffuseTexture.hasAlpha = true; victoryFlagMat.backFaceCulling = true; victoryFlag.material = victoryFlagMat; victoryFlag.position = new BABYLON.Vector3(-12.25, 34, -21); //--------------------------------------FlagpoleGoal/END----------------------------------- return scene; }; const scene = createScene(); //Call the createScene function // Register a render loop to repeatedly render the scene engine.runRenderLoop(function () { scene.render(); }); // Watch for browser/canvas resize events window.addEventListener("resize", function () { engine.resize(); }); </script><!--kg-card-end: html-->]]></content:encoded></item><item><title><![CDATA[Cannot use JSX unless the '--jsx' flag is provided]]></title><description><![CDATA[Typescript errors can be maddening.. but sometimes the error is not where you think it is. ]]></description><link>https://supercodebros.dev/typescript-react-jsx-flag/</link><guid isPermaLink="false">Ghost__Post__5fbe84dc1d8bca7a1384b8de</guid><category><![CDATA[react]]></category><category><![CDATA[typescript]]></category><category><![CDATA[environment]]></category><dc:creator><![CDATA[Evan Pacholski]]></dc:creator><pubDate>Fri, 27 Nov 2020 05:50:17 GMT</pubDate><media:content url="https://adampacholski.com/supercodebros/content/images/2020/11/4o0jnr-1.jpg" medium="image"/><content:encoded><![CDATA[<h2 id="argument-for-jsx-option-must-be-preserve-react-native-react-ts">Argument for '--jsx' option must be: 'preserve', 'react-native', 'react'. ts</h2><img src="https://adampacholski.com/supercodebros/content/images/2020/11/4o0jnr-1.jpg" alt="Cannot use JSX unless the '--jsx' flag is provided"/><p>Typescript errors can be maddening, especially if you use it less frequently, and when you clone a brand new repo after onboarding on a project and get cracking! </p><p>These two particularly flippant responses, paired, have affected quite a few developers working with typescript and create-react-app or react in general, and I ran into it myself while beginning a bit of work on a volunteer project for an <a href="https://www.orcasound.net/">awesome cause</a> recently. With a little help from <a href="https://www.supercodebros.dev/author/adam/">Adam</a>, I tracked down the culprit and found several other common issues along the way. Let's get cracking!</p><h2 id="the-problem-">The problem:</h2><figure class="kg-card kg-image-card"><img src="https://adampacholski.com/supercodebros/content/images/2020/11/tserror17004.png" class="kg-image" alt="Cannot use JSX unless the '--jsx' flag is provided" srcset="https://adampacholski.com/supercodebros/content/images/size/w600/2020/11/tserror17004.png 600w, https://adampacholski.com/supercodebros/content/images/2020/11/tserror17004.png 960w" sizes="(min-width: 720px) 720px"/></figure><p>Upon starting the development server for the project, I had freshly cloned and installed dependencies, and was hit with the below errors. Odd, since I knew it had just worked for one or both of the primary project developers. </p><!--kg-card-begin: html--><div style="width: 100%;"> <h4 style="color: red; text-align: center;">Must be a 'me' problem.</h4> </div><!--kg-card-end: html--><hr><h2 id="detective-work">Detective work</h2><p>First, I checked my package.json for any conflicts between my personal versions and the project's versions (pictured 1st). All good there. βοΈ</p><figure class="kg-card kg-gallery-card kg-width-wide"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://adampacholski.com/supercodebros/content/images/2020/11/tserrorpackage.json-1.png" width="743" height="662" alt="Cannot use JSX unless the '--jsx' flag is provided" srcset="https://adampacholski.com/supercodebros/content/images/size/w600/2020/11/tserrorpackage.json-1.png 600w, https://adampacholski.com/supercodebros/content/images/2020/11/tserrorpackage.json-1.png 743w" sizes="(min-width: 720px) 720px"/></div><div class="kg-gallery-image"><img src="https://adampacholski.com/supercodebros/content/images/2020/11/typeerror.png" width="592" height="226" alt="Cannot use JSX unless the '--jsx' flag is provided"/></div><div class="kg-gallery-image"><img src="https://adampacholski.com/supercodebros/content/images/2020/11/markererror-1.png" width="350" height="350" alt="Cannot use JSX unless the '--jsx' flag is provided"/></div></div></div></figure><p> It looks like, in my case, typescript and create-react-app were fighting over what to set the 'jsx' flag to in my tsconfig.json. Typescript wanted one of the three options from the bottom, but.... <strong>what's this?</strong> On spinning up the dev server, <em><strong>React v 17</strong></em> <em>forced the new 'react-jsx' flag syntax to even load the page. </em> </p><figure class="kg-card kg-image-card"><img src="https://adampacholski.com/supercodebros/content/images/2020/11/reactrewrite.png" class="kg-image" alt="Cannot use JSX unless the '--jsx' flag is provided" srcset="https://adampacholski.com/supercodebros/content/images/size/w600/2020/11/reactrewrite.png 600w, https://adampacholski.com/supercodebros/content/images/2020/11/reactrewrite.png 940w" sizes="(min-width: 720px) 720px"/></figure><p>Most of the solutions on stack overflow mention changing the flag to the older 'react'. It made the problems in individual tsx files (3rd) 'go away', but trying to force a load after manually saving the flag as 'react' gives a dreaded type error (2nd). Also, bad form. If you want to configure your CRA, just eject, handle your babel and webpack, and be done with it I say.</p><p>If I let CRA dictate the flag, the page loaded fine, but then when I went back into vscode, oh no! There were errors in my .tsx files again! </p><p><a href="https://stackoverflow.com/search?q=Cannot+use+JSX+unless+the+%27--jsx%27+flag+is+provided">A brief search of Stack Overflow</a> will show that this is a common(ish) problem dating back a few years, in multiple environments, with several solutions. Better get my meerschaum pipe and a big magnifying glass. ππ¬π΅οΈββοΈ</p><hr><h2 id="the-process">The Process</h2><p>Especially when I am hit with an error on a project I join mid-stride, I like to make sure I can reproduce the error locally a few times and weed out installation issues. A few outside considerations:</p><h4 id="was-the-config-of-the-individual-project-so-fine-tuned-to-the-original-dev-s-local-environment-that-it-caused-errors">was the config of the individual project so fine-tuned to the original dev's local environment that it caused errors? </h4><p>Was a husky pre-compile hook with an eslint quiet write and a possibly eclectic eslint/prettier config setup causing all the fluff? Disabled. Nope.</p><!--kg-card-begin: html--><div style="width: 100%;"> <h6 style="color: red; text-align: center;">Same errors</h6> </div><!--kg-card-end: html--><p> </p><h4 id="what-if-the-project-was-set-up-strangely-from-the-get">what if the project was set up strangely from the get? </h4><p>It's unlikely, we're talking seasoned pros, but okay, let's play ball. π</p><p>I briefly installed two skeletal versions of the project:</p><ol><li>One installing a create-react-app with a typescript template, <a href="https://create-react-app.dev/docs/adding-typescript/#installation">as is recommended here</a>.</li><li>Another with just "npx create-react-app APP_NAME" and adding ts afterword with a separate installation, their next suggestion.</li></ol><!--kg-card-begin: html--><div style="width: 100%;"> <h6 style="color: red; text-align: center;">Same errors</h6> </div><!--kg-card-end: html--><h6/><h4 id="what-if-i-was-using-an-older-cached-version-of-create-react-app">what if I was using an older cached version of create-react-app?</h4><ol><li>Mentioned in create-react-app's docs <a href="https://create-react-app.dev/docs/adding-typescript/#troubleshooting">in the very next section</a>, I uninstalled any possible old global version of CRA. nope.</li></ol><!--kg-card-begin: html--><div style="width: 100%;"> <h6 style="color: red; text-align: center;">Same errors</h6> </div><!--kg-card-end: html--><h2 id="the-solution">The Solution</h2><p>At this point, rather than trying all the stack overflow answers about miniscule changes to the config files... (after all, this is a shared project with less available configurations, and I am volunteering, I can't just change their config and dependencies to suit my needs), I asked a more senior dev.</p><p> Adam recommended sifting through my versions and settings one more time. We then looked through the SO answers for <a href="https://stackoverflow.com/questions/50432556/cannot-use-jsx-unless-the-jsx-flag-is-provided">something that mentioned versioning,</a> just to be sure, and right beneath the highest voted (and verified) answer on the above linked post was my π₯golden ticket.ποΈ</p><figure class="kg-card kg-image-card"><img src="https://adampacholski.com/supercodebros/content/images/2020/11/image-1.png" class="kg-image" alt="Cannot use JSX unless the '--jsx' flag is provided" srcset="https://adampacholski.com/supercodebros/content/images/size/w600/2020/11/image-1.png 600w, https://adampacholski.com/supercodebros/content/images/2020/11/image-1.png 888w" sizes="(min-width: 720px) 720px"/></figure><p>I like to keep the bottom bar of my vscode window clear except the 'version control' option, and enabling ALL of the options led me to this...</p><figure class="kg-card kg-image-card"><img src="https://adampacholski.com/supercodebros/content/images/2020/11/whut.jpg" class="kg-image" alt="Cannot use JSX unless the '--jsx' flag is provided" srcset="https://adampacholski.com/supercodebros/content/images/size/w600/2020/11/whut.jpg 600w, https://adampacholski.com/supercodebros/content/images/2020/11/whut.jpg 872w" sizes="(min-width: 720px) 720px"/></figure><h3 id="wat-wat">wat? WAT?</h3><p>My vscode was using typescript 4.0.3 <em>this whole timeβ </em></p><p>Is yours? </p><figure class="kg-card kg-image-card"><img src="https://adampacholski.com/supercodebros/content/images/2020/11/tsversion.png" class="kg-image" alt="Cannot use JSX unless the '--jsx' flag is provided" srcset="https://adampacholski.com/supercodebros/content/images/size/w600/2020/11/tsversion.png 600w, https://adampacholski.com/supercodebros/content/images/size/w1000/2020/11/tsversion.png 1000w, https://adampacholski.com/supercodebros/content/images/2020/11/tsversion.png 1177w" sizes="(min-width: 720px) 720px"/></figure><p>After reinstalling ts 4.1.2 globally and selecting the updated workspace version.. </p><!--kg-card-begin: html--><div style="width: 100%;"> <h5 style="color: green; text-align: center;">Great Success β </h5> </div><!--kg-card-end: html--><h2 id="the-take-home">The take-home</h2><p>Turns out: vscode uses TypeScript 4.0.3 out of the box, comes with it. <em>It exists in vscode's own node modules.</em></p><p>Notice how I didn't even mention my editor of choice, vscode, until a fair bit into the post. It's honestly because I <em>didn't think about it</em>. We all get comfortable with our code manipulation environments, so much so that we can <em>fail to see the total scope of possible issues at first</em>.</p><p>Live, learn, and stay humble.</p><p>Onward and upward, Evan out </p><figure class="kg-card kg-image-card"><img src="https://adampacholski.com/supercodebros/content/images/2020/11/three-hearts-2.png" class="kg-image" alt="Cannot use JSX unless the '--jsx' flag is provided"/></figure></hr></hr>]]></content:encoded></item><item><title><![CDATA[Signing up for your first Hackathon]]></title><description><![CDATA[My experience & some things to think about as you look for your first hackathon.]]></description><link>https://supercodebros.dev/first-hackathon-expectations/</link><guid isPermaLink="false">Ghost__Post__5fb9404f1d8bca7a1384b5bb</guid><category><![CDATA[Hackathon]]></category><category><![CDATA[Getting Started]]></category><dc:creator><![CDATA[Evan Pacholski]]></dc:creator><pubDate>Mon, 23 Nov 2020 17:40:26 GMT</pubDate><media:content url="https://adampacholski.com/supercodebros/content/images/2020/11/hacktime.png" medium="image"/><content:encoded><![CDATA[<h3 id="-and-why-you-don-t-need-to-be-as-nervous-as-i-was-">(And why you don't need to be as nervous as I was)</h3><img src="https://adampacholski.com/supercodebros/content/images/2020/11/hacktime.png" alt="Signing up for your first Hackathon"/><p/><p>So this past Saturday I attended the <a href="https://www.democracylab.org/index/?section=AboutEvent&id=8">Hack to Give Thanks</a> put on by <a href="https://www.democracylab.org/index/?section=Home">Democracy Lab</a> and some heavy-hitting sponsors like Amazon and Microsoft. The <a href="https://allofus.nih.gov/">NIH's All of us program</a> provided a nifty lunch and learn, check them out! They are aggregating national health data in a way that will help EVERYONE get better health care.</p><p>Here are some things to think about as you look for your first opportunity to take part in what is generally considered to be a <em>really</em> good step in bettering your exposure as a tech applicant.</p><h2 id="what-kind-of-hackathon-is-it">What kind of 'hackathon' is it?</h2><p>Many people aren't aware of this (I certainly wasn't), but 'hackathons' come in all shapes and sizes. The traditional structure involves a target goal (or several), and a ground-up competition with a take-home prize. </p><!--kg-card-begin: html--><div style="display: 'grid'; place-items: 'center'"> <img src="https://media1.giphy.com/media/5t3POlVgm29ZiERRXT/giphy.gif" alt="Signing up for your first Hackathon" height="250px" width="250px" style="margin: '0 auto'"/> </div> <!--kg-card-end: html--><p>These can be tremendously nerve-wracking for a first-timer, as depending on your cohort the pressure to perform and knock out a 'winner' can be extensive.</p><p>Others however, involve making incremental progress on pre-existing projects. </p><p> Hack To Give Thanks was one of the latter, and there were a host of awesome civic-minded groups ranging from <a href="https://www.democracylab.org/index/?section=AboutProject&id=500">law enforcement accountability</a> to <a href="https://www.democracylab.org/index/?section=AboutProject&id=528">resource distribution and sharing for the wild-fire displaced</a> and citizen-scientist observation-collaboration <a href="https://www.democracylab.org/index/?section=AboutProject&id=81">for the Puget Sound area's southern resident Killer Whales</a>.</p><h2 id="the-experience">The experience</h2><p>OK, so we're going for it! Now what? Let's look at the checklist.</p><ol><li> Signed up for it through <a href="https://www.eventbrite.com/e/hack-to-give-thanks-2020-tickets-120636676547">Eventbrite</a>.</li><li>Read through the available projects for volunteer at Democracy Lab's event page.</li><li>Picked a project. If you don't know me yet, I'm pretty heavily into species conservation, revamping supply chains, basically anything to do with giving the world and humanity at large a leg-up over its own hubris in the past century and a half... so you can probably guess that I picked</li></ol><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://www.orcasound.net/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">OrcaSound</div><div class="kg-bookmark-description">Listen for whales</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://i1.wp.com/www.orcasound.net/wp2017/wp-content/uploads/2018/09/cropped-orcasound-fav-icon-black.png?fit=192%2C192&ssl=1" alt="Signing up for your first Hackathon"><span class="kg-bookmark-author">Scott Veirs</span><span class="kg-bookmark-publisher">OrcaSound</span></img></div></div><div class="kg-bookmark-thumbnail"><img src="https://www.orcasound.net/wp2017/wp-content/uploads/2020/10/Screen-Shot-2020-10-20-at-1.40.28-PM.png" alt="Signing up for your first Hackathon"/></div></a></figure><p>4. Showed up. No seriously, this deserves to be its own step. The very first time you attend a large gathering of strangers intent on functionally unifying for a day, it's hard to know what to expect. Especially when all our social skills have been pandemically down-graded.</p><p>5. Relaxed. This one is extremely important! I can't stress it enough. Remember that you are there, volunteering your time, to make something better. Since these projects are pre-existing, the project leads have experience working closely with all sorts of experience levels, backgrounds, and capacities. If you were 'orca'strating (haw) a voluntary mission, wouldn't you be appreciative of the time others spend to help? </p><p/><h3 id="and-that-s-exactly-what-happened-">And that's exactly what happened.</h3><p/><!--kg-card-begin: html--> <img src="https://i.imgflip.com/jl60x.jpg" alt="Signing up for your first Hackathon" style="margin: auto"/><!--kg-card-end: html--><p/><p>After only briefly wading and wondering through over a dozen projects with dissimilar tech stacks, progress points, needs, and skill sets, I found a place I could be of service.</p><p>The team I partnered with, (<a href="https://www.orcasound.net/">Orcasound</a>) put together by marine biologist Scott Veirs, was working on migrating a static html prototype over to a React (or similar SPA-render) structure. </p><p>After spending a bit of time aligning my IDE with the project environment, I set to work on continuing the migration of a map whose purpose was rendering observation events from citizen-scientists (in this case orca enthusiasts) into a <a href="https://docs.mapbox.com/help/tutorials/use-mapbox-gl-js-with-react/">Mapbox frame</a>. </p><p> I actually spent the better part of the day debugging why only a single data point was coming across from an API call that was previously in use to grab all the rows in a user-modified google spreadsheet. And πΊ I think I found out why! πΊ</p><h4 id="turns-out-">Turns out:</h4><p>Google sheets API had recently (sept 30 2020) upgraded their API from v3 to v4, and somewhere buried in that, seems to be a slight change in the methods used which seemed to cause a problem with the <a href="https://www.npmjs.com/package/google-spreadsheet">npm package we were using</a> despite the assurances to the contrary. Ahh, here we go. Check out that 'row-based API' line from the docs on our npm package.</p><figure class="kg-card kg-image-card"><img src="https://adampacholski.com/supercodebros/content/images/2020/11/image.png" class="kg-image" alt="Signing up for your first Hackathon" srcset="https://adampacholski.com/supercodebros/content/images/size/w600/2020/11/image.png 600w, https://adampacholski.com/supercodebros/content/images/2020/11/image.png 805w" sizes="(min-width: 720px) 720px"/></figure><p>I never solved the bug. Without the time to dive the (notoriously hairy) Google Sheets API docs to find the differences in v3-v4 and bypassing the facilitator NPM package, I didn't make too much headway.</p><h3 id="what-if-i-don-t-get-enough-done">What if I don't get enough done?</h3><p>At this point, there was a bit of "what if I'm not productive enough" panic, the old impostor syndrome setting in. </p><!--kg-card-begin: html--><div style="display: 'grid'; place-items: 'center'"> <img src="https://media1.tenor.com/images/0629ad4b9ee29bc697d09fc0366ac5ad/tenor.gif" alt="Signing up for your first Hackathon" style="margin: '0 auto'"/> </div><!--kg-card-end: html--><p>But if you don't dive in, you'll stay dry. Real dry. Realllllyyyyy dry. Which is less cool when you are coding for the health and safety of cetaceans π³. </p><p>See, it came out during the day that the team was already looking at veering away from google sheets to, eventually, an interface that updates their aggregation database directly, without reading a hand-written spreadsheet. <em> And maybe the debugging I did helped encourage the transition!</em></p><p/><h3 id="tl-dr-what-does-it-mean">TL;DR; What does it mean?</h3><p>The team I joined was kind and patient, and after milling around in the awesome <a href="https://spatial.chat/s/DemocracyLab">proximity lounge spatial chat</a> after the demos and getting to know some folks, I would have been in equally good stead jumping into any of them.</p><figure class="kg-card kg-image-card"><img src="https://i.imgflip.com/tdvws.jpg" class="kg-image" alt="Signing up for your first Hackathon"/></figure><h3 id="go-for-it-seriously-go-for-it-">Go For it! Seriously. Go for it!</h3><p>I was apparently friendly and interested enough that the team welcomed my further involvement down the line!</p><p>Whether you have experience working on remote-collaborative projects or not, these civic hack days are an amazing way to hone your chops, address new problems, meet great industry professionals, and add a little something to your resume!</p><p>Cheers folks,</p><h5 id="-onward-and-upward-evan-out-">π βββπ©βπ onward and upward, Evan outπ¨βπ π </h5><p/><p/><h3 id="other-resources-">Other Resources: </h3><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://developers.google.com/sheets/api"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Sheets API | Google Developers</div><div class="kg-bookmark-description">The Sheets API gives you full control over the content and appearence of your spreadsheet data.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://www.gstatic.com/devrel-devsite/prod/v2210075187f059b839246c2c03840474501c3c6024a99fb78f6293c1b4c0f664/developers/images/touchicon-180.png" alt="Signing up for your first Hackathon"><span class="kg-bookmark-publisher">Google Developers</span></img></div></div><div class="kg-bookmark-thumbnail"><img src="https://www.gstatic.com/devrel-devsite/prod/v2210075187f059b839246c2c03840474501c3c6024a99fb78f6293c1b4c0f664/developers/images/opengraph/google-green.png" alt="Signing up for your first Hackathon"/></div></a></figure><figure class="kg-card kg-bookmark-card kg-card-hascaption"><a class="kg-bookmark-container" href="https://democracylab.org"><div class="kg-bookmark-content"><div class="kg-bookmark-title">DemocracyLab</div><div class="kg-bookmark-description">DemocracyLab is a matchmaking platform that helps socially-conscious individuals who want to help their communities find tech-for-good projects that match interests and skills</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://d1agxr2dqkgkuy.cloudfront.net/img/favicon.png" alt="Signing up for your first Hackathon"/></div></div><div class="kg-bookmark-thumbnail"><img src="https://d1agxr2dqkgkuy.cloudfront.net/img/dl_logo.png" alt="Signing up for your first Hackathon"/></div></a><figcaption>Have a project? Want a project? Come volunteer, or source some talent!</figcaption></figure>]]></content:encoded></item><item><title><![CDATA[Cache Busting For Link Previews]]></title><description><![CDATA[Link previews to the homepage are showing our OLD header image... how can we fix that?]]></description><link>https://supercodebros.dev/caching-link-previews/</link><guid isPermaLink="false">Ghost__Post__5fb95f351d8bca7a1384b5dc</guid><category><![CDATA[caching]]></category><category><![CDATA[Gatsby]]></category><category><![CDATA[Ghost]]></category><category><![CDATA[Netlify]]></category><dc:creator><![CDATA[Adam Pacholski]]></dc:creator><pubDate>Sat, 21 Nov 2020 19:23:41 GMT</pubDate><content:encoded><![CDATA[<p>Link previews to the homepage are showing our OLD header image... how can we fix that?</p><h3 id="ship-it-">Ship it!</h3><p>One thing I like to do at the beginning of a web project is deploy to production as soon as possible. Deploying early and often helps you deal with infrastructure problems early, so you can avoid wasting time coding something broken and then redoing it after you discover what you really need to take your project live.</p><p>However, that can mean deploying a project before it feels totally ready, and in this case, our first deploy had some boilerplate from the <a href="https://github.com/TryGhost/gatsby-starter-ghost">gatsby-starter-ghost</a> project we bootstrapped from.</p><h3 id="old-news">Old News</h3><p>We removed the boilerplate recently, and then ran into a minor irritation - trying to post a link to the blog homepage on LinkedIn showed the old header image and title tag in the link preview!</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://adampacholski.com/supercodebros/content/images/2020/11/old-preview.png" class="kg-image" alt="" srcset="https://adampacholski.com/supercodebros/content/images/size/w600/2020/11/old-preview.png 600w, https://adampacholski.com/supercodebros/content/images/size/w1000/2020/11/old-preview.png 1000w, https://adampacholski.com/supercodebros/content/images/size/w1600/2020/11/old-preview.png 1600w, https://adampacholski.com/supercodebros/content/images/2020/11/old-preview.png 1892w" sizes="(min-width: 720px) 720px"><figcaption>That doesn't look right...</figcaption></img></figure><p><a href="https://supercodebros.dev/author/evan">Evan</a> scoured our frontend repo for the "Ghost Gatsby Starter" string, or a reference to the header image coming from Ghost... to no avail.</p><h3 id="troubleshooting">Troubleshooting</h3><p>The first thing I wanted to know was - how do those link previews work, actually?</p><p>Looking at the NGINX logs, I was puzzled to see that not much was happening when I generated a link preview by editing a LinkedIn post.</p><p>Then I tried generating link previews on other platforms - facebook, twitter, and slack. I only saw a single request to robots.txt in the nginx logs (weird, right? more in a minute), but more importantly, on all those other platforms, the link previews were 100% up to date:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://adampacholski.com/supercodebros/content/images/2020/11/twitter.png" class="kg-image" alt="" srcset="https://adampacholski.com/supercodebros/content/images/size/w600/2020/11/twitter.png 600w, https://adampacholski.com/supercodebros/content/images/size/w1000/2020/11/twitter.png 1000w, https://adampacholski.com/supercodebros/content/images/2020/11/twitter.png 1202w" sizes="(min-width: 720px) 720px"><figcaption>Twitter really DOES have the latest</figcaption></img></figure><p>To me that says one thing: caching.</p><h3 id="cache-rules-everything-around-me">Cache Rules Everything Around Me</h3><p>So LinkedIn is probably caching the link previews, which would explain why it's the only platform that has the old title and header image, right?</p><p>Right! <a href=" https://www.linkedin.com/pulse/how-clear-linkedin-link-preview-cache-ananda-kannan-p/">This blog post</a> has some helpful info: link previews could be cached for 7 days, and there's (no kidding) a LinkedIn tool to break the cache !_!</p><p><a href="https://www.linkedin.com/post-inspector/">https://www.linkedin.com/post-inspector/</a></p><p>I dumped our homepage url into the post inspector tool, and when I tried writing another LinkedIn post, the preview was fully up to date.</p><p>What a pleasant surprise! It's so common for a caching layer to provide zero interface to users, and if something bad gets stuck in the cache, you just have to wait it out. Bravo, LinkedIn, I'm impressed.</p><h3 id="my-log-has-something-to-tell-you">My Log Has Something To Tell You</h3><p>When I was writing this up, I realized a mistake I made above... those NGINX logs are for the Ghost server, and not the Gatsby frontend! No wonder I didn't see much when I was testing out the link previews in the other platforms.</p><p>Well, can we get the logs from Netlify? I wasn't too surprised to learn that they are <a href=" https://community.netlify.com/t/download-raw-server-access-logs/6586/5">not promising</a> to provide that feature soon, if ever.</p><p>Netlify has been a totally amazing developer experience so far, but if they provide all these great features (build pipeline? build PREVIEWS? set up a JAMstack site in, like, 10 clicks?) at an unbeatable price (free), there's got to be a trade-off.</p><p>Even so, I'm inclined to enjoy Netlify's free product for a little while longer, and deal with upgrading to paid (or maybe changing our hosting solution) only when problems arise that we can't resolve with our current tools.</p>]]></content:encoded></item><item><title><![CDATA[Ghost HTTPS Redirect Loop π»]]></title><description><![CDATA[What if putting Cloudflare in front of Ghost is causing Ghost to 301?]]></description><link>https://supercodebros.dev/ghost-https-redirect-loop/</link><guid isPermaLink="false">Ghost__Post__5fb05f291cc0b133ac32419b</guid><category><![CDATA[Ghost]]></category><category><![CDATA[https]]></category><category><![CDATA[redirect]]></category><category><![CDATA[Gatsby]]></category><category><![CDATA[Cloudflare]]></category><category><![CDATA[Nginx]]></category><dc:creator><![CDATA[Adam Pacholski]]></dc:creator><pubDate>Sat, 14 Nov 2020 23:41:32 GMT</pubDate><content:encoded><![CDATA[<p>This weekend we're cleaning up some last loose ends and putting up our first couple of posts.</p><h3 id="the-problem">The Problem</h3><p>One of those loose ends was a redirect loop that we first ran into when trying to turn on HTTPS when we first set up our production Ghost install.</p><p>At the time, we were <a href="https://www.supercodebros.dev/gatsby-ghost-mysql-ubuntu/">fighting with compatibility issues between MySQL 8 and Ghost</a>, so we decided to just leave Ghost running on HTTP and troubleshoot the redirect loop later.</p><p>Then when we put up our first post, we got a bunch of Mixed Content errors from the images:</p><!--kg-card-begin: markdown--><pre><code>In gatsby-ghost-mysql-ubuntu/index.html: Insecure img urls: http://adampacholski.com/supercodebros/content/images/2020/11/Screen-Shot-2020-11-11-at-8.36.25-AM.png ... </code></pre> <!--kg-card-end: markdown--><p>Since we gave Ghost an HTTP <code>url</code> in our production config, all the images it was providing to Gatsby had HTTP urls, which were showing up in our nice HTTPS blog post π</p><h3 id="some-background">Some Background</h3><!--kg-card-begin: markdown--><ul> <li>Our frontend is a Gatsby app, which uses a production Ghost install on a Digital Ocean droplet as an API backend</li> <li>Since Ghost is installed on a domain I plan to use for other things, we installed the production Ghost instance at a subdirectory - /supercodebros/</li> </ul> <!--kg-card-end: markdown--><h3 id="try-all-the-things">Try All The Things</h3><p>Most of the <a href="https://github.com/TryGhost/Ghost/issues/2796">troubleshooting advice</a> we found for 'Ghost HTTPS Redirect Loop' centered around fixing an issue with the default Ghost nginx config:</p><!--kg-card-begin: markdown--><pre><code>proxy_set_header X-Forwarded-Proto https; </code></pre> <!--kg-card-end: markdown--><p>My guess is that the default nginx config generated by Ghost used <code>http</code> at some previous point for <code>X-Forwarded-Proto</code>, and sometime later that was fixed. The nginx config Ghost generated for us used <code>$scheme</code>, which should work fine for requests coming via HTTPS. We also tried changing <code>$scheme</code> to <code>https</code> - no luck, same redirect loop.</p><p>Next, we tried a cURL request, and reproduced the same error (but only once, since we didn't tell cURL to follow redirects):</p><!--kg-card-begin: markdown--><pre><code>curl -Is https://adampacholski.com/supercodebros/ HTTP/2 301 ... server: cloudflare x-powered-by: Express </code></pre> <!--kg-card-end: markdown--><p>A couple of interesting things that came back in the response headers:</p><!--kg-card-begin: markdown--><ul> <li>Oh yeah, I put Cloudflare in front of adampacholski.com! This was a while ago and I had forgotten - was playing with Cloudflare workers</li> <li>Wait, the redirect is coming from Express??</li> </ul> <!--kg-card-end: markdown--><p>That means the requests are getting to Ghost, and Ghost is redirecting. Nginx is fine. Confirmed that looking at the Ghost logs:</p><!--kg-card-begin: markdown--><pre><code>$ ghost log [2020-11-14 18:21:51] INFO "HEAD /supercodebros/" 301 1ms [2020-11-14 18:21:51] INFO "HEAD /supercodebros/" 301 1ms [2020-11-14 18:21:51] INFO "HEAD /supercodebros/" 301 1ms [2020-11-14 18:22:13] INFO "HEAD /supercodebros/" 301 1ms ... </code></pre> <!--kg-card-end: markdown--><p>And by shutting down Ghost via <code>ghost stop</code>, which gave us a 502 (progress! π).</p><h3 id="solution">Solution</h3><p>This got me thinking... what if something about having Cloudflare in front of Ghost is causing Ghost to 301? Looking back at the github issue we looked at originally, we saw an <a href="https://github.com/TryGhost/Ghost/issues/2796#issuecomment-392327647">answer with a Cloudflare suggestion</a>:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://adampacholski.com/supercodebros/content/images/2020/11/full-strict-ssl.png" class="kg-image" alt="" srcset="https://adampacholski.com/supercodebros/content/images/size/w600/2020/11/full-strict-ssl.png 600w, https://adampacholski.com/supercodebros/content/images/size/w1000/2020/11/full-strict-ssl.png 1000w, https://adampacholski.com/supercodebros/content/images/2020/11/full-strict-ssl.png 1488w" sizes="(min-width: 720px) 720px"><figcaption>Thank you, internet stranger</figcaption></img></figure><p>We changed our SSL settings to <code>Full (strict)</code>, and immediately got a 200 back from Ghost π π»</p><p>I was curious what that <code>Full (strict)</code> Cloudflare setting does - I don't think I changed it from the default setting, which is <code>Flexible SSL</code>. An <a href="https://support.cloudflare.com/hc/en-us/articles/115000219871-Troubleshooting-redirect-loop-errors">exceptionally helpful Cloudflare troubleshooting article</a> said this:</p><!--kg-card-begin: markdown--><blockquote> <p>The Flexible SSL encryption mode in the Cloudflare SSL/TLS app Overview tab encrypts traffic between the browser and the Cloudflare network over HTTPS. However, when the Flexible SSL option is enabled, Cloudflare sends requests to your origin web server unencrypted over HTTP. Redirect loops occur if your origin web server is configured to redirect all HTTP requests to HTTPS when using the Flexible SSL option.</p> </blockquote> <!--kg-card-end: markdown--><p>Yep, that would definitely cause Ghost to redirect to HTTPS, if Cloudflare was sending requests down via HTTP. </p><p>After that, it was easy to rebuild Gatsby and get rid of those Mixed Content errors π₯</p>]]></content:encoded></item><item><title><![CDATA[Gatsby, Ghost, Ubuntu 18.04, & MySQL - 5.7, or 8.0?]]></title><description><![CDATA[Setting up Ghost's MySQL with a Gatsby Frontend on an Ubuntu 18.04 server? Here's our journey.]]></description><link>https://supercodebros.dev/gatsby-ghost-mysql-ubuntu/</link><guid isPermaLink="false">Ghost__Post__5f95f24f6bb79165aa946a28</guid><category><![CDATA[Setup]]></category><dc:creator><![CDATA[Evan Pacholski]]></dc:creator><pubDate>Fri, 13 Nov 2020 22:45:49 GMT</pubDate><content:encoded><![CDATA[<p/><p>So Adam and I are adding in the JAM stack to our repertoire (with this blog). And, all things considered... it's not that bad. But let's talk about picking the right installation for the job. Ghost prefers a MySQL db, and we decided to start with an easy-up config repo we found at:</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://github.com/TryGhost/gatsby-starter-ghost"><div class="kg-bookmark-content"><div class="kg-bookmark-title">TryGhost/gatsby-starter-ghost</div><div class="kg-bookmark-description">A starter template to build lightning fast websites with Ghost & Gatsby - TryGhost/gatsby-starter-ghost</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.githubassets.com/favicons/favicon.svg"><span class="kg-bookmark-author">TryGhost</span><span class="kg-bookmark-publisher">GitHub</span></img></div></div><div class="kg-bookmark-thumbnail"><img src="https://repository-images.githubusercontent.com/157323155/c45ab880-edc9-11e9-9ec6-fe6099199595"/></div></a></figure><p/><p>So why write this article? </p><p>Everything in the installation instructions for Ghost and MySQL on an Ubuntu 18.04 server seems in the clear, and the last known issue was 2 years ago, give or take an acorn. </p><p>Seemed like a quick install, a user setup, and Robert's your father's Brother, call it a day!</p><h4 id="because-all-is-not-as-it-seems-">Because all is not as it seems.</h4><p/><blockquote> (By the way, did you know the "Bob's your Uncle" expression came from a minister of Ireland receiving his job through nepotism? Literal nepotism, from Nepos, Latin for nephew, as the Minister who appointed him was his actual uncle.)</blockquote><h4 id="here-s-the-rub-what-happens-when-the-docs-tell-you-there-aren-t-issues">Here's the rub; what happens when the docs tell you there aren't issues?</h4><p>Specifically we were setting up a MySQL database for an installation of Ghost, our headless CMS weapon of choice, on a multi-use Digital Ocean droplet for π SuperCodeBros. π</p><h2 id="the-process">The Process</h2><p><strong>1. </strong>We first checked to see if MySQL 8.0 was compatible. The ghost docs are pretty straightforward and easy to follow, and yep! Ubuntu 18.04 is certainly on the list of available setup options. Seems groovy.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://ghost.org/docs/install/ubuntu/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">How to install & setup Ghost on Ubuntu 16.04, 18.04 and 20.04</div><div class="kg-bookmark-description">A full production install guide for how to install the Ghost professional publishing platform on a production server running Ubuntu 16.04, 18.04 or 20.04.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://ghost.org/icons/icon-512x512.png?v=7b7c010c4f88a275be80db697657cff6"><span class="kg-bookmark-author">Ghost</span><span class="kg-bookmark-publisher">Ghost</span></img></div></div><div class="kg-bookmark-thumbnail"><img src="https://ghost.org/images/meta/Ghost-Docs.jpg"/></div></a></figure><p><br><strong>2. </strong>We then installed and tried to set up MySQL 8.0 with ghost - This also went pretty straightforward, until...</br></p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://adampacholski.com/supercodebros/content/images/2020/11/Screen-Shot-2020-11-11-at-8.36.25-AM.png" class="kg-image" alt="" srcset="https://adampacholski.com/supercodebros/content/images/size/w600/2020/11/Screen-Shot-2020-11-11-at-8.36.25-AM.png 600w, https://adampacholski.com/supercodebros/content/images/size/w1000/2020/11/Screen-Shot-2020-11-11-at-8.36.25-AM.png 1000w, https://adampacholski.com/supercodebros/content/images/size/w1600/2020/11/Screen-Shot-2020-11-11-at-8.36.25-AM.png 1600w, https://adampacholski.com/supercodebros/content/images/2020/11/Screen-Shot-2020-11-11-at-8.36.25-AM.png 2236w" sizes="(min-width: 1200px) 1200px"/></figure><p><strong>3. </strong>As we are running Ubuntu 18.04 on our droplet, setting our new user and password was the next step. We chose a secondary user name for access to our post db (<em>not</em> ghost, as this will cause a conflict with ghost itself), and set about this next step...</p><!--kg-card-begin: html--><span style="color: red; font-weight: bold;"> ER_NOT_SUPPORTED_AUTH_MODE: Client does not support authentication protocol requested by server; consider upgrading MySQL client </span><!--kg-card-end: html--><p/><p>A bit cryptic as errors go, but a dive's a dive, and we found a few minor issues along the way. </p><p>As of MySQL 5.7.5 the password() function we normally used was taken out, but this wasn't the issue.</p><p>As of MySQL 8.0 the default password management plugin is <strong><strong><em><em>caching_sha2_password</em></em></strong></strong> rather than <strong><em>mysql_native_password </em></strong>(as of 5.7 and earlier), so the following statement didn't seem like it would work for us. </p><pre><code class="language-bash">ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password';</code></pre><h4 id="-it-didn-t-">...It didn't.</h4><p/><p><strong> 4. </strong>Since we were on a call at this point and debugging the install together, I <em>should </em> have been writing down all the errors and the solutions we found the way I normally do. It's also important to note that several back-and-forth options were tried, and some small mistakes were likely made along the way, but at this point, we tried: </p><ul><li>Remaking a user. Same error. </li><li>Changing the alter user statement to use the newer plugin. (same error!)</li><li>Configuring our MySQL 8.0 installation to USE the old plugin, changing to mysql_native_password, <em>then</em> changing the password. No dice, but you can find more information about MySQL password validation plugins in their docs <a href="https://dev.mysql.com/doc/refman/8.0/en/validate-password.html">here</a>.</li><li>Reinstalling MySQL 8.0 with the legacy encryption option. (same error!) (GUI version pictured below)</li></ul><figure class="kg-card kg-image-card"><img src="https://adampacholski.com/supercodebros/content/images/2020/11/MySQL8_1_legacy.png" class="kg-image" alt="" srcset="https://adampacholski.com/supercodebros/content/images/size/w600/2020/11/MySQL8_1_legacy.png 600w, https://adampacholski.com/supercodebros/content/images/2020/11/MySQL8_1_legacy.png 799w" sizes="(min-width: 720px) 720px"/></figure><p><br>None of them worked, <strong>same error.</strong> </br></p><!--kg-card-begin: html--> <img class="giphy-gif-img" src="https://media1.giphy.com/media/3FNCFXeaVSwEM/giphy.gif?cid=ecf05e470dpee7bfhuhbl58q0p31xet8qz3zs7n09jm7zvgd&rid=giphy.gif" width="248" height="300" alt="stephen colbert oops GIF"> <!--kg-card-end: html--><p>So we uninstalled, and rolled back to MySQL 5.7, bing bang boom, donezo. </p><p> This means we'll eventually have to upgrade from MySQL 5.7... by then let's hope the issue we ran into with the newer authentication plugin has been fixed and works with MySQL 8.0 and Ghost by then.</p><hr><h2 id="the-take-home-">The Take-home...</h2><p/><p>I'm not saying skip the encryption and validation plugin weirdness and just use MySQL 5.7, even though you still need a connector just to USE the newer plugin if you want to use an interface like Workbench, far from it. </p><p>We went with 5.7 at the end mostly because my collaborator-brΓΆder is exceptionally comfortable with it (he works with it professionally), and to make sure that we hadn't accidentally changed anything else during our tweaking. After a few try-and-fail solutions, it became a game of diminished loss since we were just setting up our db and there was no issue with data loss or downtime.</p><p>What I <em>am</em> saying, is <em>you can read those docs, and still find pitfalls, but climb out anyway.</em> After all, this blog uses our accepted configuration right now, just fine.</p><p>and now, the good news: </p><p><em>You are not always as far from a solution as it seems.</em></p><p>π±βπOnward and Upward, Evan Out.π±βπ</p><p/><h3 id="other-references-">Other references: </h3><figure class="kg-card kg-bookmark-card kg-card-hascaption"><a class="kg-bookmark-container" href="https://dev.mysql.com/doc/refman/8.0/en/caching-sha2-pluggable-authentication.html"><div class="kg-bookmark-content"><div class="kg-bookmark-title">MySQL :: MySQL 8.0 Reference Manual :: 6.4.1.2 Caching SHA-2 Pluggable Authentication</div><div class="kg-bookmark-description"/><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://labs.mysql.com/common/themes/sakila/favicon.ico"/></div></div></a><figcaption>MySQL 8.0 authentication plugin documentation</figcaption></figure><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://stackoverflow.com/questions/52320576/in-mysql-server-8-0-the-password-function-not-working"><div class="kg-bookmark-content"><div class="kg-bookmark-title">In MySQL SERVER 8.0 the PASSWORD function not working</div><div class="kg-bookmark-description">Error while executing the PASSWORD function in MySQL Server version 8.0.12 I have the following query: SELECT * FROM users WHERE login = βFABIOβ AND pwd = PASSWORD(β2018β²) LIMIT 0, 50000 I...</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://cdn.sstatic.net/Sites/stackoverflow/Img/apple-touch-icon.png?v=c78bd457575a"><span class="kg-bookmark-author">Fabio C</span><span class="kg-bookmark-publisher">Stack Overflow</span></img></div></div><div class="kg-bookmark-thumbnail"><img src="https://cdn.sstatic.net/Sites/stackoverflow/Img/apple-touch-icon@2.png?v=73d79a89bded"/></div></a></figure></hr></img>]]></content:encoded></item></channel></rss>