# Jonathan Creamer > Building Frontend tooling #DivOps. Senior Engineer @microsoft 🏢. Previously @eventbrite 🎉 @lonelyplanet 🌎JavaScript | Typescript | React | Node.js Public Ghost content for AI and LLM tooling. This file includes a bounded export of public pages first, then recent public posts. Append `.md` to any post or page URL to get the content in Markdown (for example, `/example-post.md`). ## Pages ### Xbox One EPIC Clips URL: https://www.jonathancreamer.com/xbox-one-epic-clips/ Last updated: 2019-10-31T13:12:47.000Z I loves me some Xbox One! Here's some of my favorite clips from me yelling at my Xbox, "XBOX Record THAT!" ### PubG ### Titanfall ### Battlefield 4 ### Call of Duty ### Star Wars Battlefront ### About Me URL: https://www.jonathancreamer.com/about/ Last updated: 2022-02-07T17:45:59.000Z ![my headshot](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2022/02/MicrosoftTeams-image--1-.png) JavaScript and front end technologies are my passion. I also believe that you cannot ever stop learning which is why I stay active in the development community attending user groups like [http://nashjs.org](http://nashjs.org/?ref=jonathancreamer.com), and NashDotNet, blogging on [http://jonathancreamer.com](http://jonathancreamer.com/?ref=jonathancreamer.com), Net Tuts, and Smashing Magazine, tweeting at [jcreamer898](https://twitter.com/jcreamer898?ref=jonathancreamer.com) and scouring the internet for as much knowledge I can squeeze into my brain. I work as a Senior Engineer at Microsoft. I love meeting other devs who are passionate about what they do. ## Me.json ```json { "name": "Jonathan Creamer", "birthday": "6/18/1988", "degree": "Bachelor of Science in Computer Engineering Tech", "almaMater": "Middle Tennessee State University", "technologies": ["JavaScript", "node.js", "Ruby", "C#", "ColdFusion", "Python"], "hobbies": ["Xbox", "Playing Basketball", "Drinking Bourboun"] } ``` ### nick URL: https://www.jonathancreamer.com/nick/ Last updated: 2021-01-13T15:29:34.000Z Some of my BFF [nickfranciosi's](https://twitter.com/nickfranciosi?ref=jonathancreamer.com) best work... ![805_52390470279_4423_n](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2021/01/805_52390470279_4423_n.jpg) ![805_52390480279_4904_n](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2021/01/805_52390480279_4904_n.jpg) ![805_52390485279_5156_n](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2021/01/805_52390485279_5156_n.jpg) ![805_52390490279_5362_n](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2021/01/805_52390490279_5362_n.jpg) ![805_52390495279_5609_n](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2021/01/805_52390495279_5609_n.jpg) ![805_52390500279_5867_n](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2021/01/805_52390500279_5867_n.jpg) ### Talks URL: https://www.jonathancreamer.com/talks/ Last updated: 2021-11-10T20:48:39.000Z ### Scaling Monorepos Gave this talk at Connect Tech in November 2021. Here's the deck... Recording coming soon. ### Deep dive into the React Lifecycle methods ### Advanced Webpack ### Getting Started with React and typeScript ### Using VSCode's debugger for Jest and node.js ### Resume URL: https://www.jonathancreamer.com/resume/ Last updated: 2020-06-10T16:21:58.000Z Here's my [resume](https://d.pr/f/IpQ9hs+?ref=jonathancreamer.com)! ### Wish List URL: https://www.jonathancreamer.com/wish-list/ Last updated: 2020-11-27T00:00:48.000Z - [https://steelseries.com/gaming-headsets/arctis-7x](https://steelseries.com/gaming-headsets/arctis-7x?ref=jonathancreamer.com) - [https://www.amazon.com/Xbox-Gift-Card-Digital-Code/dp/B00F4CFAV8](https://www.amazon.com/Xbox-Gift-Card-Digital-Code/dp/B00F4CFAV8??ref=jonathancreamer.com) - [https://www.blackriflecoffee.com/collections/hoodies/products/zip-up-camo-poncho-liner-hoodie](https://www.blackriflecoffee.com/collections/hoodies/products/zip-up-camo-poncho-liner-hoodie?ref=jonathancreamer.com) ### Newsletter URL: https://www.jonathancreamer.com/newsletter/ Last updated: 2021-03-19T15:57:36.000Z _No content available._ ### Scaling Monorepos URL: https://www.jonathancreamer.com/scaling-monorepos-talk/ Last updated: 2024-10-29T18:35:59.000Z ### **Scaling Monorepos** Gave this talk at Connect Tech in November 2021\. Here's the deck... ### Products URL: https://www.jonathancreamer.com/products/ Last updated: 2023-01-03T18:53:10.000Z Here's some products that I've been loving! ## Fitness | Item | Price | Link | | ---------- | ----- | -------------------------------------------------------------------------- | | Bar | $56 | [https://amzn.to/3X4vN3Y](https://amzn.to/3X4vN3Y?ref=jonathancreamer.com) | | Ball | $37 | [https://amzn.to/3VQbp5C](https://amzn.to/3VQbp5C?ref=jonathancreamer.com) | | Dumbbells | $50 | [https://amzn.to/3Qjd9TB](https://amzn.to/3Qjd9TB?ref=jonathancreamer.com) | | Kettlebell | $41 | [https://amzn.to/3iawxpf](https://amzn.to/3iawxpf?ref=jonathancreamer.com) | | Box | $110 | [https://amzn.to/3GzlVcY](https://amzn.to/3GzlVcY?ref=jonathancreamer.com) | | Rope | $18 | [https://amzn.to/3QbtWYI](https://amzn.to/3QbtWYI?ref=jonathancreamer.com) | | Total | $312 | \-- | ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2023/01/bar.jpg) #### Yes4All Heavy Duty Pull Up Bar Super sturdy pull up bar. It's not going anywhere, just be sure to mount to the studs! [Buy It](https://amzn.to/3X4vN3Y?ref=jonathancreamer.com) ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2023/01/wod.jpg) #### WOD Nation Speed Jump Rope Learn double unders. They're BRUTAL, but so effective. [Buy It](https://amzn.to/3QbtWYI?ref=jonathancreamer.com) ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2023/01/db.jpg) #### Balancefrom Rubber Encased Hex Dumbbell Easy peasy dumbells with a good grip [Buy It](https://amzn.to/3Qjd9TB?ref=jonathancreamer.com) ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2023/01/kb.jpg) #### CAP Barbell Black Cast Iron Kettlebell Really nice kettlebell with a good grip for no slipping [Buy It](https://amzn.to/3iawxpf?ref=jonathancreamer.com) ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2023/01/ball.jpg) #### BalanceForm Slam Ball Use this thing to slam away all your anxiety. This thing can be used for all kinds of workouts! [Buy it](https://amzn.to/3VQbp5C?ref=jonathancreamer.com) ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2023/01/box.jpg) #### BalanceFrom Plyometric Box Great plyo box that won't scrape your knees up when you miss. ;) [Buy It](https://amzn.to/3GzlVcY?ref=jonathancreamer.com) ## Tech ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2022/07/27BE84BE-9325-4B5B-98E8-85200D9DD235.jpeg) #### Logictech Combo Touch Super cool keyboard case that has changed the game for me using my iPad. [Buy It](https://amzn.to/3Rpjo8A?ref=jonathancreamer.com) ### Developer Language Learners URL: https://www.jonathancreamer.com/developer-language-learner/ Last updated: 2023-02-06T21:47:59.000Z You a developer that wants to get with hang with other developers that love learning languages?! We're not talking JavaScript, Rust, C++, etc. We're talking, Español, French, German, Polish, etc. Come and join the DLL! [Join the DLL](https://discord.gg/6FxN5CNJGk?ref=jonathancreamer.com) ## Posts ### Using Dev Tunnels to share your local web server URL: https://www.jonathancreamer.com/using-dev-tunnels-to-share-your-local-web-server/ Last updated: 2026-07-31T14:06:47.000Z I was recently building a prototype app for some work, and wanted to share it with my team. Usually I would turn to something like ngrok, but I was curious if there was anything new, and anything that was more native to where I work (Microsoft). Turns out, one of the technologies enabling VSCode Codespaces, Microsoft's [Dev Tunnel](https://learn.microsoft.com/en-us/azure/developer/dev-tunnels/overview?ref=jonathancreamer.com), was the answer! Dev Tunnels can also give you a public HTTPS URL pointing at a port on your laptop in three commands. Perfect for sharing a running webapp with coworkers (or with M365 agents/Copilot connectors) without standing up infra. ### **Setup** On macOS at least... There's other install [instructions](https://learn.microsoft.com/en-us/azure/developer/dev-tunnels/get-started?tabs=macos&ref=jonathancreamer.com) too. ```sh brew install --cask devtunnel ``` ### **Host a local port** Whichever port you want to host, say a local express, flask, rails app or whatever... ```sh devtunnel host -p 5050 ``` This prints a tunnel ID like \`kind-cat-q7qlggx.use2\` and a URL that proxies to \`localhost:5050\` on your machine. ### **Restrict access to the Microsoft tenant** This one was a bonus for me! You can also restrict access to it if you're setup with AAD or Entra at your company. By default the URL is anonymous. To require a Microsoft AAD sign-in (so only people in the tenant can hit it): ```sh devtunnel access create kind-cat-q7qlggx.use2 --tenant ``` Now anyone hitting the URL has to auth as a Microsoft employee, which is the right default for sharing internal-only tools, demos, or webhooks. ### Why it matters - No ngrok account - No firewall changes - No temporary App Service. - Tenant-scoped access means you can safely point AAD-protected services (M365 connectors, Copilot plugins, Teams app manifests) at your laptop while iterating - Tunnel survives reboots once \`host\` is running; the URL is stable per tunnel ID so you can wire it into a manifest once. ### **Gotchas to remember** The `host` process has to keep running — when you close the terminal the tunnel goes down. `--tenant` only gates *who can reach the URL*; your app still needs to verify the AAD token itself if you care about identity inside the app. ### Celebrating my 5th year at Microsoft by developing AI skills URL: https://www.jonathancreamer.com/celebrating-my-5th-year-at-microsoft/ Last updated: 2026-02-17T03:43:10.000Z In many ways, after 5 years at Microsoft, I still feel like I just started yesterday. Especially given the immense changes that AI is bringing to our day to day lives as Software Engineers. I have been so fortunate to be a part of an incredible organization at Microsoft called ES365 focusing on the developer tooling and experience of frontend developers working on our flagship web products including Copilot, Teams, WXPOV (Word, Powerpoint, Excel Onenote, Visio), and so much more. I've been through 2 promotions in 5 years landing the Principal role most recently. The team I'm on, 1JS, is a phenomenal team with folks from every corner of the globe, not just my immediate 1JS team based out of Redmond, but our global 1JS team of about 50 in the U.S., Canada, Norway, Kenya, and India. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2026/02/image-1.png) ## Developing AI Skills I'm also fortunate to be a part of an organization that is evolving with the AI landscape, and leading the charge with things like our internal CLI, Agency, which gives Microsoft engineers a single consistent way to interact with all the latest and greatest AI models and tools like [Github Copilot CLI](https://cli.github.com/?ref=jonathancreamer.com), and [Claude CLI](https://code.claude.com/docs/en/overview?ref=jonathancreamer.com), and comes preconfigured with all kinds of cool MCP servers for making working in huge codebases much easier. We're encouraged to use AI in as much of our work as we can, and are having tons of great internal learning events hearing from all sorts of folks, and our upcoming annual FHL. In the past few weeks I've been learning about [Skills](https://claude.com/skills?ref=jonathancreamer.com), turning every day tasks like creating daily logs, pushing PRs, and so much more into markdown files that help Github and Claude both know how to easily automate. I've setup my dotfiles repo again to refresh my knowledge of things like Tmux so I can have lots of pains open with different agents running all kinds of different tasks. We've even released a [WorkIQ MCP server](https://learn.microsoft.com/en-us/microsoft-365-copilot/extensibility/workiq-overview?ref=jonathancreamer.com) that they ship with agency that connects through all of the Microsoft graph and can help summaries emails, teams, calendar events, and virtually anything. Overall I'm *extremely* excited for what all these new tools can do to help me get through my backlog, and they'll be many more blob posts to come on cool things I'm learning! ### What does Build.SourceVersion actually mean in Azure pipelines URL: https://www.jonathancreamer.com/what-does-build-sourceversion-actually-mean-in-azure-pipelines/ Last updated: 2025-12-03T16:01:48.000Z A quick tip for anyone working in Azure Pipelines as I didn't see this particular thing documented very well in any public documentation. I was recently working on a new Azure Pipeline definition to do some testing of our CI pipelines. What I wanted to do was allow our 1JS Engineering Systems team to run a "CI Validation" pipeline which basically takes their branch, and runs it against our CI pipeline, but with test accounts, and no publishing side effects. Everything, *seemed* to be going fine, however, I was seeing random failures in the build that were sometimes there, and sometimes not. The `checkout` we're using is a bit custom, but more or less is using a [Predefined Variable](https://learn.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&tabs=yaml&ref=jonathancreamer.com) called `Build.SourceVersion` which the docs describe as... > The latest version control change of the triggering repo that is included in this build. We used it in our Pull Request pipelines, so my assumption was that it would do the same thing as our PRs and more or less give you a commit that is the result of merging your Source Branch with your Target Branch, aka something like `feature-foo` with `main`. However, I found out after chatting with a co-worker, and digging around finding this [issue](https://github.com/Microsoft/azure-pipelines-tasks/issues/9801?ref=jonathancreamer.com), that `Build.SourceVersion` changes meaning depending on the context of the pipeline run. > On a pull request, Azure DevOps does not build the exact version of the code that you pushed, but rather a version merged with your target branch If you manually trigger a run of a pipeline, then `Build.SourceVersion` points to the commit at which you triggered the run. However, when Azure Pipelines triggers the build on your behalf in the context of a Pull Request, either set as a required or optional PR policy, the `Build.SourceVersion` gets set to effectively the result of doing this in a temporary branch. ```shell git fetch origin main git checkout git merge --no-ff origin/main # Use the resulting merge commit SHA as Build.SourceVersion ``` You can see this in the difference when you click the sha under the `Version` header in the Sources Cards. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2025/12/image.png) Here's the title of the commit when the run is triggered via a Pull Request... ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2025/12/image-1.png) And when it's triggered manually. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2025/12/image-2.png) You can see in the first, that it clearly mentions the fact that it's taking the latest commit in the Pull Request, and merging it with `main` and using that result as the `Build.SourceVersion` automatically. Therefore, if you want to create pipelines which actually run against a merge of your Source and Target, make sure not to manually trigger it, but instead have either a[ Pull Request Policy](https://learn.microsoft.com/en-us/azure/devops/repos/git/branch-policies?view=azure-devops&tabs=browser&ref=jonathancreamer.com) which is required or optional that allows you to trigger the pipeline from the PR itself. An Optional policy would look like the following image. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2025/12/image-3.png) This optional policy would attach itself to PRs you want, and be queue-able for any developer. Hope that helps any of y'all out there using Azure Pipelines clarify what `Build.SourceVersion` *really* means. ### rebase is dead long live the squabaserge URL: https://www.jonathancreamer.com/rebase-is-dead-long-live-the-squabaserge/ Last updated: 2025-08-21T14:42:14.000Z Alongside tabs vs spaces, tailwind vs vanilla css, emacs vs vim, pepsi vs coke, xbox vs playstation, the age old debate of git rebase vs git merge continues to rage on. It's still one of [Whiskey, Web, and What Not's](https://whiskey.fm/?ref=jonathancreamer.com) 🥃Hot Take questions... Let me start by saying, git rebase is an oft misunderstood construct of git. It's complicated, hard to understand, and can be a cause for confusion. However, it has so many benefits over just merging things. Git commit history is another form of documentation and it should be treated as such! It's such a useful tool as a developer to understand the history of how and why you're codebase changed. ## No holy wars here Instead of getting deeper into a holy war, let's just talk about how `git rebase` when used correctly, can really be so much nicer than just `git merge` all the time. First of all, I'm not saying that you should never **ever** merge stuff. Far from it. If you're a user working on a feature, you'll often have a flow where you work off of your `main` branch. %%{init: { 'logLevel': 'debug', 'theme': 'base', 'gitGraph': {'mainBranchOrder': '3' }} }%% gitGraph commit id:"A" commit id:"B" branch my-new-feature checkout my-new-feature commit id:"C1" commit id:"C2" checkout main commit id:"D" checkout my-new-feature merge main checkout main merge my-new-feature id:"merge commit" commit id:"F" commit id:"G" Here you have a developer which created a `my-new-feature` branch, and while they were working, there were other commits introduced into `main`. This means that the developer now has a choice. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2025/08/image.png) rebase vs merge pressure A lot of developers fear the rebase because of the complexity of possible git conflicts, and the idea that they're re-writing history. And to be fair it is a total PITA when you do rebase your branch, you have multiple commits in your branch history and you do happen to have a git conflict (which can happen in a merge scenario too BTW). What we propose here today is... ## squabaserge The "squabaserge" is a combination of 2 git commands. - git rebase -i :commmitId: - git merge The squash part gives you as the developer the opportunity to clean up the git history of your own branch, *before* you do the rebase. This allows you to remove extraneous git commits, or too many git commits that might make resolving git conflicts tedious. Look at the git history for your branch with `git log --oneline` pick the commit *before* you started your branch, grab it's ID, and do `git rebase -i :commit:` This will pop open a VIM window... don't forget how to save/exit with vim `:wq` ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2025/08/image-2.png) You simply replace the `pick` with `squash` on all the commits you want, leaving behind just one commit with `pick`. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2025/08/image-5.png) Now you can edit your commit messages since you'll only have one commit. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2025/08/image-6.png) ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2025/08/image-7.png) Or a cool VSCode #protip is you can do everything we just did in VIM above, which is even cooler if you have Git Lens installed. You can do is set your `EDITOR=vscode --wait` environment variable to open it in VSCode. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2025/08/image-4.png) Once the rebase is complete, you now have your branch behind `D` , you have a nice clean git history in front of your shiny clean branch with just `C1`. %%{init: { 'logLevel': 'debug', 'theme': 'base', 'gitGraph': {'mainBranchOrder': '3' }} }%% gitGraph commit id: "A" commit id: "B" branch feature commit id: "C1" commit id: "C2" checkout main commit id: "D" %% Rebased history branch feature-rebased commit id: "C1'" checkout main merge feature-rebased tag: "after rebase" Now when you merge your branch with `main` you'll have just the single merge commit into `main`, and won't have all those merge commits in your git history. Many teams in the world enforce squashing when merging into `main` anyways, so by making use of the `squabaserge`, you're actually doing yourself a favor because you'll have an opportunity to clean up the git commit history of your branch prior to that squash happening. ## Conclusion Instead of just `git merge` all the time to get this history of `main` into your branch, especially right when your feature is ready to get put into `main`, opt for the *slightly slower*, but totally worth it flow of the `squabaserge`. **Before** %%{init: { 'logLevel': 'debug', 'theme': 'base', 'gitGraph': {'mainBranchOrder': '3' }} }%% gitGraph commit id:"A" commit id:"B" branch my-new-feature checkout my-new-feature commit id:"C1" commit id:"C2" checkout main commit id:"D" checkout my-new-feature merge main checkout main merge my-new-feature id:"merge commit" commit id:"F" commit id:"G" **After** %%{init: { 'logLevel': 'debug', 'theme': 'base', 'gitGraph': {'mainBranchOrder': '3' }} }%% gitGraph commit id: "A" commit id: "B" commit id: "D" %% Rebased history branch my-new-feature checkout my-new-feature commit id: "C1'" checkout main merge my-new-feature tag: "after rebase" ### How bin linking works in node.js npm and yarn and monorepos URL: https://www.jonathancreamer.com/how-bin-linking-works-in-node-js-npm-and-yarn-and-monorepos/ Last updated: 2025-07-16T00:02:54.000Z `yarn ` `pnpm run thing` `npm run thing` are types of run commands we run all the time when working in node.js. But, how does that actually all work? Let's take a quick look at it! ## Let's start with PATH In all the various operating systems, you can set your `PATH` environment variable. The `PATH` tells your OS all the places it can look for executables, so you can just run things like `node` `git` `yarn` etc without having to specify the binaries full path on disk. Like `c:\program files\some\ridiculously\long\path\git.exe` | Feature | **macOS** (zsh/bash) | **Linux** (bash/zsh) | **Windows** (10/11) | | ---------------------- | ----------------------------------------------------- | ------------------------------------------------------- | --------------------------------------------------------------------------------- | | **Temporary Change** | export PATH=$PATH:/your/path (in Terminal) | export PATH=$PATH:/your/path (in Terminal) | set PATH=%PATH%;C:\\your\\path (in Command Prompt) | | **Permanent (User)** | Add export PATH=... to \~/.zshrc or \~/.bash\_profile | Add export PATH=... to \~/.bashrc or \~/.profile | Use GUI: System Properties → Environment Variables → Edit Path under User section | | **Permanent (System)** | Add to /etc/paths or /etc/paths.d/ (requires sudo) | Add to /etc/environment or /etc/profile (requires sudo) | Use GUI: Edit Path under System variables (admin rights needed) | | **Apply Changes** | source \~/.zshrc or restart Terminal | source \~/.bashrc or restart Terminal | Restart Command Prompt or reboot | | **Separator** | : (colon) | : (colon) | ; (semicolon) | | **View Current** PATH | echo $PATH | echo $PATH | echo %PATH% (in CMD) or $env:Path (in PowerShell) | Each OS has a default list of places it looks for binaries. Ubuntu for example has these defaults. ``` /usr/local/sbin /usr/local/bin /usr/sbin /usr/bin /sbin /bin ``` On all OS's you can add custom folders to add binary locations to your PATH as well. On Windows node adds itself to the `PATH` during install, which you can see via the settings GUI. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2025/07/image.png) That way you can just run `node` in any `cmd` or `powershell`. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2025/07/image-1.png) Node version managers such as [NVM](https://github.com/nvm-sh/nvm?ref=jonathancreamer.com) or [NVM For Windows](https://github.com/coreybutler/nvm-windows?ref=jonathancreamer.com) also add path variables to be able to find whichever version of `node` you've currently chosen with `nvm use vXX.YY`. In Linux / macOS nvm will add an entry to your Bash or ZSH profile which runs the NVM startup script. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2025/07/image-2.png) Which does all kinds of calculations, but ultimately ends up updating the `PATH` to point to where whatever version of node you're using is located. Usually somewhere in the `~/.nvm` folder. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2025/07/image-3.png) ## Let's talk about node\_modules and npm When you install a package with `npm install -g` or `yarn install --global`, you're installing those packages right next to wherever your current version of node exists in it's `node_modules` directory. See the screenshot above and notice it lives right next to where NVM for Windows has set the node version. Inside that directory you'll find all the packages that are installed globally. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2025/07/image-4.png) And all of the `"bins"` live right next to node as well.. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2025/07/image-5.png) Those bins more often than not are just shims to call into each `node_modules` like ... `node node_modules/yarn/bin/yarn.js` ## Local Packages Now, for locally installed packages, it's a bit different... There are `.bin` folders are created during `npm install` and which are symlinks up and over to the packages which list `"bin"` in their `package.json` files. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2025/07/image-10.png) So, for example, the typescript package has a couple of `"bin"` entries in it. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2025/07/image-8.png) When `npm install` or `yarn` `pnmp i` runs, it will look for all those bins, and link them to their corresponding scripts, and on windows it does all kinds of extra files to make sure they're all callable in powershell, cmd, etc. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2025/07/image-6.png) When using `npm run` or `yarn run` or even just `yarn ` you're also able to create your own entries in the `"scripts"` and reference the binary name alone like... ``` { "scripts": { "build": "tsc -p tsconfig.web.json" } } ``` Then you can run `npm run build`, and it will automatically add all the bins to the path, and allow you to run `tsc` ``` ./node_modules/.bin ../node_modules/.bin ../../node_modules/.bin ... (up the directory tree) ``` Similarly, you can invoke the binary directly with `npm exec`, since again `npm` on the fly modifies the `PATH` variable, and adds the `node_modules/.bin` to it. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2025/07/image-9.png) ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2025/07/image-7.png) ## Monorepos In monorepos using [hoisted mode](https://www.jonathancreamer.com/inside-the-pain-of-monorepos-and-hoisting/), if you want to define your own `"bin"` scripts, you can do so by following the same pattern. ``` { "name": "@foo/my-pkg", "bin": { "foo-cli": "bin/foo.js" } } ``` You then get symlinks to all the various `"bin"` entries created in the root `node_modules/.bin` so you can just call `yarn foo-cli` from the root of your monorepo. For example... ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2025/07/image-11.png) Having the `./bin/index.js` will add an entry in the `node_modules/.bin` which points to the `./packages/midgard-yarn-strict/bin/index.js` ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2025/07/image-12.png) So users of the monorepo can simply run `npm exec midgard-yarn-strict` or `yarn midgard-yarn-strict`. All package managers have some form of this behavior including pnpm... [pnpm run | pnpmAliases: run-script![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/icon/favicon-1.png)pnpm![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/thumbnail/ogimage-1.png)](https://pnpm.io/cli/run?ref=jonathancreamer.com) [pnpm exec | pnpmExecute a shell command in scope of a project.![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/icon/favicon.png)pnpm![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/thumbnail/ogimage.png)](https://pnpm.io/cli/exec?ref=jonathancreamer.com) ### 4 Years at Microsoft URL: https://www.jonathancreamer.com/4-years-at-microsoft/ Last updated: 2025-02-18T22:34:02.000Z I've reached a new career achievement. My tenure Microsoft is officially the longest of any job in my career. The previous record was 3 years and 9 months at Lonely Planet. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2025/02/image.png) What an incredible 4 years it has been too. I thought I'd list out a few learnings in addition to the ones I wrote about [last year](https://www.jonathancreamer.com/3-years-at-microsoft/). ## Scaling is not just about the code Just getting acclimated to the size and scale of Microsoft was quite a thing. Eventbrite during it's biggest time was 1,000 people, and I joined a company that has 250x that. What an adjustment! During the first year at Microsoft, I realized that even though I'd worked on a fairly large codebase, nothing really except for on-the-job training can teach you how to deal with this type of scale. One of the things you can do to prepare yourself for it is to read the following book... ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2025/02/swatg.jpg) #### Software Engineering at Google [Buy this Book](https://amzn.to/413wdeq?ref=jonathancreamer.com) That book is a game changer in terms of understanding scale. What you'll immediately realize is that probably the MOST important aspect of scaling a codebase is having a good connection with the people around you. Which is exactly what I strove to do in my first year at Microsoft. I did the best I could to meet with and stay in contact with as many of the teams around me that I could. I created sync meetings with partner teams in other geos that are still on my calendar to this day. We also setup an RFC process which helped us document and record the changes going on in our engineering system. That deserves an entire blog post in and of itself. It has really helped us capture many of the highly impactful decisions we've made over the last 4 years. ## A few things I'm most proud of *so far* One of the things I'm most proud of is the team I've been a part of. For the most part, the same folks, some of which I helped interview and I've been working with, are still around after all this time! I think that really says something about the type of culture we have in 1JS. Working every day with folks from all over the world really makes work fun and exciting! I've also been lucky enough to visit the folks in India twice which has been an incredibly rewarding experience. I'm proud to be a part of such a diverse group of people. Second, we've gone through unprecedented growth since I've been here on the team. We're now the 3rd largest monorepo at Microsoft behind Windows and Office. There are over 1k developers a month contributing code in our repo, and even with all that churn, the system continues to run very well. Yes, there's lots of room for improvement, but I'm extremely proud of the work we've delivered! One of the coolest things I got to help out on was working with Derrick Stolee on the git sizing issue we've encounter. That was something I wrote about in... [How we shrunk our Javascript monorepo git size by 94%We really did this! We work in a very large Javascript monorepo at Microsoft we colloquially call 1JS. Using some new changes to the git client it went from 178GB to 5GB.![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/icon/jclogo-1.png)Jonathan CreamerJonathan Creamer![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/thumbnail/6C7A2712.JPG)](https://www.jonathancreamer.com/how-we-shrunk-our-git-repo-size-by-94-percent/) That project was incredibly fun and impactful. Speaking of having impact, I've also been able to create a new OSS library for authenticating to Azure DevOps for NPM authentication which is available here. [GitHub - microsoft/ado-npm-auth: This package can automatically use the azureauth CLI to fetch tokens and update a user’s .npmrc file for authenticating to Azure DevOps npm feeds.This package can automatically use the azureauth CLI to fetch tokens and update a user’s .npmrc file for authenticating to Azure DevOps npm feeds. - microsoft/ado-npm-auth![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/icon/pinned-octocat-093da3e6fa40.svg)GitHubmicrosoft![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/thumbnail/ado-npm-auth)](https://github.com/microsoft/ado-npm-auth/tree/main?ref=jonathancreamer.com) That solved a fairly large pain point for many of our users, which is getting their packages installed without having to jump through too many hoops to get authenticated to ADO Artifacts. Another thing I'm super proud of is, I've been able to create a lot of great video based trainings in the last 4 years, and folks have given lots of awesome feedback about it. I figured, as a developer one of my favorite ways to learn is by watching content from places like Frontend Masters or Pluralsight, and I thought, why not try to do some of that on my own? ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2025/02/image-2.png) I created some bite sized videos in "Pluralsight style", and it's been really fun to do, and really rewarding to hear how helpful it's been for folks. ## Can't wait for what's next! There's lots of other things I could talk about, but all in all I'm just so thrilled to still be a part of the same team I joined 4 years ago, and can't wait to see what the future holds. ### Using Typescript in node.js scripts without actually writing Typescript URL: https://www.jonathancreamer.com/using-typescript-in-node-js-scripts-without-actually-writing-typescript/ Last updated: 2024-11-08T18:33:22.000Z Typescript is without a doubt my favorite way to write Javascript in 2024\. However, there are times when I don't want to actually have to run `tsc` or `tsc --watch` to do basic scripting tasks, but I also really want to have type safe Javascript, AND be able to use ES Modules. A few innovations both on the Typescript side, and the node.js side in the last few years have made it way easier to have your cake and eat it too when it comes to writing typesafe Javascript AND getting to use modern ES modules instead of commonjs! ## Use jsdoc for the types [JSDoc](https://jsdoc.app/?ref=jonathancreamer.com) has been around for quite a while now. I remember using it years ago to generate API docs when I built Backbone.js apps. It's very similar in concept to what you'd use in C# apps as well, where you describe what a function or variable does by simply writing a code comment above it. ```javascript /** * Debounces a given function * @param {function} func function to debounce * @param {number} time how long to debounce calls to func for (in milliseconds) * @returns { function } debounced function */ export function debounce(func, time) { // ... } ``` You can use the `@type` to simply set a given JS var as a type. ```javascript export function debounce(func, time) { /** @type { NodeJS.Timeout | null } */ let timeout = null; // ... } ``` You can even define custom types using `@typedef`. ```javascript /** * @typedef { Object } NpmrcOrg * @property { string } feed * @property { string } organization * @property { string } [pat] */ /** @type { Array } */ const defaultFeeds = [ { organization: "foo", feed: "https://foo.dev.azure.com/foo/bar/_packaging/foo/npm/registry/", }, ]; ``` Using the square brackets marks a field as optional, both in `@property` and in `@param` like `[pat]` in the above example. Here we're declaring a type called `NpmrcOrg` and then using it immediately below. You can also reference types from other files, and even from NPM packages using `@import` ```javascript /** * Create the user npmrc given an array of registry urls and a token * @param {object} options * @param {Array } options.feeds * @param {string} options.existingNpmrc * @returns {string} */ const createUserNpmrc = ({ feeds, existingNpmrc }) => { ``` Or referencing a node module... ```javascript import https from "https"; /** * * @param {import("http").RequestOptions} options * @returns */ export const makeRequest = async (options) => { ``` Here are the docs on all the possible things you can do with JSDoc. [Documentation - JSDoc ReferenceWhat JSDoc does TypeScript-powered JavaScript support?![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/icon/icon-192x192-1.png)JSDoc Reference![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/thumbnail/WtqB9dW6SB7c9og9HQ5fINwM8gNdsEyn--WaLQKBW8RKFRKniJQqNU8BKFRqngJQqNLwEZWErzngOVuwAAAABJRU5ErkJggg---1)](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html?ref=jonathancreamer.com) ## Configure Typescript to check Javascript and allow ES Modules There are a few important flags to have in your `tsconfig.json` and `package.json` to make this all work. ```json { "compilerOptions": { "target": "ESNext", "lib": ["dom", "ESNext"], "module": "ESNext", "allowJs": true, "checkJs": true, "rootDir": "./src", "outDir": "./lib", "allowSyntheticDefaultImports": true, "esModuleInterop": true }, "include": ["src"] } ``` The `checkJs` and `allowJs` will instruct both the Typescript compiler, and VSCode to do typechecking on your JS and thus parse all the JSDoc comments into Typescript. The `module` being set to `"ESNext"` is what tells TS that you're going to be using ESModules. If you hate the `import * as` stuff you can set `"allowSyntheticDefaultImports"` and `"esModulesInterop"`. Running `tsc` against that tsconfig will produce JS files in `./lib` along with the `.d.ts` files created from parsing your JS files, and the JSDoc comments. Lastly, in your `package.json` if you want to, you can set `"module": true` that will tell `node` when you run it that you're using ES Modules, and as of a few node versions ago, the `import` and `export` of modules will just work by running `node path/to/your/script.js` Read more about node.js's [ES Modules](https://nodejs.org/api/esm.html?ref=jonathancreamer.com) and about how [Typescript handles ES Modules](https://www.typescriptlang.org/docs/handbook/2/modules.html?ref=jonathancreamer.com) as well. ## This works so well in VSCode The best thing ever is seeing the magic of all this working in VSCode. 😍 You get full intellisense and code completion like you would using native .ts files. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2024/11/image-1.png) showing the param flyover ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2024/11/image-3.png) Showing the full debounce function (thanks to my co-worker Stu for the nice debounce😉) You get the goodness of Typescript without *needing* to do a single `yarn build` or `npm run build` or anything, because, guess what, you're using native Javascript! ## Conclusion Lots of open source tools have moved towards this model, including, but not limited to [webpack](https://github.com/webpack/webpack?ref=jonathancreamer.com), and [svelte](https://github.com/sveltejs/svelte/blob/main/packages/svelte/src/internal/server/index.js?ref=jonathancreamer.com). There are definitely still advantages to using full on `.ts` extensions, and using the `tsc` compiler, or `swc` / `babel` etc to do the transpiling work, but it's nice to know that you don't have to go for a full typescript build for every part of your code base. This type of programming works great for scripting where you just want to automate some tasks, do system level work, write a CLI, etc. The feedback loop is very fast because there's no compile step at all. Go forth and write more native Javascript with type checking! ### How we shrunk our Javascript monorepo git size by 94% URL: https://www.jonathancreamer.com/how-we-shrunk-our-git-repo-size-by-94-percent/ Last updated: 2024-10-28T21:25:45.000Z This isn't click bait. We really did this! We work in a *very large* Javascript monorepo at Microsoft we colloquially call 1JS. It's large not only in terms of GB, but also in terms of sheer volume of code and contributions. We recently crossed the 1,000 monthly active users mark, about 2,500 packages, and \~20million lines of code! The most recent clone I did of the repo clocked in at an astonishing 178GB. ![](https://media.tenor.com/dgpjP5JreugAAAAC/look-at-the-size-of-that-thing-amazed.gif) For many reasons, that's just too big, we have folks in Europe that can't even clone the repo due to it's size. The question is, how did this even happen?! ## Lesson #1 When I first joined the repo a few years ago, I noticed after a few months that it was growing, when I first cloned it was a gig or 2, but after a few months was already at around 4gb. It was hard to know exactly why. Back then I ran a tool called `git-sizer` , and it told me a few things about some blobs that were large. Large blobs happens when someone accidentally checks in some binary, so, not much you can do there other than enforce size limits on check ins which is a feature of Azure DevOps. Retroactively, once the file is there though, it's semi stuck in history. Secondly, it flagged me about our [Beachball change files](https://github.com/microsoft/beachball/?ref=jonathancreamer.com), which we weren't deleting. We use them in the same way that [Changesets](https://github.com/changesets/changesets?ref=jonathancreamer.com) work, accomplishing similar goals as [semantic-release](https://github.com/semantic-release/semantic-release?ref=jonathancreamer.com) where we want to tell the packages how to automatically bump their semver ranges. At times we'd get to 40k of them in a single folder, which we found out causes a large tree object to be created every time you add a new file into that folder. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2024/10/image.png) So, lesson #1 we learned was... > Don't keep thousands of things in a single folder. We ended up implementing two things to help here. One was a [pull request into beachball](https://github.com/microsoft/beachball/pull/584?ref=jonathancreamer.com) which did several changes in a single change file instead of one per package. Second, we wrote a pipeline which runs and automatically cleans up that change folder periodically to stop it from getting so large. Huzzah! We fixed git bloat! ## Lesson #2 ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2024/10/image-6.png) we fixed git bloat! no we didn't Our versioning flow at scale maintains a mirror of `main` called `versioned` which stores the actual versions of packages so we can keep `main` free of git conflicts, and have an accurate view of which git commits correspond to which semver versions we release via NPM packages. (this needs another blog post, but I digress...) I noticed that the versioned branch seeming to get harder and harder to clone because it kept getting so huge. But, we'd dealt with the change file issue, and the only thing going in that `versioned` branch in terms of commits was appends to `CHANGELOG.md` and `CHANGELOG.json` files. ![](https://media.tenor.com/VWbwqhXAS7gAAAAC/hmmm-thinking.gif) batman scratching his chin Time passed on, and our repo, while growing slightly slower, still grew and grew. However, it was sort of difficult to know whether this growth was now due to simply scale, or something else altogether. We were adding hundreds of thousands of lines of code, and hundreds of developers every year since 2021, so a case was to be made that natural growth was occurring. However, once we came to realize that we had surpassed the growth rate of the one of the biggest monorepos at Microsoft, the Office one, we realized, something else must be wrong! That's when we called for backup... ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2024/10/image-1.png) The author of such git features as [git shallow checkout](https://github.blog/open-source/git/bring-your-monorepo-down-to-size-with-sparse-checkout/?ref=jonathancreamer.com), [git sparse index](https://github.blog/open-source/git/make-your-monorepo-feel-small-with-gits-sparse-index/?ref=jonathancreamer.com), and all [kinds of other](https://devblogs.microsoft.com/devops/exploring-new-frontiers-for-git-push-performance/?ref=jonathancreamer.com) features created because of the size of our monorepos in Office, had just re-joined our organization after a stint at Github bringing those features to the **world**. He took a look, and immediately realized something was definitely not right with this growth rate. When we pulled our versioned branches, those branches that only change CHANGELOG.md and CHANGELOG.json, we were fetching 125GB of *extra* git data?! HOW THO?? Welp, after some super deep git digging, it turned out that some [old packing code](https://github.com/git/git/commit/ce0bd64299ae148ef61a63edcac635de41254cb5?ref=jonathancreamer.com#diff-d3b31a15a4dfd94e1201658d9bfc496a8c606d1ae7083cdbbc05e55c615f89ddL495) checked in by Linux Torvalds (ever heard of him 🤷‍♂️) was actually only checking the last 16 characters of a filename when it gets ready to do compression of a file before it pushes the diffs. For context, *usually* git just pushes the *diffs* of changed files, however, because of this packing issue, git was comparing CHANGELOG.md files from two different packages! Stolee explains this well in [here](https://lore.kernel.org/git/pull.1785.git.1725890210.gitgitgadget@gmail.com/?ref=jonathancreamer.com). For example, if you changed `repo/packages/foo/CHANGELOG.json`, when git was getting ready to do the push, it was generating a diff against `repo/packages/bar/CHANGELOG.json`! This meant we were in many occasions just pushing the entire file again and again, which could be 10s of MBs per file in some cases, and you can imagine in a repo our size, how that would be a problem. We were then able to try repacking our repo with a larger window `git repack -adf --window=250` to have git do a better job compressing the pack files for our repo to reduce the size. This did definitely reduce the size of the repo significantly, however, we can do even better! This PR [https://github.com/git-for-windows/git/pull/5171](https://github.com/git-for-windows/git/pull/5171?ref=jonathancreamer.com) added a new way to pack the repo based upon walking git paths as opposed to the default of walking commits. The results are staggering... ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2024/10/image-2.png) I ran a new git clone on my machine yesterday to try the new version of git in [Microsoft's git fork](https://github.com/microsoft/git?ref=jonathancreamer.com) (git version 2.47.0.vfs.0.2)... ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2024/10/image-3.png) And after running the new `git repack -adf --path-walk` ... ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2024/10/image-4.png) Crazy. It went from 178GB to 5GB. 😱 ![](https://media.tenor.com/9CJaHEmyKPAAAAAC/chris-pratt-andy-dwyer.gif) The other new configuration option being added will further ensure that the right types of deltas are generated at `git push` time... `git config --global pack.usePathWalk true` That will make sure your `git push` commands are performing the correct compression. Any developer on the git version 2.47.0.vfs.0.2 can now repack the repo once cloned locally, as well as use the new `git push` path walk algorithm to stop the growth rate. On Github, re-packing and git garbage collection happens periodically, but again, the type of packing which Github does will not correctly compute the deltas of these CHANGELOG.md and CHANGELOG.json files, or potentially any file that has the same 16+ character names which change a lot over time. Think i18n type of large string files and such. Azure DevOps, which we're on, doesn't do any such re-packing, *yet*. So, we're working on getting that done as well so we can reduce the size of the repo on the server side as well. Those changes will all make their way into the upstream of git as well! Hurray for OSS. ## Wrap Up If you work in a large-ish scale monorepo, and you have CHANGELOG.md or really any file that has a relatively long-ish name (>16 characters) which repeatedly gets updated, you may want to keep your eyes on this path walk stuff. You can also try out thew new `git survey` command to see all kinds of new heuristics such as Top Files By Disk Size, Top Directories By Inflated Size, or Top Files By Inflated Size. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2024/10/image-5.png) These heuristics will help give you a sense of whether the path walk work will affect your repo size too. Overall I am so impressed and excited about our commitment to trying to produce solutions that help us scale repositories at Microsoft, but also take those solutions to the rest of the world.. ### Reflecting on 3 years at Microsoft URL: https://www.jonathancreamer.com/3-years-at-microsoft/ Last updated: 2024-02-20T23:33:12.000Z February 16, 2024 marks 3 years for me at my dream job at Microsoft. I've loved Microsoft since long ago when I was learning ASP.net MVC from Scott Hanselman, Jon Galloway, Phil Haack, etc via blogs and tweets in 2012! I've had every Xbox we've made, every Zune we made, and even bought my 2011 Ford Fusion because it had Microsoft sync. 🤣 A couple of things I got into early on in my career were networking, and content creation. These types of activities were instrumental in me getting to land my dream job here! I started with a super basic blog where I wrote down some stuff I was learning about MVC and Javascript, then started speaking at user groups, and ultimately conferences. It's absolutely critical, in my opinion, to learn in the open. The conference speaking I did in early in my career led me to meet amazing folks like [Ben Illegbodu](https://twitter.com/benmvp?ref=jonathancreamer.com) who got me a job working with him at Eventbrite, which in turn led me to work with folks like [Kyle Welch](https://twitter.com/kylewelch?ref=jonathancreamer.com) and [Jamie Kyle](https://twitter.com/buildsghost?ref=jonathancreamer.com) on a large JS monorepo there. I wrote a series of [blog posts on monorepos](https://www.jonathancreamer.com/how-to-successfully-manage-a-monorepo-aka-megarepo/), started a [DivOps movement](https://www.jonathancreamer.com/announcing-div-ops/), and met lots of folks along the way. The blog posts on monorepos directly contributed to me getting discovered by Microsoft! I can't stress enough how important it is to network, create content, and learn in the open. Ok, now I'll reflect on my first 3 years here at Microsoft, and explain why I'm here for the long haul. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2024/02/IMG_2753.jpeg) Proof I'm a Microsoft fan ## 😍 Microsoft believes in OSS In case it wasn't obvious with Microsoft's acquisition of things like Github, NPM, and the amazing VSCode editor, we care *deeply* about OSS. We are encouraged to leverage it, push things to the open source, and work in the open. The OneDrive team owns [Rush](https://rushjs.io/?ref=jonathancreamer.com). If you're not working in the OSS inside Microsoft, it's only because you've chosen not to. Other than that, we're very much enabled to do so. Another thing that blew my mind when I joined the Office Engineering org (now ES365) is, we hit scale issues with git with our Office Monorepo (different than the one we maintain, 1JS). Rather than inventing some net new thing ala , we actually sent developers over to Github to make [git work at scale](https://github.blog/2020-01-17-bring-your-monorepo-down-to-size-with-sparse-checkout/?ref=jonathancreamer.com)! We also built the new [git credential manager](https://www.bing.com/ck/a?!&&p=34786679455c4704JmltdHM9MTcwODM4NzIwMCZpZ3VpZD0zYWJkMGUxNC0wNTMwLTY0OWYtMzM2Mi0wMWY2MDQxYzY1YzcmaW5zaWQ9NTQ5MA&ptn=3&ver=2&hsh=3&fclid=3abd0e14-0530-649f-3362-01f6041c65c7&psq=git+credential+manager&u=a1aHR0cHM6Ly9naXRodWIuY29tL2dpdC1lY29zeXN0ZW0vZ2l0LWNyZWRlbnRpYWwtbWFuYWdlcg&ntb=1&ref=jonathancreamer.com) directly into git also. Inside my direct 1JS team, we contributed directly to NPM with the [Isolated Mode RFC](https://github.com/npm/rfcs/blob/main/accepted/0042-isolated-mode.md?ref=jonathancreamer.com) which brought some of the goodness of pnpm to NPM, and also directly to [pnpm](https://github.com/orgs/pnpm/discussions/6376?ref=jonathancreamer.com) as well. Collaborating with Vincent Bailly on our work of a pnpm like fork of Yarn called [midgard-yarn-strict](https://www.npmjs.com/package/midgard-yarn-strict?ref=jonathancreamer.com) has been one of the highlights of my career! Our task runner [Lage](https://github.com/microsoft/lage?ref=jonathancreamer.com) ([the one that inspired Turborepo](https://github.com/vercel/turbo/pull/370?ref=jonathancreamer.com)), is also open source, as well as many of the other tools we use for maintaining a monorepo such as [Beachball](https://github.com/microsoft/beachball?ref=jonathancreamer.com), [workspace-tools](https://github.com/microsoft/workspace-tools?ref=jonathancreamer.com), [Backfill](https://github.com/microsoft/backfill?ref=jonathancreamer.com), and many others. ## 🐋 The Scale Microsoft has 250,000 employees or something like that as of 2024, a great deal of which were hired after 2020. On the team I work at, 1JS, we maintain a Javascript monorepo which has reached 1,000 active monthly users. Our engineering system ships thousands of commits to production every single week. If I don't git pull for a few days I'll pull down several hundred, possibly thousand commits. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2024/02/image.png) This means you have to think a little harder about certain workflows you make changes too, features you add, etc because you're literally affecting the day to day lives of about 1,000 engineers. 😅 It's super important to keep your customers at the forefront of your mind because there are a lot of them. The scale was definitely something that really surprised me coming from Eventbrite where we had \~90 engineers using the system compared to the 1,000 here! ## 🦄 My team is seriously awesome My direct team of 7 alone, the one I interact with daily, is based in Vancouver, I work in Nashville, but extends all the way to eastern Canada. We represent 5 countries, India, Bangladesh, Mexico, US, China, and Canada. That's just my team of 7\. The team has an incredible culture, we genuinely care for each other first, our jobs second, and we never shy away from a task. We're in the process of uniting two huge engineering systems into one, and it has been a tough gig, but we've all learned SO much together, and we're mostly the same core team over the last 3 years. If you include the broader x-geo 1JS v-team (virtual team), that begins to include, Norway, France, Kenya, and I'm sure a couple others as well. That type of diversity of thought brings an incredibly fresh perspective on things! In 2022 I got to travel to London for a conference, Remond for a team offsite, and Hyderabad, India for another team offsite. That was a heck of a year. 😄 I'm so proud to be a part of such an incredible team. I've made lifelong friends here, and had a ton of fun with them! ## 🥰 It's big, but somehow feels small One of the things I'm most impressed by is, despite how large the company is, I am never overwhelmed by the size. The organization structure lends itself well to feeling a part of your immediate organization. My manager's manager (skip), is amazing, and does such a great job keeping our org communicating, encouraging us to use our time off well, and really makes us feel included in the broader Microsoft ecosystem. The leadership all the way from my org, our parent org, which does Engineering Systems for all of Office, and up into Office itself does such a great job of making us feel like people, and not like cogs in a machine. ## 💻 Yes, I have a mac too One that I get a lot is, "oh you work at Microsoft, you must love working on Windows", while *yes* I actually do love it, ala [working with WSL](https://www.jonathancreamer.com/the-javascript-developers-guide-to-ubuntu-on-windows-11-with-wsl/), I actually am writing this post on my Microsoft provisioned Macbook Pro. I frequently take meetings on my iPad with Teams (which is a great teams device), and I answer emails on my iPhone 15 pro max with the awesome Outlook iOS app. Microsoft products run everywhere y'all! ## 🚀 Here's to the future I couldn't be more proud of being a part of my team, and Microsoft. I am very excited the work I'm doing now, and can't wait to see where it takes me over the next few years of my career and beyond! No company is perfect, but I am sure glad to be at Microsoft! ### The JavaScript Developer's Guide to Ubuntu on Windows 11 with WSL URL: https://www.jonathancreamer.com/the-javascript-developers-guide-to-ubuntu-on-windows-11-with-wsl/ Last updated: 2024-01-05T16:34:09.000Z This is a bit of a re-hash of an [earlier post](https://www.jonathancreamer.com/setting-up-wsl2-and-the-vscode-containers-plugin/) on setting up WSL in Windows. It is just so easy now, that I wanted to make a whole new post. It's seriously as easy as `wsl --install` now! Seriously just check this out... 👇👇 ### Switched to the Shure MV7 from the Blue Yeti URL: https://www.jonathancreamer.com/switched-to-the-shure-mv7-from-the-blue-yeti/ Last updated: 2023-11-27T21:45:00.000Z While I have loved my 9 years with the Blue Yeti, the time has come to make a change. I upgraded to the amazing [Shure MV7](https://bit.ly/shure-mv7-usb?ref=jonathancreamer.com) over Thanksgiving break. Give a listen to how good this thing sounds! ### Learning how to Learn URL: https://www.jonathancreamer.com/learning-how-to-learn/ Last updated: 2023-10-05T20:47:51.000Z As silly as it sounds, the ability to learn is not necessarily easy. We all as engineers put ourselves in a state where we put guilt on ourselves when we stop what we're doing, and learn something because it feels like that's not "work". 💡 "Be passionate and bold. Always keep learning. You stop doing useful things if you don’t learn" Satya Nadella Microsoft CEO The reality is, learning is everything, we should be perpetual learners in every aspect of work, and life. I'd even go so far as to say we're doing our company's a disservice by NOT taking time to learn! The big question then is, "how do I learn how to learn?!" ## Create systems not goals I heard Cory House reference a book a few years ago in his keynote at Connect.tech called "How to Fail at Everything and Still Win Big" and one idea in that book says that instead of trying to create new goals such as "read x number of books" or "exercise more", we should create systems that enable us to make those goals a reality. We should approach learning that way! Rather than saying things like, "I want to learn a new language like Rust", or "I want to read 3 new books this year", we should create a system for ourselves that allows us to complete those goals. For example, use an app such as To Do, Todoist, Apple Reminders, and create yourself a morning checklist that covers a daily few things you want to do. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2023/05/image.png) If you want to learn a new technology, block off time on your calendar daily or weekly in order to spend a bit of time reading a book about it. Take an hour every Friday to stop what you're doing, read a book, watch some content, etc. ## Learning Resources There are so many resources out there for developers to learn from, but here are a few of my favorite ### Start a blog Wait wait? A blog? YES! One of the absolute BEST ways to learn is simply to write down things down. I love the quote, "*Writing is thinking".* It's so true, often times taking the time to write down something that you've been learning and thinking about will offer clarity and understanding that you didn't expect. It doesn't have to be a complicated blog setup, it's 2023, use Wordpress, Github Pages, Azure Static Webapps, Notepad, just something to get your mind working. ### Social Media This one sometimes makes people giggle, but the reality of it is, Twitter is still a massive source of great free content. Social media, in spite of its obvious pitfalls, is still a great place to go to consume new ideas, and occasionally when the mood strikes, put your own new learnings and ideas out there. Use Tweetdeck to create a List of top news sites that cover your interests, and add a column for it. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2023/05/image-1.png) ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2023/05/image-2.png) You can even follow certain hashtags if you want, and join them with an `OR`. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2023/05/image-3.png) ### Online courses Occasionally a company you work for will offer a free education site like [Pluralsight](pluralsight.com), [Frontend Masters](https://frontendmasters.com/?ref=jonathancreamer.com), [Egghead.io](https://egghead.io/?ref=jonathancreamer.com), [Udemy](https://www.udemy.com/?ref=jonathancreamer.com), [LinkedIn Learning](https://www.linkedin.com/learning/?ref=jonathancreamer.com) and more, but even if they don't offer it to you as a free resource, consider joining one of them anyways! Most of them only cost roughly $30/month, and are full of great content. Here is where you could leverage that hour block of time you schedule on Friday's to go watch some courses on that new language, or learn more about advanced React Hooks usage. Whatever that thing is, take the time to go watch some content on it. ### News Sites and Newsletters [Dev.to](http://dev.to/?ref=jonathancreamer.com) is one of the best ways to get a stream of content, and also turn around, and write about your own thoughts and ideas. There are lots of great online news sites like Reddit's [r/javascript](https://reddit.com/r/javascript?ref=jonathancreamer.com), or [r/rust](https://www.reddit.com/r/rust/?ref=jonathancreamer.com). If that's all too overwhelming, just go subscribe to one of the dozens of great newsletters out there that aggregate all that data into a weekly digest. [JavaScript Weekly: The JavaScript Email Newsletter![](https://javascriptweekly.com/favicon.png)](https://javascriptweekly.com/?ref=jonathancreamer.com) [React Status![](https://react.statuscode.com/favicon.png)![](https://react.statuscode.com/images/reactkeith.png)](https://react.statuscode.com/?ref=jonathancreamer.com) [Node Weekly![](https://nodeweekly.com/favicon.png)](https://nodeweekly.com/?ref=jonathancreamer.com) [ES.next News: the latest in JavaScript and cross-platform tools![](http://esnextnews.com/favicon.ico)Logo ES.next News![](http://esnextnews.com/images/logo-es-next-news.png)](http://esnextnews.com/?ref=jonathancreamer.com) ## Podcasts ## Go forth and learn Learning is key to growth. Make sure to create the systems you need to foster learning as a part of your day to day life. You will better yourself, better your company, and better those around you. ### How to spend a week in Madrid URL: https://www.jonathancreamer.com/enjoying-a-few-days-in-madrid-spain/ Last updated: 2025-08-08T16:04:48.000Z As I've traveled for work and conferences, I've learned how to find amazing things to do. One place my wife and I enjoyed a bunch when I had to do a trip working for Eventbrite was Madrid, Spain. ## Things to eat and drink in Madrid Paella is a good Spain is known for. There’s no shortage of good Paella places, so just look around, or ask a local where their favorite Paellea spot is! Churros are a Madrid staple, and the best place to try them is at [Chocoleteria San Gines](https://maps.app.goo.gl/PV836JxrbowraSZA6?ref=jonathancreamer.com)... One of my favorite dishes we discovered at several restaurants was [Huevos Rotos](https://theculturetrip.com/europe/spain/articles/an-introduction-to-huevos-rotos-madrids-delicious-egg-and-potato-dish/?ref=jonathancreamer.com). It's potatoes topped with Iberian Ham, and eggs. There are several different mercados in the central part of Madrid, and what's nice is, it's basically a food court that has many different vendors so you can try all sorts of foods. One great one is [Saint Ildephonsus Market](https://maps.app.goo.gl/Bj9fRtTQuYnJXy489?g%5Fst=ic&ref=jonathancreamer.com). There's a super cool bar called [Bodega de la Ardosa](https://maps.app.goo.gl/qRLkjgNyTH5mfE9N7?g%5Fst=ic&ref=jonathancreamer.com) where you can sip the local favorite lager, Mahou. It's most often served in small glasses so that the beer is always served cold. It may seem odd to get a burger in Spain, but one of the various [Goiko](Goiko +34 910 60 72 27 https://maps.app.goo.gl/egBHz6jjg2F9uBk96?g%5Fst=ic) burger joints won't disappoint. There's a lot of great rooftop spots such as [La Terraza del Urban](https://maps.app.goo.gl/ynheKiSAacxqk9dm8?g%5Fst=ic&ref=jonathancreamer.com) which give you a great view of the streets below, and of the gorgeous Madrid sunset. **Things to Do** You have to see the spectacular [Royal Palace of Madrid](Royal Palace of Madrid +34 914 54 87 00 https://maps.app.goo.gl/kArTAN8v9xRiWFfE8?g%5Fst=ic) and it's gardens. They do tours if you want to go inside the palace, but the gardens alone, which are free, are a sight in and of themselves. Speaking of things you have to see, [Crystal Palace](Palacio de Cristal +34 917 74 10 00 https://maps.app.goo.gl/kGjRP6pvFkLQEUGM7?g%5Fst=ic), and it's surrounding park is also another must see sight. [Plaza Mayor](Plaza Mayor https://maps.app.goo.gl/tT2TfQVvzL7TgJYb9?g%5Fst=ic) is at the center of the downtown area, and is always packed with visitors, shops, and great food. One of the main shopping streets is [Calle De Fuencarral](Dropped pin https://maps.app.goo.gl/QsJQa81Dvwv6u2Cx6?g%5Fst=ic), and you'll find tons of great things to shop for down that long street. ### Tips for writing nodejs scripts using only built-in modules and async URL: https://www.jonathancreamer.com/write-async-node-scripts-using-the-only-native-nodejs-modules/ Last updated: 2023-01-09T17:14:01.000Z For the last few years, I've spent a huge majority of my time writing scripts in nodejs as my default scripting language. I've dabbled with bash a bit too, but it's still so weird, and hard to test, and I'm such a Typescript fan that I generally prefer to go the typescript route as often as possible. Below is a list of tips to make writing nodejs scripts fun, fast, and easy. ## Use ES Modules Yes! You can do it now! However, [thar be dragons](https://nodejs.org/docs/latest-v16.x/api/packages.html?ref=jonathancreamer.com#determining-module-system). In general tho, if you want to use ES modules in node, you can... 1. Name your file with `.mjs` on the end of it 2. Use a `.js` file in a place where the closest `package.json` has `"type": "module"` 3. Use `node --eval` and pass `--input-type=module` Similar rules apply to keeping a file use `commonjs` 1. Name your file with `.cjs` 2. Use a `.js` file in a place where the closest `package.json` has `"type": "commonjs"` 3. Use `node --eval` and pass `--input-type=commonjs` **There are a couple of provisos**, a, ****a couple** of ****quid** pro quos ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2023/01/genie.gif) When you switch to using ES Modules in node, you have to: - Always include the extension in your imports... ```js import { foo } from "./bar.js"; ``` ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2023/01/image-3.png) - You have to use `import()` instead of `require` - This doesn't work... `__dirname` But, this does... ```js import path from 'node:path'; import { fileURLToPath } from 'node:url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); ``` You might also run into issues with *some* of the `node_modules` that you import, so, just be careful. But, if you're generally sticking to native node modules and utilities anyways, then you should be pretty good to go. ## Use top level await This became available in 14.8\. For the longest time, I'd always write a wrapper every time I got ready to write a node.js script like... ```js const main = async () => { // await some stuff in here }; main().catch((err) => { console.error(err); process.exit(1); }); ``` I did that so often that I wrote VSCode snippets for it. Heh. But, cool thing is, you can just `await` anywhere in a JS file now. **NOTE: you can only do this in a "module" context, i.e. see "Use ES Modules" above** ```js import fs from 'node:fs/promises'; const pkg = await fs.readFile('./package.json'); console.log(JSON.parse(pkg).name); ``` ## Use the new-ish promise based APIs along with the node prefix As of node v14.18 and v16, you can now require *native* node modules, i.e. those things like `path`, `fs`, etc which are built into node, by using the `node:` protocol in the import or require statement. This will prevent script writers from accidentally importing 3rd party versions of utilities. ```js import fs from "node:fs/promises"; import stream from "node:stream/promises"; import dns from "node:dns/promises"; import timers from "node:timers/promises"; ``` Also, for everything else that takes a standard node `(err, results) => {}` callback, you can always fall back on the `promisify` method. ```js import fs from 'node:fs/promises'; import { exec as _exec } from 'node:child_process'; import { promisify } from 'node:util'; const exec = promisify(_exec); const out = await exec('git status'); console.log(out.stdout); ``` ## Use child\_process effectively The `child_process` module is one of the most useful modules in a script writer's backpack. You can run any arbitrary thing on the machine using its `spawn` or `exec` methods. However, knowing when, and how to use either of them, is essential. The most fundamental difference between `spawn` and `exec` is... `spawn` creates and returns a child process that you can interact with easily via streams `exec` creates a runs a subprocess returning the output and error streams with a max of 200kb Therefore, most of the time `exec` will do what you need, but if you need some sort of interactivity or have a bunch of output, then you can use `spawn`. One of the previous examples above used `exec` to call `git status`. Another example could be an abstraction over some sort of `unix` method like `find`. ```js import fs from 'node:fs/promises'; import { exec as _exec } from 'node:child_process'; import { promisify } from 'node:util'; const exec = promisify(_exec); const [,, filename, directory] = process.argv; const out = await exec(`find ${directory} -type f -name ${filename} `); console.log(out.stdout); // run with... // node ./script.mjs filename ./directory ``` Here's an example of using `spawn` to do something. In this case, we're going to spawn an `npm install` in a different package in our repo. ```js import { spawn } from 'node:child_process'; const install = () => { return new Promise((resolve, reject) => { const proc = spawn('npm', ['install'], { stdio: ['inherit', 'pipe', 'pipe'], env: { ...process.env, } }); proc.stdout.on('data', (data) => { if (data.includes('npm WARN')) { // Do something } }); proc.stderr.on('data', (data) => { console.error(`stderr: ${data}`); }); proc.on('close', (code) => { if (code !== 0) { return reject(code); } resolve(); }); }); }; try { await install(); } catch(e) { console.error("Something went wrong with the install."); process.exit(1); } ``` First thing to note here is that we're going create a function and use the `Promise` constructor in the `return`. That way we can `await` it at the top level. Let's take a look at the options we're passing to `spawn`. (You can see the entire API of spawn [here](https://nodejs.org/api/child%5Fprocess.html?ref=jonathancreamer.com#:~:text=The%20child%5Fprocess.spawn%20%28%29%20method%20spawns%20the%20child%20process,the%20spawned%20process%20either%20exits%20or%20is%20terminated.)) The first is the top level command, in this case `npm`. The second option is an array of the arguments we want to pass to `npm` in this case just `install`. But, if we wanted pass multiple things to `npm`, we'd need to add them each as elements to that array such as `['install', '--save-dev', '--legacy-peer-deps']`. The third argument is the `options` object. By default, when you spawn a process, the `stdio` option is set to `pipe`. The `pipe` option means that the `proc` which is returned from `spawn` will have `proc.stdout`, `proc.stdin`, and `proc.stderr` on it. These are `streams` so that you can use methods like `on` and `one` to respond to events such as `data`. The `stdin` argument can either take `pipe`, `inherit`, or an array corresponding to which parts of the process you want to control... If you don't *need* to use `stdin` or `stderr` for example, you can switch from `pipe` to `inherit`. ```js stdin: [ 'inherit' /* stdin */, 'pipe' /* stdout */, 'inherit' /* stderr */ ], ``` Passing `stdin` like that 👆 would provide `proc.stdout` but not `proc.stdin` or `proc.stderr`. Lastly, you can watch for the process you're spawning to close by using `proc.on('close', () => {});`. One more nice option is that you can control which working directory the process is spawned in via the `cwd` option. This allows you to use either `process.cwd()`, or set some other path where you actually want to spawn the process. ## Use Typescript for an even cooler script writing experience Generally I prefer to use Typescript when writing just about anything these days, including scripts. There's a couple of different ways when writing scripts like this which you can use to leverage Typescript for the extra security blanket of checks in your code. First of all, if you're going to be using Typescript and Node, make sure that you have the `@types/node` package installed as a `"devDependency"`. That will ensure that all the native modules have correct type information. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2023/01/image.png) Intellisense for parameters ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2023/01/image-1.png) Intellisense for options That in and of itself is super useful. What's really worth noting though is that, you don't HAVE to have your file end in a `.ts` extension. You can add a couple of options to a `tsconfig.json`. They are `"allowJs"` and `"checkJs"`. Set them both to true, and you're good to go. Typescript as of the [4.7 release](https://devblogs.microsoft.com/typescript/announcing-typescript-4-7?ref=jonathancreamer.com) now supports ES Modules too by reading the `"type": "module"` that can be added to `package.json` files. And you can also manually tell Typescript to use modules by setting the `"module"` option to `node16` or `nodenext`. All that is documented [here](https://www.typescriptlang.org/docs/handbook/2/modules.html?ref=jonathancreamer.com#es-module-syntax). If you want to use `.ts` instead of using allowJs and checkJs, you'll probably also want to install something like `@babel/register`, or `ts-node`, maybe even [esbuild-runner](https://github.com/folke/esbuild-runner?ref=jonathancreamer.com). I still personaly prefer the [Babel route](https://www.jonathancreamer.com/running-programs-with-babel-node/). ## Conclusion In the world of Frontend and DevOps aka [DivOps](https://www.jonathancreamer.com/announcing-div-ops/), writing scripts is inevitable. Writing scripts in Javascript / Typescript allows you to use the JS skills you have as a Frontend dev to interact with the machine to do all kinds of automations, pipelines, etc. And bonus points because you can also write unit tests too. Go forth and write all your scripts in JavaScript! ### Calling an API with authorization using native nodejs with zero external dependencies URL: https://www.jonathancreamer.com/calling-an-api-with-authorization-using-native-nodejs-with-zero-external-dependencies/ Last updated: 2022-08-19T19:17:48.000Z Fetching from an API is one of the most repeated tasks for anyone writing JavaScript backends with node.js. There are dozens of libraries for doing this including `node-fetch`, `isomorphic-fetch`, `axios`, etc etc, however, there's actually a couple of native ways that are fairly simple to use and require no external dependencies at all. Let's talk about a few of them! ## The 'https' way In a world prior to node 18, many people may or may not realize that it's actually not *that* hard to simply use the built in node.js `https` module to do a simple API request with ZERO external dependencies. ```javascript const https = require("node:https"); const request = (options) => { return new Promise((resolve, reject) => { const req = https.request(options, (res) => { let data = ""; res.on("data", (d) => { data += d; }); res.on("end", () => { data = JSON.parse(data.toString().trim()); if (res.statusCode === 200) { resolve(data); } else { reject(new Error(`Error code: ${res.statusCode}.`)); } }); res.on("error", (error) => { reject(new Error(error)); }); }); req.end(); }); }; const auth = `Basic ${Buffer.from(`${user}:${password}`).toString("base64")}`; request({ hostname: "foobar.com", port: 443, path: "/api/users/1", method: "GET", headers: { Accept: "application/json", Authorization: auth, }, }) .then((data) => { console.log(data); }) .catch((error) => { // Do something with the error }); ``` Here you can see we used the native via `require("node:http")`. You can optionally start by creating your own function that returns a promise, here above there's a `request` function of our own. The `https.request` function returns an instance of the `ClientRequest` class. And takes options, and a callback function. The callback function contains a parameter for an instance the `IncomingMessage` class, or basically, the response. You can use the `res.on("data", callback)` like you would when you're reading a stream. There's a few event handers you can listen to, build the full response data by just building up a big response string. Then in `res.on("end", callback)` you can check the status code, and send back data as JSON or however you want to send back data to the calling function by calling `resolve` and resolving the promise. If anything goes wrong such as the status not being 200, you can `reject` with an error, or a string, just something to let the caller know what's going on. There's also an `res.on("error", callback)` handler in case something else goes wrong in the response. Finally, you just call `req.end()`. Now you have your own custom `request` function to make API requests with! The last piece is just building up the correct `Authorization` headers to send along with the `headers` option. That's often just a base64 encoded string in the format of `username:password` along with `Basic ${auth}`. As seen above... ```javascript const auth = Basic ${Buffer.from(${user}:${password}).toString("base64")}; ``` ## The 'fetch' way For a while now, the `fetch` API has been available in most [browsers](https://caniuse.com/fetch?ref=jonathancreamer.com). However, it wasn't until recently that you have access to the `fetch` API in node. As of [18.0.0](https://github.com/nodejs/node/blob/main/doc/changelogs/CHANGELOG%5FV18.md?ref=jonathancreamer.com#fetch-experimental), you are now able to simply use `fetch` in basically the same way you would in the browser. In 17.5.0 you could use the `--experimental-fetch` flag, but that will no longer be needed in 18+. ```Javascript fetch("https://foobar.com/api/users/1", { headers: new Headers({ 'Authorization': `Basic ${Buffer.from(`${user}:${password}`).toString("base64")}`, 'Content-Type': 'application/json' }), }) .then((resp) => { if (response.status >= 200 && response.status <= 299) { return response.json(); } else { throw Error(response.statusText); } }) .catch(() => { return resp.text(); }) ``` Well, that was a lot simpler. That's thanks to the work of the folks who put together the new `undici` [library](https://undici.nodejs.org/?ref=jonathancreamer.com#/) that powers fetch in node! ## Conclusion tldr; if you're in node 18, use fetch, otherwise, use https. The key takeaway here is that you *probably* don't need to add some big fetching library just to make a simple HTTP request in node. It's simple enough to make your own thing. ### Running Typescript scripts in node using esbuild URL: https://www.jonathancreamer.com/running-typescript-scripts-in-node-using-esbuild/ Last updated: 2022-07-11T18:33:17.000Z Typescript is hands down my favorite way to write Javascript these days, but sometimes when I just want to run a quick JS script to do something, I'll still reach for `node ./foo/script.js`. I'll sometimes reach for `babel-node`\`, or `@babel/register`, but that can be pretty difficult to configure sometimes, and has some perf issues. It does work though, and I have a post on it... [Running Typescript programs with babel-node aka how does babel register workRunning Typescript programs on demand with babel-node basically uses babel register, but how does babel register work? This post will detail it.![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/size/w256h256/2021/03/jclogo-1.png)Jonathan CreamerJonathan Creamer![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2020/12/IMG_7214.JPG)](https://www.jonathancreamer.com/running-programs-with-babel-node/) However, I recently discovered that ESBuild has a similar mechanism to babel-node called `esbuild-runner`. [esbuild-runnerSuper-fast on-the-fly transpilation of modern JS, TypeScript and JSX using esbuild. Latest version: 2.2.1, last published: 10 months ago. Start using esbuild-runner in your project by running \`npm i esbuild-runner\`. There are 13 other projects in the npm registry using esbuild-runner.![](https://static.npmjs.com/1996fcfdf7ca81ea795f67f093d7f449.png)npm![](https://static.npmjs.com/338e4905a2684ca96e08c7780fc68412.png)](https://www.npmjs.com/package/esbuild-runner?ref=jonathancreamer.com) It's SUPER easy to use as well. ``` > npm i esbuild esbuild-runner ``` Then, anytime you run a `.ts` file, you do... ``` node -r esbuild-runner/register ./path/to/script.ts ``` And it's stupid fast... ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2022/07/image-1.png) I hope to employ `esbuild-runner` in some more local developer tooling so developers don't have to pre-compile or build their tools. ### Getting around London for the first time URL: https://www.jonathancreamer.com/getting-around-london-for-the-first-time/ Last updated: 2022-04-01T15:22:00.000Z After having travelled to London a few times now, I wanted to share some tips for how to get around. ## Transport for London The [TFL](https://tfl.gov.uk/?ref=jonathancreamer.com) (Transport for London) has many great options for how to navigate the city. The most obvious is the tube aka the Underground aka "please mind the gap" (which you'll here approximately 700,000 times). ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2022/04/image.png) As of a couple of years ago, they started supporting contactless payments, so if you have Apple or Android Pay, and/or a contactless credit card, you can go ahead and use one of those options. Just make sure to tap IN on your way down to a tube station, and tap OUT when you leave on the other end. If you don't want to use your phone, you can buy an Oyster card for a few quid at the airport, or various stops, and top it off as you need more money at any tube station in town. To get somewhere fast-ish, but less direct, you can use the tube, and to get somewhere less fast, but more direct you can use the double decker buses. They're both TFL, and your contactless will work there too. Just keep in mind for buses you only have to top on, not off. The price difference is £2.50 for tube, £1.65 for buses (within zone 1, which is most likely the zone you'll be navigating, the prices go up the farther outside the city you venture). Keep in mind that **rush hour is from 8-10 and 4-6,** and during that time, the tubes can be really really busy. They even have bikes you can rent. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2022/04/image-28.png) ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2022/04/image-29.png) Riding in the top is great! Just make sure to hold the railing going up, they take off quickly. You can also press the stop button when you need to get off. You can also use your contactless / Oyster on the Uber Boats to ride around to various stops along the Thames up and down all the way to Greenwich. ### Apps to use for navigating the transport system You can see a [tube map](https://tfl.gov.uk/maps/track/tube?ref=jonathancreamer.com), but the best 2 ways to actually know where/when/how to get around is to use either: - [Citymapper](https://apps.apple.com/us/app/citymapper-all-your-transport/id469463298?ref=jonathancreamer.com) \- Probably the best because it shows the costs / times of ALL travel options, really cool - [Google Maps](https://apps.apple.com/us/app/google-maps/id585027354?ref=jonathancreamer.com) \- I still bounced around a bit between this and citymapper - [TFL Oyster](https://tfl.gov.uk/fares/how-to-pay-and-where-to-buy-tickets-and-oyster/tfl-oyster-and-contactless-app?ref=jonathancreamer.com) \- This lets you register your contactless and oyster cards to see your journey history, and top off on the go - [TFL Go](https://apps.apple.com/us/app/tfl-go/id1419541638?ref=jonathancreamer.com) \- Up to date maps and travel times Another tip for Google Maps, when you click on any item, you can add it to a "Want to go" list, that shows it as a green flag on the map, then after you visit, you can add it to "Saved places". It's a nice visual sort of to-do list. You can also add the Transit layer to see all the tube map stops. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2022/04/image-3.png) Here's an overview of the different neighborhoods / boroughs of London also... ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2022/04/image-9.png) ## Currency The UK uses GBP's which is roughly 1.3 USDs. Most places take cards, so you probably won't need much cash on hand, but you can stop by any bank in town and use an ATM to get some out if you want, they're really nice looking notes too. ## Conclusion Overall the travel system in London is fantastic, but still a lot to understand for the first visit, so hopefully all this helps! I'm also working on a Top things to do in London by Neighborhood post, but there's sooo much, that it's going to take a bit. ### Inside the pain of monorepos and hoisting URL: https://www.jonathancreamer.com/inside-the-pain-of-monorepos-and-hoisting/ Last updated: 2023-11-29T01:22:44.000Z Working in a monorepo comes with a long list of pros, and a few cons. One of the most painful cons, when it comes to working in a specifically JavaScript based monorepo, is the pain that comes from hoisting. Here's a video that talks about it, and you can also keep reading to learn more! ## What's this hoisting thing? ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2021/12/image-5.png) As we all know, node\_modules is a deep dark place with lots and lots of stuff. That problem is even more massive in a large monorepo. Let's take a step back for a sec and take a look at what happens when you `require` something. ### node module resolution If you take a read through [the docs](https://nodejs.org/api/modules.html?ref=jonathancreamer.com#all-together), you'll find this... ``` require(X) from module at path Y 1. If X is a core module, a. return the core module b. STOP 2. If X begins with '/' a. set Y to be the filesystem root 3. If X begins with './' or '/' or '../' a. LOAD_AS_FILE(Y + X) b. LOAD_AS_DIRECTORY(Y + X) c. THROW "not found" 4. If X begins with '#' a. LOAD_PACKAGE_IMPORTS(X, dirname(Y)) 5. LOAD_PACKAGE_SELF(X, dirname(Y)) 6. LOAD_NODE_MODULES(X, dirname(Y)) 7. THROW "not found" ``` What this is basically saying is... If you require `X`, see if it exists in node, things like `fs`, `child_process`, etc. If you start `require('/X');` you're at the file system root. If you `require('./X');` then see if `.X` is file, then a directory. *...and this is the interesting one...* If you do `require('X');` and it's not a node thing, then keep traversing the file system looking in every `node_modules` along the way. ### Hoisting Package managers such as `yarn` and `npm` implemented a hoisting algorithm as a part of their different `workspaces` implementations. What this hoisting does is, it scans your `package.json` files across your workspaces and figures out what the most common versions of dependencies are. If you have 50 packages, and 47 of them are using react 16.9.0, but 3 are using 16.13.0, it'll "hoist" the common version of react, 16.9.0 to the top level `./node_modules` directory. That way you don't have to have 50 different versions of react in your project, the `import React` or `require('react')` calls will simply pull from the root `node_modules/react` one in the case of 16.9.0, or `./packages/*/node_modules/react` for the 3 cases of 16.13.0. Voila, space saved. However, the plot thickens... ### Monorepo structure So, *most* monorepo structures have a root `package.json`, and a `packages` folder. ``` ./packages/foo/ ./packages/foo/node_modules ./packages/bar/ ./packages/bar/node_modules ./package.json ./node_modules ``` Let's say we're working with the `./packages/foo/` and it does an `import React from 'react';`. Following the node module resolution from above, it'll eventually look in the `./packages/foo/node_modules` directory. If it's *not* there, it'll look at `../packages/node_modules/`, which definitely should not be there, and then it'll look at the `./node_modules` directory and see if it's there. Ok, so that seems fine, where can we go wrong here? Well, a couple of different bad things can happen here. Let's say the `foo/package.json` has `"react": "^16.9.0"`. Then, let's say over in `bar`, they forgot to add react in the package.json, but in `bar/src/index.ts` someone does an `import React...`, and it also happens that `bar` used a feature that only exists in the `16.13.0` version of React. What's gonna happen? Welp, because of the fact that `bar` doesn't have a `react` in its `package.json`, the node module resolution algorithm will kick in and look up to the `../../node_modules/` directory and grab react from there. But OH NOES, that code won't work at runtime because the code written in `bar` needs that fancy new feature in React 16.13! This is a problem that is also nicely summed up in the Rush docs as a "[Phantom Dependency](https://rushjs.io/pages/advanced/phantom%5Fdeps/?ref=jonathancreamer.com)" and a "[Doppleganger](https://rushjs.io/pages/advanced/npm%5Fdoppelgangers/?ref=jonathancreamer.com)". ### Real world ouchies Here's a real world example of how this came into play in a recent release pipeline failure. In the monorepo supported by my team, the most common version of `jest-environment-jsdom` that was hoisted to the root `node_modules` folder in the repo was `24.9.0`. A pull request when in which added `jest-environment-jsdom` 26.10.0 to one of the packages in the `./packages` folder. Well, what happened was, there were a couple of other places using that same version across the repo so, `yarn` in its attempt to de-duplicate by hoisting decided to switch the hoisted version to `26.10.0`! Here's where things got bad. Let's say the package which added `jest-environment-jsdom` was called, `cool-button`. The pull request for adding the new dependency will get pushed to CI, and the CI server does a check for all the places where `cool-button` is used, and start running tests, builds, etc on the dependencies in the repo to make sure that the changes `cool-button` doesn't break any of it's downstream dependencies. Ok, PR is green, all looks great! Well, let's say there's another package called `cool-color-picker`. And `cool-color-picker` had some tests which was more or less like... ```JavaScript it('should pull from memory storage when localStorage isnt there', () => { expect(storageUtilThing.get('item')).toBeTruthy(); }) ``` Well, in this crazy insane case... `cool-color-picker` *was* relying on the hoisted `jest-environment-jsdom` version, 24.9.0\. Well, that particular version used jsdom `11.11.0`. In that particular version of `jsdom` there was NO local storage support in the `jsdom` environment, so the test would pass. However! In the new version of 26.10.0 of `jest-environment-jsdom`, the version of `jsdom` gets bumped to `16.x.x` and it just so happens every so ironically that version `11.12.0` of `jsdom` implemented localStorage. See [Changelog](https://github.com/jsdom/jsdom/blob/master/Changelog.md?ref=jonathancreamer.com). ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2021/12/image-6.png) All the sudden, the test is now failing because the test had previously assumed that there was no localStorage support in jsdom without some kind of 3rd party mocking like `jest-localstorage-mock`. What's worse here is, because `cool-color-picker` isn't in the dependency graph of `cool-button`, it never got tested as a part of the pull request. Therefore, the change landed and attempted to go through the release pipeline. THANKFULLY, although also painfully, the release pipeline currently does a "build the world" strategy regardless of what changed. It's here where the release pipeline which always builds all the packages that the failure happened and broke releases for a few hours until we figured out what happened which involved LOTS of code spelunking. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2021/12/cave-dive.gif) ## So what next? Well, in an ideal world we'll soon be switching to a "strict" package manager like the one we're currently building called [midgard-yarn-strict](https://www.npmjs.com/package/midgard-yarn-strict?ref=jonathancreamer.com), which will eventually migrate much of it's feature set into the implementation of [Isolated Mode](https://github.com/npm/rfcs/pull/436?ref=jonathancreamer.com) in NPM. Yarn v2 or later can also solve this problem with plug and play, or `pnpm` as well, but we're currently focusing on the former of the solutions with NPM and midgard yarn strict. A `strict` package manager such as these options will do a couple of things. First of all, the packages are forced to correctly declare their dependencies in the `package.json` so as to eliminate the phantom dependency / doppleganger problem. Secondly, the root `node_modules` folder can be moved to a different location on disk, and pointed to with symlinks. ``` ./packages/foo/ ./packages/foo/node_modules/react -> node_modules/.store/react-16.9.0 ./packages/bar/ ./packages/bar/node_modules/react -> node_modules/.store/react-16.13.0 ./package.json # This breaks the node module resoution ./node_modules/.store ./node_modules/.store/react-16.13.0 ./node_modules/.store/react-16.9.0 ``` By moving the `node_modules` folder somewhere else, and simply symlinking all of the package's dependencies to the new location, the hoisting algorithm breaks. So, in the previous case where the `bar` package had forgotten to declare react in its package, `bar` can no longer rely on hoisting! Other things you can do to help are implementing a couple of solutions like [DepCheck](https://www.npmjs.com/package/depcheck?ref=jonathancreamer.com) to make based on scanning your code that your dependencies are all correctly declared. There's also a tool called [TypeSync](https://github.com/jeffijoe/typesync?ref=jonathancreamer.com) which does something similar for the `@types` packages to make sure they're present. The hoisting problem with`@types` packages presents a whole different set of complexities because their usage isn't in code necessarily, but only in the TypeScript setup. ## Conclusion Hoisting helped a lot in solving *some* problems for *many* repos out there. However, the moment your monorepo starts to scale a bit, you'll inevitably run into these problems. ### Setting up WSL2 and the VSCode Remote Containers WSL Plugin URL: https://www.jonathancreamer.com/setting-up-wsl2-and-the-vscode-containers-plugin/ Last updated: 2024-01-05T16:26:17.000Z For years, macOS has been the gold standard for tooling when it comes to Web Development. The unix basis of macOS, iTerm, bash, VIM, etc are all things that folks building for the web platform rely on day to day. That all changes with WSL2. WSL2 allows web dev folks working in Windows to get the same goodness of macOS's unix based tooling running "more natively" on Windows. Read more about how all this works: - [Docker ❤️ WSL 2 - The Future of Docker Desktop for Windows - Docker Blog](https://www.docker.com/blog/docker-hearts-wsl-2/?ref=jonathancreamer.com) - [Introducing the Docker Desktop WSL 2 Backend - Docker Blog](https://www.docker.com/blog/new-docker-desktop-wsl2-backend/?ref=jonathancreamer.com) UPDATE: 1-5-24 ## Steps to install WSL2 (The Windows 11 way) ### Step 1 ```bash wsl --install ``` THAT IS LITERALLY IT You can also watch this video I recorded about WSL and Javascript... ## Steps to install WSL2 (The old way) ### Step 1 Turn on WSL in a command prompt or Powershell. ``` dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart ``` ### Step 2 Enable the Virtual Machine Platform ``` dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart ``` Probably should restart after this. ### Step 3 Install this [X64 Patch](https://wslstorestorage.blob.core.windows.net/wslblob/wsl%5Fupdate%5Fx64.msi?ref=jonathancreamer.com). ### Step 4 Make sure WSL2.0 is default. ``` wsl --set-default-version 2 ``` ### Step 4 Install Linux! Go to the [Windows Store](https://aka.ms/wslstore?ref=jonathancreamer.com), and find your favorite Linux. e.g. [Ubuntu 20](https://www.microsoft.com/store/apps/9n6svws3rx71?ref=jonathancreamer.com), [Alpine Linux](https://www.microsoft.com/store/apps/9p804crf0395?ref=jonathancreamer.com) ### Optional other good stuff That's it for WSL2 install, but there's more goodness you can get with... ### Install Windows Terminal [Windows Terminal](https://www.microsoft.com/en-us/p/windows-terminal/9n0dx20hk701?ref=jonathancreamer.com) is a brilliant app that manages all kinds of command prompts, terminals, etc. Read more about how to make [Powershell Pretty](https://www.jonathancreamer.com/making-powershell-pretty/). ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2021/09/image-1.png) By default it will work with Command Prompt and Powershell, but once you have WSL, and Linux installed, it'll also allow you to open Terminals straight into your Linux! ### Install Docker Desktop (optional) If you use Docker on Windows, installing [Docker Desktop](https://www.docker.com/products/docker-desktop?ref=jonathancreamer.com) allows you to install a new WSL2 based backend that speeds up working with Docker on Windows, adds a nice GUI on top of Docker that allows you to see your running containers visually, installs all the command line tools for docker like docker-compose, and allows you to install a small kubernetes cluster as well. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2021/09/image-2.png) Read more [here](https://docs.docker.com/desktop/windows/wsl/?ref=jonathancreamer.com). ## VSCode Remote Containers ### Step 1 Install the [Remote Containers - WSL](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-wsl&ref=jonathancreamer.com) plugin. ### Step 2 Clone a Repo, or something in your Linux Distro. Note, don't clone into the `/mnt/C` directory anywhere, that's a built in way for your WSL instance to interact with your `c:` drive, but it's tremendously slow. Make sure you go to `cd ~` or somewhere on the WSL drive before cloning. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2021/09/image-3.png) ### Step 3 You can `cd /path/to/clone`, and run `code .`, or... Use the Command Palette (Control + Shift + P) and search for Remote-WSL to Open a Folder. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2021/09/image-4.png) Choose the folder you want, and VSCode will now open the project connected directly to your Linux Distro. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2021/09/image-5.png) Alternatively you can navigate to the repo in Terminal, and use `code .` which will also open VSCode connected directly to the Linux Distro. ## Conclusion This post tried to be as clear and concise along the way to get up and running with WSL2 and VSCode as quick as possible. It's a huge productivity win for Web Dev folks working on Windows to be able to develop against Linux, so hopefully this will help accelerate those gains. ## BONUS PROTIP You can also install the [Remote - Containers ](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers&ref=jonathancreamer.com)plugin and test out different programming languages with the power of Docker. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2021/09/image-7.png) ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2021/09/image-6.png) ### Making powershell pretty URL: https://www.jonathancreamer.com/making-powershell-pretty/ Last updated: 2021-07-23T16:07:05.000Z For the first time in a long time, Windows has re-entered my life now that I work at Microsoft. In macOS land, I use iTerm and oh-my-zsh to make a pretty shell environment with all the colors and such, so when I got to Windows again and lanched into the blue Powershell Window, I figured there had to be something better... This is what Powershell looks like now! ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2021/07/image.png) ### Windows Terminal The first thing to make sure you have installed is the [Windows Terminal](https://www.microsoft.com/en-us/p/windows-terminal/9n0dx20hk701?activetab=pivot:overviewtab&ref=jonathancreamer.com). That is a fantastic product, and even has built in support for WSL, CMD, Azure, and launching into Ubuntu or whatever your Linux Environment is... ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2021/07/image-1.png) Another #protip is you can hold Ctrl + Shift when launching Windows Terminal and it'll open as an Administrator. Windows Terminal is super easy to configure as well, and there are themes for it as well at [\>\_TerminalSplash - Windows Terminal Themes](https://terminalsplash.com/?ref=jonathancreamer.com) or [Windows Terminal Themes](https://windowsterminalthemes.dev/?ref=jonathancreamer.com). You simply copy the JSON from those sites, and open the Windows Terminal Settings and Click "Open JSON File"... ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2021/07/image-2.png) ### Oh My Posh The tool that adds the bar at the bottom of the Powershell window is called [Oh My Posh](https://ohmyposh.dev/?ref=jonathancreamer.com) of course inspired by [oh-my-zsh](https://ohmyz.sh/?ref=jonathancreamer.com) for mac / linux. To install, simply run... ```Powershell Install-Module oh-my-posh -Scope CurrentUser ``` Then you can list all the themes with... ```Powershell Get-PoshThemes ``` Once you find one you like, you can edit your `$PROFILE` in your editor of choice, e.g. `notepad $PROFILE`, `code -a $PROFILE`. And you can set your theme up with... ```Powershell Set-PoshPrompt -Theme jandedobbeleer ``` The last thing you'll more than likely also have to do is download the right fonts that supports "Powerline" fonts, the Oh My Posh [docs ](https://ohmyposh.dev/docs/fonts?ref=jonathancreamer.com)talk about that. More than likely, you'll choose a font from [Nerd Fonts](https://www.nerdfonts.com/?ref=jonathancreamer.com), and update your Windows Terminal settings to point to the new font. ```json "profiles": { "defaults": { "fontFace": "MesloLGL Nerd Font" } } ``` It'll even show you what version of node you're in as well as many other what they call "Segments"... ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2021/07/image-3.png) There ya go! You'll be the envy of all your Windows code demos in meetings going forward. ### Getting started with Frontend Architecture and DivOps URL: https://www.jonathancreamer.com/getting-started-with-frontend-architecture-and-divops/ Last updated: 2021-05-25T14:27:22.000Z Trying to understand the world of Frontend Software Development is like trying to solve a Rubix cube that automatically scrambles itself when you get close to solving it. You'll never fully understand it, and that's ok! What you can do though is maybe stop worrying about the entire cube, and focus on one side or even one row, and just be happy you solved that part of it. My friend Cory House tweeted this a while ago, and I heard him give a talk on it as well... > Why specialize? Because the industry is changing faster than ever. > > Today, the half-life of an engineering degree is between 2.5 and 5 years. You must study 10 - 20 hours a week just to keep up with the weekly changes. > > Critical read for every dev: [https://t.co/lDaDAYA1RH](https://t.co/lDaDAYA1RH?ref=jonathancreamer.com) [pic.twitter.com/869LlGzlJd](https://t.co/869LlGzlJd?ref=jonathancreamer.com) > > — Cory House (@housecor) [March 19, 2018](https://twitter.com/housecor/status/975730389925982208?ref%5Fsrc=twsrc%5Etfw&ref=jonathancreamer.com) Specializing in something can be just as if not more rewarding for your long term career growth. Sometimes it's ok to stop learning a little about a lot and learn a lot about a little. The important point to make here is, you don't have to know everything there is to know about Frontend Development to be successful. In fact, you might be more successful if you simply pick the area of frontend you want to specialize in, and stick with that! Inside of the frontend world, as [Brad Frost](https://twitter.com/brad%5Ffrost??ref=jonathancreamer.com) put it so well recently, there's the [Front of the frontend, and the Back of the frontend](https://bradfrost.com/blog/post/front-of-the-front-end-and-back-of-the-front-end-web-development/?ref=jonathancreamer.com). If you're reading this and you find yourself leaning more and more into the "Back of the frontend", aka things like bundlers, testing, CI, Docker, deployments, APIs, etc, then maybe you should consider specializing in Frontend Architecture aka DivOps. A lot of folks who start to specialize in DivOps at one point were on the Front side of frontend, using jQuery, then into Backbone, require.js, maybe Angular, then React or Vue, etc etc. Along that journey a few folks might have decided that actually enjoyed spending time configuring all those different tools to work. As time has passed, it's become increasingly more and more difficult and tedious to manage all that configuration and infrastructure. If the journey you're on feels like you've started venturing into the Divops world, then here are some great resources to get you started! - Weekly newsletters such as [node](https://nodeweekly.com/?ref=jonathancreamer.com), [esnext](http://esnextnews.com/?ref=jonathancreamer.com), and [JS Weekly](https://javascriptweekly.com/?ref=jonathancreamer.com) are expertly curated with swaths of knowledge delivered to your inbox weekly - Ya know that `node_modules` folder? Actually try looking into it some times. You can spend a few minutes reading over some babel plugins and learn a ton about important topics like ASTs - Speaking of which, learn some ASTs! Start with the [Babel Handbook](https://github.com/jamiebuilds/babel-handbook?ref=jonathancreamer.com), ASTs are how most modern tools work under the hood and can be extremely powerful in manipulating thousands of lines of code in seconds through things like babel plugins and "[codemods](https://github.com/codemod-js/codemod?ref=jonathancreamer.com)". Codemods are a great way to update code consistently across a codebase. it is important for a DivOps role because we tend to focus on making wild scale changes - While yes, there are a TON of tools out there, you don't have to learn ALL of them at once, pick a few at a time, find out which one you like, try to learn the ins and outs of webpack vs rollup vs snowpack. In the end of the day, there are reasons why you might choose one over the other, but there's no technically wrong choice. Pick what works for your problem at that moment. - Learn about monorepo tools like yarn workspaces, lerna, bolt, rush, etc. Monorepos can be extremely useful in a large scale frontend architecture - Read code, like lots of it, particularly in code bases like [Babel](https://github.com/babel/babel?ref=jonathancreamer.com), [Typescript ESLint](https://github.com/typescript-eslint/typescript-eslint?ref=jonathancreamer.com), or just go through trending Github languages like... [http://github.com/trending/javascript](http://github.com/trending/javascript?ref=jonathancreamer.com), [http://github.com/trending/javascript](http://github.com/trending/javascript?ref=jonathancreamer.com). - Learn [Docker](https://docs.docker.com/get-started/overview/?ref=jonathancreamer.com). Docker is huge in the DivOps, and DevOps worlds alike. - Learn a CI tool, or a few... CircleCI, Travis CI, Github Actions, and Azure Pipelines are all good ones to get started with and basically all do the same thing, just slightly differently - [Typescript](https://2020.stateofjs.com/en-US/technologies/javascript-flavors/?ref=jonathancreamer.com) is probably a good idea now. :) - Talk to people, engage, join a [community](https://www.jonathancreamer.com/announcing-div-ops/), join Twitter to meet folks doing this same type of stuff, go to conferences, even virtual ones can offer you a chance to meet others - Write. Things. Down. This cannot be stressed this enough. Journal, blog, create a Notion blog, Evernote, etc. Write down everything, ideas, day to day work logs, tips and tricks, proposals that you can share with other engineers or leadership folks, etc. Our brains can hold a finite amount of information in them at once. Stay connected, specialize, and have fun! DivOps is a great path to tread, and is an ever evolving discipline. ### Running Typescript programs with babel-node aka how does babel register work URL: https://www.jonathancreamer.com/running-programs-with-babel-node/ Last updated: 2021-06-04T15:28:42.000Z As a member of a frontend infrastructure team, (aka [DivOps](https://www.jonathancreamer.com/announcing-div-ops/)), I write a lot of Javascript tooling. I also really really like Typescript. In fact most of the tooling I write these days is written in Typescript. The problem is, when the tools we work on are handed off to our end users, they have to either be compiled with tsc, or babel first. Generally we typecheck with `tsc` and transpile with the `@babel/preset-typescript` preset. Sometimes though, that compilation step causes a delay, and we'd rather just run the code as is, Typescript, ES modules and all. That's where tools like `@babel/node` come in. `@babel/node` allows you to "transpile" code from Typescript, or "ES Next" JavaScript with modules, or whatever to Javascript that can run in nodejs, and it does this at runtime as opposed to pre-transpiling. Aka you can do stuff like this, and run Typescript files without "pre" compiling them. ```bash babel-node -x .ts -- ./path/to/script.ts ``` So, how does `@babel/node` actually work? ## npm i -D @babel/node When you install this pacakage, it will add a `babel-node` command to your `./node_modules/.bin` folder. This `.bin` folder is used for all CLI tools you install, things like `webpack`, `babel-node`, etc. The `.bin` then just symlinks the `babel-node` command to `./node_modules/@babel/node/bin/babel-node`. This works because of the `"bin"` being set to `"babel-node": "./bin/babel-node.js"` right [here](https://github.com/babel/babel/blob/a17e4715c986eed1768461173b123650e5371307/packages/babel-node/package.json?ref=jonathancreamer.com#L46). Then, `babel-node` is simply a program with a shebang that tells the system to what interpreter to use for the program, in this case the `/usr/bin/env` [command](https://en.wikipedia.org/wiki/Env?ref=jonathancreamer.com) is what actually runs first, and makes sure that it can find `node`. ```bash #!/usr/bin/env node ``` The `babel-node` program itself then just imports `*require*("../lib/babel-node");` This is where we start getting into the actual runtime of `@babel/node`. The `lib/babel-node.js` script is responsible for reading in any runtime arguments for [v8](https://www.npmjs.com/package/v8-flags?ref=jonathancreamer.com) runtime stuff like `--harmon` or [node](https://www.npmjs.com/package/node-environment-flags?ref=jonathancreamer.com) env variables like `--require`. Ultimately the `lib/babel-node.js` uses a program called `kexec`, a [C++ program ](https://github.com/jprichardson/node-kexec/blob/b9a462ee508b1b799e68db91ee0fc1938b9be3f7/src/kexec.cc?ref=jonathancreamer.com)which swaps out a process at runtime, to actually run `./lib/_babel-node.js` `./lib/_babel-node.js` is where things really start to happen as this is *really* the meat of what `@babel/node` is actually doing. It uses a CLI helper utility called commander and sets it up with the following possible args. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2020/12/Screenshot-on-2020-12-09-at-09-41-47.png) You'll notice a few of those are similar to ones you'd use when working directly with `node` itself. Things like `-e`, `-p`, and `-r` are all flags that `node` itself also accepts, other flags like `--inspect` fall through to the node process that will ultimately get spun up. Also note, the `--extensions` flag requires ALL the extensions you're going to use, so if you know you're going to need multiple, make sure you do `--extensions .js,.ts,.jsx,.tsx` Eventually after some args parsing, an internal `Module.runMain` [function](https://github.com/nodejs/node/blob/b589128f6f4c3c6f636bfb0146957847ef0a8d53/lib/internal/bootstrap/pre%5Fexecution.js?ref=jonathancreamer.com#L417%29) happens and runs your script by overwriting the arguments passed with `process.argv`. ```js process.argv = [ '/path/to/node/v12.17.0/bin/node', '/path/to/node_modules/@babel/node/lib/_babel-node', ...args, ]; ``` Then what happens in \[this line\]([https://github.com/babel/babel/blob/5067edfdd95fc4c38cb5e60192eac5f7649fa229/packages/babel-node/src/\_babel-node.js#L198](https://github.com/babel/babel/blob/5067edfdd95fc4c38cb5e60192eac5f7649fa229/packages/babel-node/src/%5Fbabel-node.js?ref=jonathancreamer.com#L198)) is, `node` will now run `/path/to/_babel-node`, and send it your actual runtime arguments. So, that's how `_babel-node` is actually invoked, but how does it transpile Typescript to Javascript on the fly? The answer there is `@babel/register`. ## @babel/register `@babel/register` is the special sauce of how `@babel/node` is able to actually read in Typescript, or ES Next Javascript, transpile it to commonjs, and run it on the fly. Using `@babel/register` is one way the [docs](https://babeljs.io/setup?ref=jonathancreamer.com#installation) say to setup your code. Basically instead of `babel-node -- path/to/your/script.ts` you can manually call the `@babel/register` module itself by importing it in your code and call it with `node /path/to/your/script.ts`, in the case of Typescript though ```js require("@babel/register")({ extension: ['.ts', '.tsx', '.js', '.jsx'] }); const { main } = require("./src/cli"); main().catch((e) => console.error(e)); ``` Keep in mind with register, you have to separate your entry point from your code because register has to run and add the `require` hooks to the NEXT files you'll load, aka you can't have `import` and `require("@babel/register");` in the same file. Note to use Typescript and Typescript with React, you'll have to add the `"extensions"`. The other arguments you can pass to register are the same as the regular babel [options](https://babeljs.io/docs/en/options?ref=jonathancreamer.com). In terms of `@babel/node`, the `register` function gets called [here](https://github.com/babel/babel/blob/5067edfdd95fc4c38cb5e60192eac5f7649fa229/packages/babel-node/src/%5Fbabel-node.js?ref=jonathancreamer.com#L93) and is passed the command line args, and some additional configuration for babel. `@babel/register` uses a library called [pirates](https://www.npmjs.com/package/pirates?ref=jonathancreamer.com) to add a compile hook using some internal `Module.__extensions` [magic](https://github.com/ariporad/pirates/blob/5223d20e54f724780eb73d4d4918f70004d9d8dc/src/index.js?ref=jonathancreamer.com#L59) to change the underlying Javascript loader in node. It does that [here](https://github.com/babel/babel/blob/main/packages/babel-register/src/node.js?ref=jonathancreamer.com#L99). So basically every time a file gets `require`'d going forward in your program, it'll first run through the `compile` [function](https://github.com/babel/babel/blob/5067edfdd95fc4c38cb5e60192eac5f7649fa229/packages/babel-register/src/node.js?ref=jonathancreamer.com#L39), get compiled, and finally executed. ### Running and debugging babel-node There's a couple of different ways to run a script with `babel-node`, just like with `node` itself. Passing it the name of a script will invoke the script just like it would with `node`. `babel-node -x .ts -- ./path/to/script.ts` 👆note the `-x .ts` or `--extensions .ts` there to make it recognize the Typescript `.ts` extension as well as `.js` You can also add the `--inspect` flag and in VS Code or using `chrome://inspect`, you can **debug** your command line script. In VS Code make sure you have on Auto Inspect, and you'll then be able to throw breakpoints into your code. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2020/12/Screenshot-on-2020-12-09-at-10-45-36.png) ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2020/12/Screenshot-on-2020-12-09-at-10-44-14.png) If you want to use the Chrome developer tools to debug your script, make sure you throw a `debugger;` statement somewhere in your code, then you can run... ```bash babel-node -x .ts --inspect-brk -- ./path/to/script.ts ``` The `--inspect-brk` will force the process to pause at the very first line of execution, which actually in this case will pause in the `_babel-node.js` script since that's technically the first line of code that gets executed. Then you can visit `chrome://inspect` and find your node script in the list, and click inspect. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2020/12/Screenshot-on-2020-12-09-at-11-16-04.png) You'll then get dropped into a Chrome Debugger. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2020/12/Screenshot-on-2020-12-09-at-10-50-23.png) You can run `babel-node` with `-e` and execute code on the fly. ```bash babel-node -e "console.log('oh hai');" ``` You can run a script in combo with `-p` and print the results... ```bash babel-node -e "new Date().getTime()" -p // 1607531838009 ``` Lastly you can run `babel-node` by itself, to spin up a `repl` for playing around in `node` with an environment that will transpile code on the fly for you. ## Gotchas One thing in particular that has bit me many times, and partly is the reason why I am writing this post to begin with is, ### When using babel register, call the register function before your code As mentioned previously, make sure when using `@babel/register`, call the `require("@babel/register")` first, then require your actual code. Otherwise it won't work at all. ### babel-node's command line ignore overrides the ignores in your babel.config.js This one seems like a \[bug\]([https://github.com/babel/babel/issues/11892#issuecomment-687845634](https://github.com/babel/babel/issues/11892?ref=jonathancreamer.com#issuecomment-687845634)), but basically, if your `babel.config.js` has any kind of ignore in it... ```js module.exports = { /* ... blah blah babel stuff */ ignore: [function(filepath) { return filepath.includes('some-string-you-wanna-ignore'); }] } ``` You have to pass an empty ignore in your `babel-node --ignore ' ' path/to/script.ts`. It probably has to do with [this line](https://github.com/babel/babel/blob/5067edfdd95fc4c38cb5e60192eac5f7649fa229/packages/babel-register/src/node.js?ref=jonathancreamer.com#L140). Register is expecting ignore not to be undefined, and it's not checking that against the ignore from the `babel.config.js`. ### babel-node will add your process.cwd() location to the --only param by default. This might be fine in your codebase, but if you're expecting `babel-node` to be able to transpile code from outside of your `cwd`, it simply won't work because `@babel/register` . This goes back to the same [line](https://github.com/babel/babel/blob/5067edfdd95fc4c38cb5e60192eac5f7649fa229/packages/babel-register/src/node.js?ref=jonathancreamer.com#L140) above. If no `--only` flag is passed at run time, babel will by default ignore anything outside your current working directory AND anything in `$(cwd)/node_modules`. So, say you have a project structure like this. ```text packages/tools/my-awesome-tool/ packages/utils/ package.json ``` If you have a script in `my-awesome-tool/scripts/cool-script.ts` which imports from `packages/utils` and you invoke it FROM the directory, i.e. if you did `cd packages/tools/my-awesome-tool`, the scripts in`packages/utils` will NOT get transpiled by default. There are 2 ways around this. First, just run all your scripts from the root of your project. Second, you can pass `--only /path/to/project`. That way it won't ignore stuff. ### Make sure to add the \`--\` thing when running a script If you forget to add \`--\` in between your options i.e. `babel-node -x .ts path/to/scripts.ts` instead of `babel-node -x .ts -- path/to/script.ts`, then `babel-node` is going to try to parse through the options you passed to your OWN script. This is especially important when the script your calling takes it args. For example... ```bash babel-node -x .ts -- ./node_modules/.bin/build-storybook \ -c packages/tools/storybook/src/config \ -o packages/tools/storybook/dist/storybook ``` In this case, we're calling the `build-storybook` command with `babel-node`, and the `-o` flag is setting the output dir for storybook. If you forget the \`--\`, the `-o` param will actually be parsed as the `--only` flag for babel! ### rootMode: upward In a monorepo setup, or a setup where a babel config doesn't live in the root of your package, but rather up in a higher directory level... ```text ./package.json ./babel.config.js ./packages/tools/storybook/package.json ./packages/tools/storybook/bin/cli.js ``` If the `bin/cli.js` has a call to `@babel/register`... ```js require('@babel/register')({ extension: ['.ts', '.js'], rootMode: 'upward' }); require('../src')(); ``` In this case, if you want to `cd packages/tools/storybook` and then `node bin/cli.js`, the `rootMode` needs to be set so that babel will recursively search for a babel.config.js since there isn't one in the `./packages/tools/storybook` working directory. ## Conclusion `@babel/node` is a nice convenient way to run scripts on the fly without pre-compiling them. Just be aware of some its nuances and you'll be able to build some amazing tools. This post was written primarily because many of the things mentioned are things I've dealt with in real life, and I kept running into them over and over again, and finally decided it was time to write them down for myself! Hopefully it can help you too. ### Understanding typescript generics by example URL: https://www.jonathancreamer.com/understanding-typescript-generics-by-example-with-window-fetch/ Last updated: 2020-11-25T23:40:55.000Z Typescript [generics](https://www.typescriptlang.org/docs/handbook/generics.html?ref=jonathancreamer.com) are a built in language feature using the same principles of generics in other languages like [C#](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/?ref=jonathancreamer.com), [Java](https://www.tutorialspoint.com/java/java%5Fgenerics.htm?ref=jonathancreamer.com), etc. To understand generics, let's talk about functions. Functions take parameters... ```js const getUser = async (id: number) => { const resp = await fetch(`/api/users/${id}`); const user = await resp.json(); return user; } ``` In this case, the `id` is a function parameter. Well, in some cases, you might want to also pass a type as a parameter too, that's when you can use a generic One super useful time to use a generic is when creating an SDK for an API. ```ts interface User { id: number; name: string; } interface Repository { getById(id: number): RepoType; } class UserRepository implements Repository { async getById(id: number) { const resp = await fetch(`/api/users/${id}`); const user = await resp.json(); return user; } } const repo = new UserRepository(); const user = repo.getById(1); ``` In the above example, we're creating an interface with a generic parameter for the `RepoType`. This will allow us to create other types of repositories for different data models, but leveraging the code re-usability of the underlying fetch logic. A similar thing can be done with just pure functions instead of classes. ```ts async function fetchAPI(url: string, opts?: FetchOptions) { const resp = await fetch(url); const json = await resp.json(); return json as DataType; } const fetchUserById = async (id: number) => { const user = await fetchAPI(`/api/users/${id}`); return user; } const user = fetchUserById(1); ``` A lot of examples around the internet are fairly contrived, and also tend to use single letter generic names like ``, ``, etc. While the short name is more terse, it can also make things a little hard to read. The following is a quick and dirty implementation of the [queue](https://www.geeksforgeeks.org/queue-data-structure/?ref=jonathancreamer.com) data structure. Don't worry too much about that, a queue is basically like waiting in line for a ride at Disney World. You enter the line, first, you're the first person to get on the ride. (First in first out or FIFO). ```ts function createQueue () { const queue: Type[] = []; return { add(obj: Type) { queue.push(obj); }, remove() { return queue.shift(); }, peek() { return queue[0]; } } } interface Task { name: string; work: () => Promise; } const taskQueue = createQueue(); taskQueue.add({ name: 'thing', work: () => {}}); taskQueue.add({ name: 'another', work: () => {}}); taskQueue.add({ name: 'last thing', work: () => {}}); const task = taskQueue.remove(); assert.equal(task.name, 'thing'); // True! ``` So, in this case we're able to create a strongly typed queue called `taskQueue` when we remove an item from the queue using `taskQueue.remove()`, the `task` will be a `Task`. This means your IDE will be able to properly understand what the `task` is as well, which is massivley helpful. In the above examples, we're manually passing the generic parameter, and you can think of the generic in terms of using "of". For example, the queue above, is a `Queue` "of" `Task` types, and the `UserRepostory` is a `Repository` "of" type `User`. It's also possible to infer the generic parameter from the actual function parameters. ```ts const find = function( items: ItemType[], fn: (t: ItemType) => boolean ): ItemType | null { for (const item of items) { if (fn(item)) { return item; } } return null; } interface Avenger { name: string; } const avengers: Avenger[] = [ { name: 'iron man' }, { name: 'hulk' }, { name: 'thor' } ]; const strongestAvenger = find( avengers, (avenger) => avenger.name === 'thor', ); ``` Here we have a `find` function. Notice that the `ItemType` is the generic parameter name. When we call the `find` function, the `ItemType` parameter will automatically get passed the `Avenger` type because we said in the function parameters that the `items` are of `ItemType`. You can use `ItemType | null` as the return type too. ### How I fixed webpack tree shaking in 3 easy steps URL: https://www.jonathancreamer.com/webpack-tree-shaking-in-3-easy-steps/ Last updated: 2020-10-06T16:56:54.000Z [Tree shaking](https://webpack.js.org/guides/tree-shaking/?ref=jonathancreamer.com) is an important step in Webpack bundling to get rid of unused stuff. ## Step 1 ```json { "presets": [["@babel/env", { targets: { browsers: ['last 2 versions'], }, // The magic sauce modules: false, }]] } ``` The `modules: false` is what does it. This tells babel to leave the `imports` and `exports` alone. Because all this tree shaking business is built upon "statically analyzing" you're dependency tree, which only works when ES modules are used. Commonjs modules that use the`require` style for importing don't work with tree shaking Note: that'll break `jest` probably, so make sure you turn modules back on in `test`. ```js "test": { "presets": ["@babel/env", { "targets": { "node": true }, "modules": "commonjs", }], }, ``` ChakraUI's `babel.config.js` is also a pretty good example [https://github.com/chakra-ui/chakra-ui/blob/develop/babel.config.js](https://github.com/chakra-ui/chakra-ui/blob/develop/babel.config.js?ref=jonathancreamer.com) ## Step 2 Make sure webpack is set to `mode: production`. This turns on a bunch of internal flags to make it all work. ## Step 3 This one took me a while to figure out. The code base I work in, not unlike lots and lots of others, does this kinda thing a lot via `index.ts` or `index.js` where we define the public API of a package... ```js export { default as Thing } from './thing'; export { default as Thing2 } from './thing2'; export { default as Thing3 } from './thing3'; export { default as Thing4 } from './thing4'; ``` Here's the problem, webpack doesn't know whether or not any of those `thing` imports are used without a little extra help. `sideEffects: false` is what you need. .Throw that into the `package.json` of the libraries where those `index`'s are used. Webpack uses that flag internally to help the tree shaking actually work. ![](https://techcrunch.com/wp-content/uploads/2015/02/giphy.gif) Magic. HOWEVER. The final thing with this though is, make sure your code actually doesn't have "side effects". Meaning, stuff like this isn't happening... ```js export const sideEffectyThing = window.localStorage.getItem('stuff'); ``` This is bad because the moment you import this code, it calls something on the window. That's no bueno. Put that in a function. ```js export const getStuff = () => window.localStorage.getItem('stuff'); ``` Another one, which is super common is... ```js import './component.scss'; ``` In this case, you'll have to mark your module as `sideEffect: ['*.scss'];` . [https://github.com/webpack/webpack/issues/6741#issuecomment-372720641](https://github.com/webpack/webpack/issues/6741?ref=jonathancreamer.com#issuecomment-372720641) ### Dealing with async jest tests URL: https://www.jonathancreamer.com/dealing-with-async-jest-tests/ Last updated: 2020-09-25T18:16:18.000Z In [jest](https://jestjs.io/?ref=jonathancreamer.com), there are a couple of different ways to handle tests which deal with async code. Let's say way have a test that calls `fetchSomeAsyncThing()`. ```js it('should call my api', () => { const result = fetchSomeAsyncThing(); expect(result.foo).toBe('hello world') }); ``` This will fail. Why? Because of the way JavaScript handles async code. When you call a function known to be async, whether it's something that returns a `Promise`, calls a `setTimeout`, etc, that function get pushed onto a call stack, and called wayyy after your tests have actually ran. A stack is a data structure kinda like an array which basically means, the last thing that got put into it, is the first thing to come out. Read more about [stacks](https://github.com/trekhleb/javascript-algorithms/tree/master/src/data-structures/stack?ref=jonathancreamer.com) and [async Javacript.](https://blog.bitsrc.io/understanding-asynchronous-javascript-the-event-loop-74cd408419ff?ref=jonathancreamer.com) ## Use the done callback If your async function returns a callback, several of the jest functions provide a done callback which you can use like this... ```js it('should call my api', (done) => { const result = fetchSomeAsyncThing((result) => { expect(result.foo).toBe('hello world'); done(); }); }); ``` As you can see, the callback to the `it` method above returns a `done` function. This `done` function will tell jest that your async method is done and it will wait for that all to finish before wrapping up that test. ## Use promises If your async function returns promises you can do one of 2 things. ```js it('should call my api', (done) => { const result = fetchSomeAsyncThing().then((result) => { expect(result.foo).toBe('hello world'); done(); }); }); ``` Similar to above, use the `done` function inside the `.then`. Or, you can simply return the entire promise. ```js it('should call my api', () => { return fetchSomeAsyncThing((result) => { expect(result.foo).toBe('hello world'); }); }); ``` In this case, jest will realize that the return value of the test was itself a promise, and will therefore wait until that promise fully resolves before wrapping up the test. ## Use async / await Once again, if you know that your async function returns a promise, you can use the `async` and `await` features of [modern Javascript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async%5Ffunction?ref=jonathancreamer.com). ```js it('should call my api', async () => { const result = await fetchSomeAsyncThing(); expect(result.foo).toBe('hello world'); }); ``` The `async` / `await` method is a nice, clean way to read the code. ### Running Cypress tests in a monorepo URL: https://www.jonathancreamer.com/running-cypress-tests-in-a-monorepo/ Last updated: 2020-07-02T19:10:41.000Z [Cypress](https://www.cypress.io/?ref=jonathancreamer.com) is a fantastic testing tool for running your applications in a browser like environment. It's reminiscent of Selenium, but runs quicker, and has a much nicer developer experience. It can be used for full on acceptance or integration testing, or even at a feature test level with mocking in place. If used correctly, it can even replace the component testing you might would see with a Jest + Enzyme setup as, rather than having to do a lot of mocking setup, mounting, etc in your unit tests, you can simply just run tests against the features you have and directly interact with the components themselves in a runtime environment. There are many great examples of how to use Cypress out in the wild already including this [https://github.com/cypress-io/cypress-realworld-app](https://github.com/cypress-io/cypress-realworld-app?ref=jonathancreamer.com) example, but there's not many that cover the monorepo use case. ## Monorepo Go check this post on , but the tldr; there is... > A ~~mono~~megarepo is a collection of many units of code or "packages" all in the same repository, managed by tooling which enables sharing those packages easily. ## Cypress in a monorepo Cypress is built around a few conventions, for one, when you first run Cypress it'll create a nice neat structure for you in the root of whatever project you're working in. That however doesn't necessarily jive with the structure of a megarepo which has lots of packages all over the place. Let's take the following as our megarepo structure. We're going to use yarn workspaces in this example since it's a fairly easy to understand setup. Note that the same principles should apply to a lerna, bolt, or insert your monorepo of choice. ``` ./cypress ./cypress/fixtures ./cypress/integration ./cypress/support ./cypress/plugins ./packages/ ./packages/login ./packages/account ./packages/ui-shared ./packages/web ./cypress.json ./package.json ``` So, in this case our Cypress tests are setup in the `./cypress` folder along with the `plugins`, `fixtures`, and `support` folders. When `cypress run` is executed, it will by default look into the `./cypress/integration` folder for your tests. This in the case of a monorepo is a little less than ideal as it'd be much better if the tests for each package could live in the actual `./packages` themselves. Let's go ahead then and create some `cypress` folders in each of our packages too. `./packages/login/cypress/integration`, etc etc. Then our tests can live next to the code that they're actually testing. Let's create a `packages/tooling` package as well and add some scripts in here. First, let's create `./packages/tooling/src/cypress-runner.js`. Let's assume there's also a package called `@demo/web` which is an express.js app that has routes for loading the other various packages like `@demo/login`, `@demo/account`, etc. ```typescript import cypress from 'cypress'; import { app } from '@demo/web'; import glob from 'glob'; const tests = glob.sync('src/cypress/integration/**/*.{js,ts}', { cwd: app.directory, absolute: true, }); export const main = async () => { await app.listen(3000); const result = await cypress.run({ spec: tests, reporter: 'junit' }); return result; }; main().then((result) => { if (result.failures) { console.log('Tests failed'); process.exit(1); } process.exit(0); }); ``` Running this script by either first compiling it with babel or using `babel-node`, you will effectively start your web app, ideally with a promise that resolves when the listener is listening. Then programmatically run cypress with a glob of the test files from each package. We can expand on this in a number of ways. As well. ### Parallelizing the tests in CI Ideally your CI will parallelize the tests runners because even as fast as Cypress is, it can still be expensive when running it against many tests. One way of accomplishing this is to utilize a package called, [chunkd](https://www.npmjs.com/package/chunkd?ref=jonathancreamer.com). ```javascript export const main = async () => { const index = process.env.CI_NODE_INDEX; const total = process.env.CI_NODE_TOTAL; await app.listen(3000); const result = await cypress.run({ spec: chunkd(tests, index, total).join(','), reporter: 'junit' }); return result; }; ``` What chunkd allows you to do is, pass in an array of something, give it a number of machines available with an environment variable or something like `process.env.CI_NODE_TOTAL`, and the index of the machine you're running on, with `process.env.CI_NODE_INDEX`. Let's add an alias for running the script we created. ```json { "scripts": { "cypress:ci", "babel-node ./packages/tooling/src/cypress-runner.js" } } ``` So, if you were using Jenkins for your CI as an example... ```groovy stage('Cypress Tests') { failFast true parallel { stage('Test group 1') { steps { sh 'CI_NODE_TOTAL=2 CI_NODE_INDEX=0 yarn cypress:ci` } } stage('Test group 2') { steps { sh 'CI_NODE_TOTAL=2 CI_NODE_INDEX=1 yarn cypress:ci` } } // ... } ``` In this case we're leveraging a declarative Jenkins syntax for running tests in parallel. In [travis ci](https://travis-ci.com/?ref=jonathancreamer.com), you could accomplish something similar with a "build matrix" configuration... ```yaml script: "yarn cypress:ci`" env: global: - CI_NODE_TOTAL=2 jobs: - CI_NODE_INDEX=0 - CI_NODE_INDEX=1 ``` ### Only run tests against changed packages Another nice optimization especially as a monorepo gets larger is to only run tests against packages that have actually changed. With a tool like lerna, you could leverage the \[lerna changed\]([https://github.com/lerna/lerna/tree/master/commands/changed](https://github.com/lerna/lerna/tree/master/commands/changed?ref=jonathancreamer.com)) command to get a list of the changed packges, and then just iterate through each package, and glob for those same test files. ```javascript // https://github.com/lerna/lerna/issues/2013 const changed = somehowGetChangedPackages(); const tests = []; for (const [location] of changed) { tests.push(...glob.sync(`${location}/src/cypress/integration/**`)) } ``` You can also utilize something like \[changesets\]([https://github.com/atlassian/changesets](https://github.com/atlassian/changesets?ref=jonathancreamer.com)) from atlassian as well, which keeps track of all the changes to each package in your monorepo as well. ```javascript import getReleasePlan from '@changesets/get-release-plan'; import getWorkspaces from '@changesets/get-release-plan'; const workspaces = await getWorkspaces(); const releasePlan = await getReleasePlan({ cwd }); const changed = []; for (const release of releasePlan.releases) { const workspace = workspaces.find((w) => w.name === release.name); if (workspace) { changed.push(workspace.directory); } } const tests = []; for (const directory of changed) { tests.push(...glob.sync(`${directory}/src/cypress/integration/**`)) } ``` Now you're not only parallelizing the cypress tests you're running, but also only parallelizing the tests against packages which have changes! ## Conclusion Cypress is a very powerful tool that can be used in many ways. Using it in a monorepo gives you a chance to see how a change in one part of the code base can affect other downstream dependent packages. It also gives you a chance to try a different way to test your code. Rather than mounting your React apps in Jest where you can't actually see them running, Cypress affords you the opportunity to test a whole bunch of cases at once, all the while being able to actually physically see how your components are rendering. It even provides screenshots and videos of your tests! Go give Cypress a try. ### For of loops in Javascript one loop to rule them all URL: https://www.jonathancreamer.com/for-of-loops-in-javascript-one-loop-to-rule-them-all/ Last updated: 2020-03-17T19:09:40.000Z For the longest time, `for in` and `for` were the main loops you could use to iterate over collections of things in JavaScript. Then we got some fancy array methods like `forEach`, `map`, `filter`, etc. It starts to get a bit confusing of when to use each loop type. For example, you can't use `for in` on array's, only on objects. Then, how do I loop over an object? Well, you can use `for in`, but only if you check `hasOwnProperty` or whatever, or use... ```js Object.keys(obj).map((key) => { const value = map[key]; }); ``` Which is all weird because you have to get the keys, then grab the value, etc. Now, we have a new thing as of ES6 called `for of`. It's becoming more and more used as knowledge of how to use it has grown, but there's still occasional confusion around how/when to use it. Below is a quick cheatseat of some usages of `for of`, one loop to rule them all. ### Arrays ```js const arrayOfStuff = ['thing one', 'thing two', 'thing three']; for (const thing of arrayOfStuff) { console.log(thing); } ``` For arrays, it's pretty simple. It looks like a `for in`, but you can't `for in` an array. The point here is, the `thing` becomes each item in the array. ### Arrays of Objects ```js const arrayOfObjectsOfStuff = [{ name: 'thing one' }, {name: 'thing two' }, { name: 'thing three' }]; for (const { name } of arrayOfObjectsOfStuff) { console.log(name); } ``` Here you'll notice when iterating an array of objects, you can utilize destructuring to pull the value of the key `name` off of every item in the array. Note, the descructuring here uses `{}`'s because we're descructuring an object, vs `[]` in the case of desctructuring an array. ### Objects ```js const userMap = { '123': 'user 1', '456': 'user 2', '789': 'user 3', }; for (const [ id, name ] of Object.entries(userMap)) { console.log(id, name); } ``` Things get even cooler here now, thanks to the magic of `Object.entries`. Object.entries returns an array of key value pairs, so in this case basically... ```js [ [123, 'user 1'], [456, 'user 2'], [789, 'user 3'], ] ``` So, you're in one line converting the object to an array of key value arrays, and then using destructuring to get the id, and name values! ### Maps ```js const actualMapOfUsers = new Map(); actualMapOfUsers.set('123', 'user 1'); actualMapOfUsers.set('456', 'user 2'); actualMapOfUsers.set('7899', 'user 3'); for (const [id, name] of Array.from(actualMapOfUsers)) { console.log(id, name); } ``` With ES6 `Map` objects, you can just use the `Array.from` method to convert the `Map` into, you guessed it, an array of key values pairs again. ### Promises ```js const getUser = async (name) => { const response = await fetch(`https://api.github.com/users/${name}`); const json = await response.json(); return json; }; const arrayOfPromises = []; const usernames = ['jcreamer898', 'kwelch', 'AlexSwensen']; for (const user of usernames) { arrayOfPromises.push(getUser(user)); } Promise.all(arrayOfPromises).then((users) => { for (const user of users) { console.log(user.name); } }); ``` The final crazy cool thing you can do is handle promises or async await inside of `for of` loops. In the above example, we're actually creating an array of promises which we then resolve with `Promise.all`, so this will add a bunch of stuff into the event loop and then once they're all resolved, call the `.then` on the `Promise.all`. Note in this case, there's no use of async / await, so the code will transpile to much less than that of the code that would require babel polyfill, etc from using async await. That said, you probably already have a polyfill like babel installed, so alternatively you can still async/await the `Promise.all` with... ```js const main = async () => { const users = await Promise.all(arrayOfPromises); }; ``` The other option is to use `await` in an `async` function and actually `await` each response. ```js const getUser = async (name) => { const response = await fetch(`https://api.github.com/users/${name}`); const json = await response.json(); return json; }; const getUsers = async () => { const users = []; const usernames = ['jcreamer898', 'kwelch', 'AlexSwensen']; for (const name of usernames) { const user = await getUser(name); users.push(user); } return users; }; const main = async () => { await getUsers(); }; ``` In this case the code will pause and wait for each `getUser` response to come back before moving on to the next one. Here is a code sandbox where you can see all of this running! Hopefully this article helps clear up any confusion in `for of` loops going forward. ### WFH pro tips URL: https://www.jonathancreamer.com/wfh-pro-tips/ Last updated: 2020-03-16T16:25:26.000Z I've had the privilege of getting to work both in offices, at home 100%, and remote working over the course of my career. There are pros and cons to every approach. My personal favorite is having the best of both worlds like where I'm at with Eventbrite. We have an amazing office in Nashville, snacks, la croix, free catered lunches, etc. At home for me though, I have my two beautiful daughters and my wife. And I also thrive in that environment as well. I don't mind the interruptions of my five year old just wanting to say hi or play barbies with me for a few minutes, or my 18 month old banging on my door yelling, "DADDDY", here and then throughout the day. Those interruptions I welcome. After having done the 100% remote, and remote working things, here are a few tips on working from home... - You don't need 3 monitors to work from home, get one nice large one, and as of these days I think 1440p (2,560 x 1,440) is a good resolution. 4k (3,840 x 2,160)can actually end up being too much - Subscribe to a coffee company's monthly delivery service! I use [Black Rifle](https://www.blackriflecoffee.com/?ref=jonathancreamer.com). Then I get the beans, grind them in a [Capresso Burr Grinder](https://www.amazon.com/Capresso-560-01-Infinity-Conical-Grinder/dp/B0000AR7SY/ref=sr%5F1%5F8?keywords=burr+grinder&qid=1584375507&sr=8-8&ref=jonathancreamer.com), and use a [V60 pour over](https://www.amazon.com/Hario-Dripper-Measuring-Filters-Together/dp/B00V0752FU/ref=sr%5F1%5F1?keywords=v60+glass&qid=1584375542&sr=8-1&ref=jonathancreamer.com). I start every day doing this, and something about the process of it, gets my day started on the right foot. But, find what works for you! - Use the slack mute button. It's there, and it's useful, tell your team, "heads down for X hours", and do it, ignore slack for a while, let the red dots grow for a bit, then check them later after your mute expires ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2020/03/Screen-Shot-on-2020-03-16-at-10-29-16.png) - That said, keep your team updated! Create a team chat where you let people know you're away. - Even more importantly, create a #watercooler or #random chat. When we were 100% remote at appendTo, we still felt like an IRL team because, well we talked to each other a lot in there. Just to say, hi every now and then. - Open a long running hangout every now and then. It helps to just see faces from time to time - Hangouts + [Visual Studio Code Live share ](https://visualstudio.microsoft.com/services/live-share/?ref=jonathancreamer.com)\= 👏🏻. Hands down some of our team's best code writing happened while we opened up a long running hangout, stared a live share session, and just slung some code together ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2020/03/Screen-Shot-on-2020-03-16-at-10-36-32.png) - Take breaks. Use a break timer like [https://breaktimer.app/](https://breaktimer.app/?ref=jonathancreamer.com). Take a quick walk every few hours, take the dog out, play with your kids, stand up and do a sun salutation a to keep your lower back from exploding, etc - If there's a lot of noise in your hosue, get some noise cancelling headphones like airpods pro, bose or whatever - I got myself a nice microphone too... [Blue Yeti](https://www.amazon.com/Blue-Yeti-USB-Microphone-Blackout/dp/B00N1YPXW2/ref=sr%5F1%5F2?keywords=blue+yeti&qid=1584375656&sr=8-2&ref=jonathancreamer.com) - Take a trip to a coffee shop , grab a drip coffee or tea, hangout a few hours, and go home. Something about just getting out of the house for a little while can help. - Try actually getting ready for work! Don't just roll out of bed and into the office, go for a run or do a quick workout, take a shower, get dressed, heck even just drive a loop around your neighborhood and come back. It'll help get your mind in a work mindset - Keep getting interrupted when you don't need to be? Get a colored lightbulb and internet of things it into a, "hey I'm on a call" light to let your family know not to come in right now... [https://github.com/burkeholland/house-bot](https://github.com/burkeholland/house-bot?ref=jonathancreamer.com) - Stop working at some point! One thing I've found difficult is to separate work from home. On days when I WFH I often work way later than I would have at the office. Try really hard to stop when you normally would. I have a hue light setup in my house and I'll even turn my office to like a red color at 5:30pm to remind myself to stop working. Hope some of the above tips can help you improve the quality of your remote working experience! Let me know if you have any other cool ideas for me to add to the list. ### Announcing Div Ops as the Slack, Discord and Reddit communities URL: https://www.jonathancreamer.com/announcing-div-ops/ Last updated: 2021-08-05T16:00:32.000Z tldr; Let's unite around the idea of frontend tooling by creating a #DivOps community. - Join our [Discord](https://discord.gg/Vvk67JDX6t?ref=jonathancreamer.com) - [https://join.slack.com/t/divopsdevs/shared\_invite/enQtOTcxMjQ5MzUxMjcwLWIwNGQzMmRjNjkwMWNjNmEyMjY0ZmRjNzRkNzgzMTgxNjJiNjNlZTg2MmIzYmQyOTg1MmEzOWJjMWZiN2IxMWE](https://join.slack.com/t/divopsdevs/shared%5Finvite/enQtOTcxMjQ5MzUxMjcwLWIwNGQzMmRjNjkwMWNjNmEyMjY0ZmRjNzRkNzgzMTgxNjJiNjNlZTg2MmIzYmQyOTg1MmEzOWJjMWZiN2IxMWE?ref=jonathancreamer.com) (being deprecated for slack, but feel free to hang for now) - [https://www.reddit.com/r/divops/](https://www.reddit.com/r/divops/?ref=jonathancreamer.com) - [https://www.divops.dev/](https://www.divops.dev/?ref=jonathancreamer.com) (help wanted, I'm not a designer) - [https://github.com/divopsdevs/divopsdevs.github.io](https://github.com/divopsdevs/divopsdevs.github.io?ref=jonathancreamer.com) ## What am I even? A few months ago, my co-worker [Kyle Welch](https://twitter.com/kylewelch?ref=jonathancreamer.com) and I were chatting about what we do on our team here at Eventbrite. We are on the Frontend Platform team, but here's the thing, he and I both spend a HUGE majority of our time NOT writing "frontend" things. We spend most of our time writing Dockerfiles, updating CI pipelines, writing CLI's in Bash or Typescript, etc. So, naturally I went to Twitter to figure out what we should call ourselves... > So, if I'm a frontend engineer, but all I do all day is write Jenkins pipelines, bash scripts, and Dockerfile's, what am I really? > > The complexity of maintaining a complex FE build system for a team of hundreds of engineers is crazy. > > What should the title be? [#discuss](https://twitter.com/hashtag/discuss?src=hash&ref%5Fsrc=twsrc%5Etfw&ref=jonathancreamer.com) > > — Jonathan Creamer (@jcreamer898) [September 9, 2019](https://twitter.com/jcreamer898/status/1171101726037987328?ref%5Fsrc=twsrc%5Etfw&ref=jonathancreamer.com) The most popular answer was "Frontend DevOps". Not bad. Does a decent job explaining what we do, but then, about a month later, this popped up on my radar thanks to [Ben Illegbodu](https://twitter.com/benmvp?ref=jonathancreamer.com) and [Enrique](https://twitter.com/chochosmx?ref=jonathancreamer.com)! > Frontend engineers who also manage infra should be called
ops > > — Enrique 🐘 (@chochosmx) [October 12, 2019](https://twitter.com/chochosmx/status/1183045782095699968?ref%5Fsrc=twsrc%5Etfw&ref=jonathancreamer.com) #DivOps it is! What a fun and cheeky name to describe what we do. After going making it through the holiday's I decided to try to turn this into a thing. Naturally, I went back to Twitter, and determined that the most sensible next step is to create a Slack group and Reddit. > To try to unite the [#frontend](https://twitter.com/hashtag/frontend?src=hash&ref%5Fsrc=twsrc%5Etfw&ref=jonathancreamer.com) folks who work on things like [#webpack](https://twitter.com/hashtag/webpack?src=hash&ref%5Fsrc=twsrc%5Etfw&ref=jonathancreamer.com) [#parcel](https://twitter.com/hashtag/parcel?src=hash&ref%5Fsrc=twsrc%5Etfw&ref=jonathancreamer.com) [#babel](https://twitter.com/hashtag/babel?src=hash&ref%5Fsrc=twsrc%5Etfw&ref=jonathancreamer.com), and/or [#docker](https://twitter.com/hashtag/docker?src=hash&ref%5Fsrc=twsrc%5Etfw&ref=jonathancreamer.com), [#CI](https://twitter.com/hashtag/CI?src=hash&ref%5Fsrc=twsrc%5Etfw&ref=jonathancreamer.com), [#jenkins](https://twitter.com/hashtag/jenkins?src=hash&ref%5Fsrc=twsrc%5Etfw&ref=jonathancreamer.com), etc. > > I'd love to make [#divops](https://twitter.com/hashtag/divops?src=hash&ref%5Fsrc=twsrc%5Etfw&ref=jonathancreamer.com) a thing! > > Slack... [https://t.co/fETM9NSryw](https://t.co/fETM9NSryw?ref=jonathancreamer.com) > Reddit...[https://t.co/5wtaqvsSwP](https://t.co/5wtaqvsSwP?ref=jonathancreamer.com)[#divops](https://twitter.com/hashtag/divops?src=hash&ref%5Fsrc=twsrc%5Etfw&ref=jonathancreamer.com) on the Twitters. > > Help me make it so. [pic.twitter.com/4JhTYBThQc](https://t.co/4JhTYBThQc?ref=jonathancreamer.com) > > — Jonathan Creamer (@jcreamer898) [January 21, 2020](https://twitter.com/jcreamer898/status/1219646832294481924?ref%5Fsrc=twsrc%5Etfw&ref=jonathancreamer.com) ## DivOps communities Here's the links below as well... - [https://join.slack.com/t/divopsdevs/shared\_invite/enQtOTcxMjQ5MzUxMjcwLWIwNGQzMmRjNjkwMWNjNmEyMjY0ZmRjNzRkNzgzMTgxNjJiNjNlZTg2MmIzYmQyOTg1MmEzOWJjMWZiN2IxMWE](https://join.slack.com/t/divopsdevs/shared%5Finvite/enQtOTcxMjQ5MzUxMjcwLWIwNGQzMmRjNjkwMWNjNmEyMjY0ZmRjNzRkNzgzMTgxNjJiNjNlZTg2MmIzYmQyOTg1MmEzOWJjMWZiN2IxMWE?ref=jonathancreamer.com) - [https://www.reddit.com/r/divops/](https://www.reddit.com/r/divops/?ref=jonathancreamer.com) - And #DivOps for Tweets. My friend [David Neal](https://twitter.com/reverentgeek?ref=jonathancreamer.com), being the wonderful man he is, also lent me his skillz to create this logo for us. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2020/01/divops.png) The idea here is, when my grandma or some friend of mine who doesn't understand what I do asks me what I do, the simplest way I've come to explain it is, "Well, we work on a tooling team that basically builds hammers for the rest of the engineers at our company". And the most awesome hammer I could think of was of course Mjornir. We'll see where this goes in 2020, but the big goal is to try to unite folks who spend a lot of their time working through things like, WebPack configuration, babelrc's, Jest, React setup, Jenkins pipelines, Circle CI, travis, Parcel... etc etc, and try to help facilitate some cross talk amongst companies. The Frontend of our industry has become increasingly more complex. I mean, just look at this... ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2020/01/frontend.png) Thanks to Frontend Masters for the above chart. [Front-end Developer Handbook 2019 - Learn the entire JavaScript, CSS and HTML development practice!A guide for front-end developers to equip themselves with latest learning resources and development tools in front-end engineering.![](https://frontendmasters.com/favicon.ico)Learn the entire JavaScript, CSS and HTML development practice!![](https://frontendmasters.com/books/front-end-handbook/2019/assets/images/FM_2019Cover_small.jpg)](https://frontendmasters.com/books/front-end-handbook/2019/?ref=jonathancreamer.com) It's quite frankly a daunting task to be a frontend engineer these days. The goal of all of the folks who work on frontend tooling aka #DivOps should be to at least attempt to hide a bit of that magic and allow teams to do what the need to do to ship code to their customers. So, join me in uniting towards that end. Even if the name DivOps doesn't stick, there should be a community of us who work together to support each other in creating better ways to build tooling for supporting our teams. ### How to successfully manage a large scale JavaScript monorepo aka megarepo URL: https://www.jonathancreamer.com/how-to-successfully-manage-a-monorepo-aka-megarepo/ Last updated: 2019-11-21T21:27:48.000Z First off, this is a follow up to a post I did recently! [Say bye to monorepos say hello to megareposBreaking up a monolith can be a daunting task. Oftentimes a monolith gets brokenup into many repos, and sometimes into a monorepo. But, here’s the problem I have with the term monorepoo. > Monorepos are NOT monoliths!I recently heard a talk by Marcel Cutts called... MonoRepos for the Masses\[https://www.youtube.com/watch?v=rdeBtjBNcDI&=&feature=youtu.be…![](https://www.jonathancreamer.com/favicon.ico)Jonathan CreamerJonathan Creamer![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2019/11/IMG_2348.JPG)](https://www.jonathancreamer.com/say-bye-to-monorepos-say-hello-to-megarepos/) tldr; of the previous post is, when switching from a monolith to a monorepo, call it a megarepo instead... ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2019/11/image.png) Rather than a Death Star sized monolith which is prone to weakness with a single proton torpedo, prefer a **megarepo**, a force of specialized ships and pilots, which when working in concert together, can turn the tides of the galaxy. ## Tools for managing a megarepo There are many tools already in the wild for managing a megarepo. They handle things like running scripts against each package, deploying packages to NPM, determining which packages have changed, etc. - [Lerna](https://github.com/lerna/lerna?ref=jonathancreamer.com) - [Bolt](https://github.com/boltpkg/bolt?ref=jonathancreamer.com) - [Rush](https://rushjs.io/?ref=jonathancreamer.com) - [Yarn Workspaces](https://yarnpkg.com/lang/en/docs/workspaces/?ref=jonathancreamer.com) Each has its own pros and cons as all tools do. The particular tool we'll discuss going forward in this post is Bolt. ## Bolt Bolt and lerna were both created by the same folks. Bolt was just an iteration on a lot of the ideas lerna had, but with a few deviations. Bolt is installed as a global module as well ```shell npm i -g bolt # or yarn add global bolt ``` Bolt is meant to be even more of a "project" based, and is actually kind of a wrapper around Yarn and Yarn Workspaces. It also locks the dependencies of the shared packages. Meaning, a single version of React, or React Router, etc is installed across all of the packages in the megarepo. There are a few reasons why this is done. In a bolt megarepo, unlike lerna, when you add a project to a package, it is also automatically added to the top level package.json. This makes installs a bit quicker and easier as you won't ever have forked dependencies like you can in Lerna. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2019/11/Lerna-vs-bolt.png) The above diagram illustrates how allowing packages to have differing versions of pacakges can cause the nested node\_modules directories to fork in many different places. This bloats the repo size as well as complexity of maintenance, and overall yarn install times. This also makes running the megarepo with Docker a bit less trivial as well ```Dockerfile FROM node:12 WORKDIR /code COPY package.json yarn.lock ./ RUN yarn RUN npm i -g bolt COPY . . RUN bolt ``` With lerna and other monorepo tools, you have to constantly add new lines like... ```Dockerfile COPY ./packages/alpha/package.json /code/packages/alpha/package.json COPY ./packages/beta/package.json /code/packages/beta/package.json COPY ./packages/charlie/package.json /code/packages/charlie/package.json ``` But, since `bolt` has all the dependencies of every package listed at the top level, it's much simpler and faster to install all the dependencies. It also makes testing upgrades easier because the internal dependency graph of bolt makes it to where testing upgrades across packages is very simple. You can bump a package, run the tests and see how you're upgrade affects other parts of the megraepo. ## Structure of a megarepo Here is an example project... [jcreamer898/bolt-demoRepo for demonstrating how to build a bolt megarepo - jcreamer898/bolt-demo![](https://github.githubassets.com/favicon.ico)GitHubjcreamer898![](https://avatars3.githubusercontent.com/u/472487?s=400&v=4)](https://github.com/jcreamer898/bolt-demo?ref=jonathancreamer.com) Most of the megarepo tools have similar ideas about how to structure themselves. In general there's a top level `package.json` folder, and a `packages` directory. At least in the case of working with `bolt` and `lerna`. For lerna, a `lerna.json` file is created which contains a tiny bit of config for setting which packages are a part of the And with `bolt` the configuration is done in the `package.json`. ```JSON { "name": "rebel-alliance", "private": true, "bolt": { "workspaces": [ "./packages/*/*" ] }, "dependencies": { "react": "^16.12.0", "react-dom": "^16.12.0" } } ``` ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2019/11/Screen-Shot-on-2019-11-20-at-11-13-44.png) See in the above how react in each package is just a symlink to the top level node\_modules directory. ## Creating shared tooling One of the major benefits of having a megarepo is the ability to have one set of tooling which builds, tests, and deploys every package. It takes the worry out of having to configure all of our mountains of front end code across multiple repos. To get started... Create a `build` directory and add it to the `bolt` section of the `package.json` ``` "bolt": { "workspaces": [ "./packages/*/*", "./build/*" ] }, ``` Let's create a builder project called hoth in `./build/hoth`. We'll also use a library called scritch to help us create a super easy CLI... [scritchA small CLI to help you write sharable scripts for your team![](https://static.npmjs.com/1996fcfdf7ca81ea795f67f093d7f449.png)npm![](https://static.npmjs.com/338e4905a2684ca96e08c7780fc68412.png)](https://www.npmjs.com/package/scritch?ref=jonathancreamer.com) ## Transpiling with Babel Babel is undoubtedly going to be the first thing we need in our project. So, in hoth, add a few things... First, a `package.json`... ```JSON { "name": "@rebels/hoth", "version": "1.0.0", "dependencies": { "@babel/cli": "^7.7.0", "@babel/core": "^7.7.2", "@babel/plugin-proposal-class-properties": "^7.7.0", "@babel/plugin-syntax-dynamic-import": "^7.2.0", "@babel/preset-env": "^7.7.1", "@babel/preset-react": "^7.7.0", "@babel/preset-typescript": "^7.7.2", "scritch": "^1.3.1" } } ``` Add a `cli.js` file... ```shell #!/usr/bin/env node require("scritch")(__dirname); ``` Scritch will scan the `scripts` directory and auto generate a CLI. Then a `scripts` directory with a `babel.sh` script in it... ```shell #!/usr/bin/env bash set -e bolt workspaces exec \ --parallel-nodes \ -- \ babel \ --extensions .ts,.tsx,.js,.jsx \ --root-mode upward \ --source-maps true \ -Dd dist \ src ``` Don't forget to `chmod +x ./build/hoth/scripts/babel.sh` as well as the `cli.js` too. This is to make these scripts executable. Make sure and always do that for any new script added. This will run `babel` in every workspace in parallel. Then let's create a `src/configs` directory in hoth, and add `babel.js` ```javascript module.exports = { presets: [ [ '@babel/env', { targets: { browsers: ['last 2 versions'], }, }, ], '@babel/react', '@babel/typescript', ], plugins: [ '@babel/proposal-class-properties', '@babel/plugin-syntax-dynamic-import', ], }; ``` Next, since we are placing these config files in a package, we have to add some top level files that point to these... ```js // babel.config.js module.exports = require('./build/hoth/src/configs/babel'); ``` One more thing to make life easy is, you can hop up to the top level package.json and add... ```json "scripts": { "hoth": "./build/hoth/cli.js", "build": "yarn hoth babel" }, ``` Now, what you can do is... ```shell > bolt hoth > bolt build ``` `bolt hoth` will run scritch to tell you what all scripts you have available, and `bolt build` will run the `babel.js` script to run babel in every package. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2019/11/Screen-Shot-on-2019-11-20-at-12-14-34.png) ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2019/11/Screen-Shot-on-2019-11-20-at-12-14-47.png) We're well on our way now! Since we're using the `@rebels` scope, we'll need to make sure that babel knows how to properly resolve that scope. Let's say we add a new package called `@rebels/endor` for shared utils, ```js const resolver = { root: ['.'], alias: { '@rebels/alpha': './packages/theme-two/endor/src', '@rebels/beta': './packages/theme-three/endor/src', '@rebels/charlie': './packages/theme-one/charlie/src', '@rebels/endor': './packages/theme-one/endor/src', }, }; module.exports = { presets: [ [ '@babel/env', { targets: { browsers: ['last 2 versions'], }, }, ], '@babel/react', '@babel/typescript', ], plugins: [ '@babel/proposal-class-properties', '@babel/plugin-syntax-dynamic-import', ['module-resolver', resolver], ], }; ``` The `alias` piece could be configured to be dynamic as well by reading in all the packages and setting them up with so packages aren't manually added. The premise here though is the resolver will at compile time swap `@rebels/endor` or any of the packages for its actual location on the file system at transpile time. This is mostly used for local development as ideally during a production build you'll run the `bolt build` step anyways. So, you can consider wrapping the resolver stuff up in a check for local dev or not. ## Prettier Let's add a format script which will run prettier on everything because who wants to format their code anyways. Add a `prettier.js` to the configs folder. ```js module.exports = { tabWidth: 4, arrowParens: 'always', trailingComma: 'all', proseWrap: 'always', singleQuote: true, overrides: [ { files: '**/package.json', options: { tabWidth: 2, }, }, ], }; ``` And add the prettier.js at the root of the project... ```javascript // prettier.config.js module.exports = require('./build/hoth/src/configs/prettier'); ``` You can add dependencies just like you would with yarn. ```shell bolt workspace @rebels/hoth add prettier ``` Now we'll add another script to `./build/hoth/scripts`. ```shell #!/usr/bin/env bash set -e prettier --write '**/src/**/*.{ts,tsx,js}' ``` Now you can run... ```shell bolt hoth format ``` ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2019/11/Screen-Shot-on-2019-11-20-at-12-22-00.png) Which will format all the code in the repo. You can continue adding more and more scripts to the CLI as needed for you and your team. ## Typescript We're relying on the `@babel/preset-typescript` to remove the types from the `.ts` files as shown in the babel config previously. We'll use `typescript` itself with `noEmit: true` to only do type checking. We can setup typescript type checking by adding a `tsconfig.json` in our configs directory, and linking it at the top level like with babel and prettier. For TypeScript we first run `bolt w @rebels/hoth add typescript`, and then add a `./build/hoth/src/configs/tsconfig.json`... ```json { "exclude": ["**/node_modules/**", "**/test/**"], "compilerOptions": { "target": "es5", "module": "commonjs", "lib": ["dom", "es2015"], "jsx": "react", "outDir": "./dist", "noEmit": true, "downlevelIteration": true, "strict": true, "moduleResolution": "node", "esModuleInterop": true } } ``` And at the top level add a tsconfig... ```json { "compilerOptions": { "resolveJsonModule": true, "paths": { "@rebels/alpha": ["packages/theme-one/alpha/src/index"], "@rebels/beta": ["packages/theme-one/beta/src/index"], "@rebels/charlie": ["packages/theme-one/charlie/src/index"] }, "baseUrl": ".", "rootDir": "." }, "extends": "./build/hoth/src/tsconfig.json", "include": ["./types/**/*", "./packages/**/*", "build/**/*"] } ``` In here you can see we've also specified some paths. This will help TypeScript know what the `@rebels` prefix points to. Now we can add a `typecheck.sh` script into the cli... ```bash #!/usr/bin/env bash set -e tsc ``` The final thing to make sure we have installed is the `@types/react` package. ```bash bolt w @rebels/alpha add @types/react bolt w @rebels/beta add @types/react bolt w @rebels/charlie add @types/react ``` Now running `bolt hoth typecheck` will perform ONLY typechecking. ## Jest Setting up jest requires a few additional config files, but nothing we can't handle. Add a `./build/hoth/src/configs/jest.js` file... ```js module.exports = { resolver: require.resolve('./jest/resolver.js'), testPathIgnorePatterns: ['dist'], moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'], transform: { '^.+\\.(js|jsx|ts|tsx)$': require.resolve('./jest/transformer.js'), }, }; ``` And a `./build/hoth/src/configs/jest/transformer.js` ```js const babelJest = require('babel-jest'); module.exports = babelJest.createTransformer({ configFile: './babel.config.js', }); ``` The resolver is where things get interesting... Let's say in the future you have a package which imports another package. ```ts import { ewoks } from "@rebels/endor"; ``` Well, jest will look in the `dist` folder of `@rebels/endor` . That's not good... So, we can use `jest-enhanced-resolve` to update the mainFields for where to find source code during imports. ```js 'use strict'; let createResolve = require('jest-enhanced-resolve').default; let resolve = createResolve({ mainFields: ['rebels:source', 'main'], extensions: ['.js', '.jsx', '.ts', '.tsx'], }); function resolver(modulePath, opts) { return resolve(modulePath, opts); } module.exports = resolver; ``` Then just make sure to add `"rebels:source": "./src/index.ts"` in all the package.json files. The final thing is to add your `test.sh` script.. ```bash #!/usr/bin/env bash set -e jest ``` Now you can run `bolt hoth test` or add it to the `"scripts"` block to simply do `bolt test`. ## Wrapping up This is all just the start of working with a megarepo! There's so many more things you can add. Webpack, a development server, versioning the packages, etc. All things to keep an eye out for in future posts. [Building a megarepo with webpack · Issue #2 · jcreamer898/blog-post-ideasGotta do it.![](https://github.githubassets.com/favicon.ico)GitHubjcreamer898![](https://avatars3.githubusercontent.com/u/472487?s=400&v=4)](https://github.com/jcreamer898/blog-post-ideas/issues/2?ref=jonathancreamer.com) [Creating a common local dev server for a megarepo · Issue #3 · jcreamer898/blog-post-ideasGotta talk about how to serve apps locally![](https://github.githubassets.com/favicon.ico)GitHubjcreamer898![](https://avatars3.githubusercontent.com/u/472487?s=400&v=4)](https://github.com/jcreamer898/blog-post-ideas/issues/3?ref=jonathancreamer.com) [Using changesets/cli to version packages in a megarepo · Issue #4 · jcreamer898/blog-post-ideasVersioning is hard, but not with changesets![](https://github.githubassets.com/favicon.ico)GitHubjcreamer898![](https://avatars3.githubusercontent.com/u/472487?s=400&v=4)](https://github.com/jcreamer898/blog-post-ideas/issues/4?ref=jonathancreamer.com) ### Say bye to monorepos say hello to megarepos URL: https://www.jonathancreamer.com/say-bye-to-monorepos-say-hello-to-megarepos/ Last updated: 2019-11-04T14:16:48.000Z Breaking up a monolith can be a daunting task. Oftentimes a monolith gets broken up into many repos, and sometimes into a monorepo. But, here's the problem I have with the term monorepoo. > Monorepos are NOT monoliths! I recently heard a talk by Marcel Cutts called... [MonoRepos for the Masses](https://www.youtube.com/watch?v=rdeBtjBNcDI&=&feature=youtu.be&ref=jonathancreamer.com). It's a great talk about the history of monorepos and big companies using them. I LOVED that talk. Definitely give it a listen. Mono, obviously is a word that comes from latin which means, "one". The fact that the word "mono" is used in both monorepo, and monlith, tends to lend itself to a lot of confusion. ## MonoLITH First, off, monolith in an of itself might not be a bad thing for your company/team. There are a lot of companies, particularly startups, that have done great things with monoliths. In fact, Martin Fowler notes that most microservice architectures that started as microservice vs starting as a monolith and getting broken up, tend to result in bad architectures. [bliki: MonolithFirstGoing directly to a microservices architecture is risky, so consider building a monolithic system first. Split to microservices when, and if, you need it.martinfowler.comMartin Fowler![](https://martinfowler.com/bliki/images/microservice-verdict/path.png)](https://martinfowler.com/bliki/MonolithFirst.html?ref=jonathancreamer.com) However, I think the drawback starts to happen when you get to a point where hundreds of folks are constantly working and committing code into it. You can very easily end up with the \[Big Ball of Mud\]([https://exceptionnotfound.net/big-ball-of-mud-the-daily-software-anti-pattern/](https://exceptionnotfound.net/big-ball-of-mud-the-daily-software-anti-pattern/?ref=jonathancreamer.com)) anti-pattern. A monolith can be come a castle housing your most precious of code; an impenetrable fortress surrounded by a crocodile filled moat of unforgiving technical debt. If you're not careful, very tight coupling between different parts of the monolith can make it impossible to ship one part of the application without shipping the whole thing, and make it very difficult to refactor into smaller pieces as well. ## MonoREPO Here's where things get interesting. So, yes, the word "mono" exists in both places, but that's not to say that they do and serve the same exact purpose. There are a lot great reasons to keep all of your code in a single repository, just like you would in a monolith. The distinction however though is that, unlike a monoLITH, a monoREPO is *usually* just a lot of packages sharing the same code base. Some of these packages are dependent on other packages sure, but in no way does the monorepo have to ship all at once as is the case with a monolith. Nor does it mean that the repo is a big ball of mud with muddied concerns, and tight coupling. It's for the above reasons that the term monorepo should be replaced by "megarepo". ## s/mono/mega/ > I propose we call them "megarepos" going forward. Too many people conflate the term "monolith" with "monorepo". Let's fix it. [#divops](https://twitter.com/hashtag/divops?src=hash&ref%5Fsrc=twsrc%5Etfw&ref=jonathancreamer.com) > > — Jonathan Creamer (@jcreamer898) [October 31, 2019](https://twitter.com/jcreamer898/status/1189887019020894208?ref%5Fsrc=twsrc%5Etfw&ref=jonathancreamer.com) The mono/mega repo should be defined as... > A bunch of packages in once place with shared tooling to facilitate collaboration, increase productivity, and create a single culture. A few highlights from using a megarepo are: - Consistent build tooling - Less context switching - Much MUCH easier to update shared code - Easier to affect change across the platform - Code reviews are centralized In the wonderful world of a megarepo, you can in fact see the forest for the trees. Meaning, you get a sense of what all is available to you as a member of a larger organization. If the packages in your megarepo have tooling which enables them to be easily tested, shared, and used as isolated units, you end up in a world where your application itself is the component library. Disparate packages which, in a multirepo setup, might become difficult to discover are easy to find and work with as they are written, tested, and documented all in the same place. The megarepo often has a group of shared dependencies as well. In the case of a frontend project with a React toolchain, the base level React version might be shared across packages, or there maybe shared components which have a shared version across packages. This is not a drawback of a mega, it's a plus! Now you get to live in a world where an update to a shared package can all at once have every single dependent package run a test suite to verify that it didn't break. This makes it so much easier to maintain consistency across your entire application. Nothing is without its negative sides of course. There is no such thing as a silver bullet, and in no way is a megarepo the right thing for every team, in every situation. Just like any tool, it should be thought out extensively before put into practice. One of the biggest obstacles in working this way is, it requires a good deal more tooling to work with. So, if you're at a company where there's no team managing the platform of the megarepo itself, it can be a difficult task to maintain it. That being said, when you do have a team dedicated to writing tools for the megarepo, it can be a fantastic option, and I have to give credit to [Ben Ilegbodu](https://twitter.com/benmvp?ref=jonathancreamer.com), and *maybe* [Kyle Welch](https://twitter.com/kylewelch?ref=jonathancreamer.com) for helping me come up with the term. > I'll give you microapps but megarepos was me. My how quickly revisionist history happens after leaving! > > January 2020: "Ben who???" > > — Ben Ilegbodu 🏀 (@benmvp) [October 31, 2019](https://twitter.com/benmvp/status/1189943361785888769?ref%5Fsrc=twsrc%5Etfw&ref=jonathancreamer.com) ## Conclusion A move away from the word monorepo will hopefully convey the fact that even though code might share the same repo, it is not in any way, a monolith. I love the idea of creating a single culture, it was a great take away from Marcell's talk. So, how about going forward, rather than talking about monorepos, we talk about megarepos, and how much benefit they can bring to a large team. ## // TODO: write a post about how to implement one [How to successfully implement a megarepo · Issue #1 · jcreamer898/blog-post-ideasNeeds to discuss options like Bolt and Lerna. Talk about the build tooling.![](https://github.githubassets.com/favicon.ico)GitHubjcreamer898![](https://avatars3.githubusercontent.com/u/472487?s=400&v=4)](https://github.com/jcreamer898/blog-post-ideas/issues/1?ref=jonathancreamer.com) Any other topics you want to hear more about? [Add an idea](https://github.com/jcreamer898/blog-post-ideas/issues/new?ref=jonathancreamer.com). ### Running Cypress with Docker inside CI URL: https://www.jonathancreamer.com/running-cypress-with-docker-inside-jenkins/ Last updated: 2019-11-04T14:29:15.000Z I've been looking at Cypress lately as a solution to do better end to end testing, and even in some cases to just dodge TDD altogether, and rely on seeing my code work in a browser like environment and actually see the results. Locally, Cypress runs with ease, just `cypress open` or `cypress run`, and voila. The problem has come from getting it running in CI. ## Insert your CI of Choice Jenkins is the CI of choice at Eventbrite, and we use containers to build and test things in our Jenkins workers. The big issue has been, how do I get our local dev server started, THEN run cypress? The following instructions are really universal to any CI. The following is my current solution, but I'm all ears for a better one if such a solution exists... The Dockerfile above creates a container with all the tools needed to run Cypress. Then the bash script does a few things. First it runs adds an entry to the hosts file because we talk over that domain to a QA server for data. Then `yarn dev` actually runs the server, now this being a dev server, it can take a second to spin up. Therefore, a while loop curls the server until it's ready. Finally, we run `cypress run` and output mocha/junit style xml results so Jenkins can show us nice failure messages. Again, not sure if this is janky or elegant, but it seems to work, and I'm all for things working. If you or your team have a similar solution for your CI, particularly if it's Jenkins, reach out and let me know! ### How WebPack decides what entry to load from a package.json URL: https://www.jonathancreamer.com/how-webpack-decides-what-entry-to-load-from-a-package-json/ Last updated: 2019-05-08T13:04:36.000Z Today I was working on creating a node.js bundle using webpack 4, and came across a fun little doozie of an error which lead me to do a bit of code spelunkery into how Webpack actually decides on what to load when you `require` something from `node_modules`. Most `package.json` files have a `main` in them, because it tells the "requirer" of the package what the entry point of the package is. > The main field is a module ID that is the primary entry point to your program. [https://docs.npmjs.com/files/package.json#main](https://docs.npmjs.com/files/package.json?ref=jonathancreamer.com#main) Well, there's also a spec for defining other targets, namely the "browser" field. [https://docs.npmjs.com/files/package.json#browser](https://docs.npmjs.com/files/package.json?ref=jonathancreamer.com#browser) There is even a proposal to add a "module" field. [https://github.com/dherman/defense-of-dot-js/blob/master/proposal.md](https://github.com/dherman/defense-of-dot-js/blob/master/proposal.md?ref=jonathancreamer.com), which some libraries are already taking advantage of, and Webpack can also understand it. In terms of Webpack, the 3 most important ones are `main`, `browser`, and `module`. - `browser` should point to a browser compatible bundle - `module` should point to a folder of ES modules - `main` should point to whatever whatever you want the default entry to be If the `target` of your app is `web` or a few others (which is default). It will look first at the `browser` field, and if it doesn't exist, it'll look for the `module`, and lastly `main`. ``` if ( options.target === "web" || options.target === "webworker" || options.target === "electron-renderer" ) { return ["browser", "module", "main"]; } else { return ["module", "main"]; } ``` [https://github.com/webpack/webpack/blob/52184b897f40c75560b3630e43ca642fcac7e2cf/lib/WebpackOptionsDefaulter.js#L336](https://github.com/webpack/webpack/blob/52184b897f40c75560b3630e43ca642fcac7e2cf/lib/WebpackOptionsDefaulter.js?ref=jonathancreamer.com#L336). If your package is simply only meant to work in the browser, you can get away with only using `main`, but if you're looking to support both the web and node, then take advantage of the `browser` field by pointing it to your browser compatible bundle. For me what was happening was, there's an import in one of our libraries for `isomorphic-fetch`. Well, since the target is `node` in the Webpack build I was working with, `node-fetch` actually got required. And `node-fetch` requires a library which can't be lazily loaded by Webpack. Aka this issue. [https://github.com/andris9/encoding/issues/18](https://github.com/andris9/encoding/issues/18?ref=jonathancreamer.com). **tldr; if your Webpack target is `node`, it looks at the `module` and `main` for entry. Otherwise, it goes to the `browser`, then `module`, then `main`.** ### WebPack production environment is NOT the NODE_ENV or BABEL_ENV environment variable URL: https://www.jonathancreamer.com/webpack-production-environment-is-not-the-node_env-or-babel_env-environment-variable/ Last updated: 2019-04-12T16:00:49.000Z A revelation came to me the other day when I was reviewing some of our bundling processes at Eventbrite. We currently run production bundles like this... ``` node --max_old_space_size=4096\ ./node_modules/.bin/webpack \ --bail \ --config-name node \ --env.production \ --config ./config/webpack.production.config.js ``` Notice the `--env.production` in there. We also use the `babel-loader` along with the `babel-preset-env` plugin as any good citizen would. Here's what's interesting. > `--env.production` does NOT set `NODE_ENV=production` I proceeded to hover over the `environment` key on my .babelrc in VSCode, and got this little nugget. ![](https://d.pr/i/VbEYd6+) I then found the exact quote from the old [62.6 docs](https://babeljs.io/docs/en/6.26.3/babelrc?ref=jonathancreamer.com#env-option) (we're still on Babel 6 for now). > The env key will be taken from process.env.BABEL\_ENV, when this is not available then it uses process.env.NODE\_ENV if even that is not available then it defaults to "development". Alright so basically that means we've been running Babel as dev mode! ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2019/04/doh.gif) What does that `--env.production` thing even do? Well, according to [https://webpack.js.org/guides/environment-variables](https://webpack.js.org/guides/environment-variables?ref=jonathancreamer.com), all it does is make it so when you setup your webpack config, you actually export a function that gives you an `env`, and evidently you can also set your actual NODE\_ENV like... ``` webpack --env.NODE_ENV=local --env.production --progress ``` Then you you can get that `env` in the config callback. ``` module.exports = env => { // Use env. here: console.log('NODE_ENV: ', env.NODE_ENV); // 'local' console.log('Production: ', env.production); // true ``` ### DefinePlugin Here's the thing though, just running `NODE_ENV=production webpack` doesn't necessarily give you production bundles either. `NODE_ENV=production` just tells node in what mode to actually run webpack. Meaning, if you have code in your app which does... ``` if (process.env.NODE_ENV === 'production') { // do production things } ``` It will simply leave those in place. In order to make your output code reflect the proper NODE\_ENV you have to use either the EnvironmentPlugin or the DefinePlugin. (The EnvironmentPlugin uses Define under the covers). I personally prefer the explicitness of the DefinePlugin... ``` new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV), 'process.env.DEBUG': JSON.stringify(process.env.DEBUG) }); ``` Or if you had passed in the `--env.production` thing, you could do like... ``` new webpack.DefinePlugin({ 'process.env.NODE_ENV': env.production ? JSON.stringify('production'), JSON.stringify('development'), 'process.env.DEBUG': JSON.stringify(process.env.DEBUG) }); ``` p.s. You have to do the `JSON.stringify` thing so that in your code it'll do... ``` if (process.env.NODE_ENV === 'production') // Converts the above to if ('production' === 'production') ``` ### webpack 4 In webpack 4, what's nice is, there is a webpack configuration setting called `mode`. And when you set `mode: "production"`, it goes ahead and sets up the DefinePlugin for you. However, you still may run into a scenario where you need to make sure that the `babel-loader` knows your `NODE_ENV=production`. So, just keep a close eye on it. # tldr; Make sure when you're doing production webpack builds involving babel, particularly in webpack 3 where you don't have the `mode` option, make sure to set `NODE_ENV=production` when you run webpack. ### Brite future URL: https://www.jonathancreamer.com/brite-future/ Last updated: 2019-11-04T18:27:00.000Z The past almost 4 years I've spent at Lonely Planet are coming to an end, and I'm excited to be joining the Front End Platform Team at Eventbrite! When I started at Lonely Planet 4 years ago. React was barely a thing, node was at version 0.10.33, and Angular was juggernaut framework everyone was learning. I've learned a ton and can't thank my team enough for the fun we've had over the last few years. Eventbrite has a great set of values starting with... > Bringing the world together through live experiences It'll be great to be a part of the team that helps bring not only the Eventbrite engineering team together, but also helps build a platform for anyone to enjoy experiences together. It's never easy to leave a job, especially after being there almost 4 years. But, I am very excited about what the future holds and can't wait to start. ### Why would you NOT use TypeScript? URL: https://www.jonathancreamer.com/why-would-you-not-use-typescript/ Last updated: 2019-11-04T18:27:33.000Z In a world where JavaScript is arguably the [most popular](https://insights.stackoverflow.com/survey/2017?ref=jonathancreamer.com#technology) tool for building software these days, it seems like it's everywhere now. With node.js, it's on the backend, with Electron it's native on your machine, with React Native, it's native on your phone. There's no doubt that JavaScript is nothing but at least pervasive across so many ecosystems. So, the next question I have is, if JavaScript is so popular, then TypeScript by nature of what is is, also should be popular. After all, in case you didn't realize it... > Any JavaScript you can possibly write which is at least ECMA stage 3 is valid TypeScript. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2017/12/itstrue.gif) ### VSCode First and foremost, if you're not using Visual Studio Code to write JavaScript, you should be, so [go get it](https://code.visualstudio.com/?ref=jonathancreamer.com), and also go get [all this stuff](http://vscodecandothat.com/?ref=jonathancreamer.com) from [Burke Holland](https://twitter.com/burkeholland?ref=jonathancreamer.com). Under the covers, the TypeScript compiler will do a lot of amazing things for you without you even having to think twice about it. The reason it's able to do this is, VS Code is running your JavaScript through the TypeScript compiler whether you know it or not! [https://github.com/Microsoft/TypeScript/wiki/JavaScript-Language-Service-in-Visual-Studio](https://github.com/Microsoft/TypeScript/wiki/JavaScript-Language-Service-in-Visual-Studio?ref=jonathancreamer.com) On top of that, it also uses something called Automatic Type Definitions using the phenomenal [Definitely Typed](https://github.com/DefinitelyTyped/DefinitelyTyped?ref=jonathancreamer.com) library of type definitions to automatically download types for thousands of popular JavaScript libraries. ### From JS to TS, TypeScript's got you In the following example, we're simply formatting a price string. ```javascript const formatPrice = (num, symbol = "$") => `${symbol}${num.toFixed(2)}`; formatPrice("1234"); ``` It could be easy to forget that if pass a string here, this function will asplode because `toFixed` doesn't exist on a string. Simply adding types can save you runtime bugs... ```typescript const formatPrice = (num: number, symbol = "$": string) => `${symbol}${num.toFixed(2)}`; formatPrice("1234"); // num.toFixed is not a function ``` But, there's even better news... ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2018/01/jstscheckmorpheus.jpg) You may or may not already be a big user of JSDoc, but if you are, you will be pleased as punch to know that as of a recent version of TypeScript, you can add `// @ts-check` to the top of a JavaScript file, and get typechcking in it! ``` // @ts-check /** * Format a price * @param num {number} The price * @param symbol {string} The currency symbol */ const formatPrice = (num, symbol = "$") => `${symbol}${num.toFixed(2)}`; formatPrice("1234"); ``` ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2018/01/format.png) Here's more info about what all you can do with JSDoc... [https://github.com/Microsoft/TypeScript/wiki/JSDoc-support-in-JavaScript](https://github.com/Microsoft/TypeScript/wiki/JSDoc-support-in-JavaScript?ref=jonathancreamer.com) With VSCode you can enable full type typechecking with the following User Settings option... ```javascript "javascript.implicitProjectConfig.checkJs": true ``` ### React Cool thing is, TypeScript also supports React out of the box by adding the following to your tsconfig... ```json { "jsx": "react" } ``` Now for the real fun... ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2018/01/reactproptypes.jpeg) PropTypes are a great way to catch runtime React bugs. But the frustrating thing about them is, you don't know if something is broken generally until your app builds, the browser or hot loading reloads, and you see a cryptic red error message in the console. Wouldn't it be nice to just catch that bug while working on the component? ```typescript import * as React from "react"; import formatPrice from "../utils/formatPrice"; export interface IPriceProps { num: number; symbol: "$" | "€" | "£"; } const Price: React.SFC = ({ num, symbol, }: IPriceProps) => (

{formatPrice(num, symbol)}

); ``` Now, check this out... ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2018/01/Screen-Capture-on-2018-01-29-at-22-52-24.gif) It's amazing to be able to get intellisense on props. You can start typing, or in VSCode hit Control + Space to pull open the Intellisense menu. You can even get intellisense on React classes as well... ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2018/01/Screen-Shot-on-2018-01-29-at-22_55_40.png) ### Conclusion Whether or not you decide to go full on TypeScript, it's clear you can see many benefits even if you stick with pure JavaScript. ### Using do expressions in redux reducers URL: https://www.jonathancreamer.com/using-do-expressions-in-redux-reducers/ Last updated: 2017-09-26T20:59:09.000Z There's an interesting proposal that's stage 1 currently (as of 9/26/2017) called `do expressions`. Thanks to the magic that is Babel, you can already go ahead and try this out with the do expression transform... [https://babeljs.io/docs/plugins/transform-do-expressions/](https://babeljs.io/docs/plugins/transform-do-expressions/?ref=jonathancreamer.com). The basic idea is that you can have a block of code such as a few `if` statements, or a `try catch`, wrap them in a `do { }`, and whatever the final statement evaluates to, can be returned. For example, have a look at a standard if block... ```prettyprint function fizzBuzz() { let str; for (var i=1; i <= 20; i++) { if (i % 15 == 0) { str = "FizzBuzz"; } else if (i % 3 == 0) { str = "Fizz"; } else if (i % 5 == 0) { str = "Buzz"; } else { str = i; } } return str; } ``` Here you have to initialize str to something, then reassign it in each `if` block. With `do expressions`, you can do the following... ```prettyprint function fizzBuzz() { return do { for (var i=1; i <= 20; i++) { if (i % 15 == 0) { "FizzBuzz"; } else if (i % 3 == 0) { "Fizz"; } else if (i % 5 == 0) { "Buzz"; } else { i; } } } } ``` With `do expressions`, you don't need to initialize `str`, you can simply return the entire `do expression`. Each `if` gets evaluated, and the one that ends up being truthy simply returns its value out of the expression. So, how about for a Redux reducer? Well, most Redux reducer's are written in `switch` statements. Not a big deal, but I was curious if I could use a `do` and some `if` statements since I'm not the biggest fan of `switch` statements. For example, this code is taken from a todomvc example... ```prettyprint function todos(state = initialState, action) { switch (action.type) { case ADD_TODO: { return [...state, { id: state.reduce((maxId, todo) => Math.max(todo.id, maxId), -1) + 1, completed: false, text: action.text }]; } case DELETE_TODO: { return state.filter(todo => todo.id !== action.id ); } default: return state; } } ``` Pretty straightforward, now check out the same reducer with `if`'s inside of a `do expression`... ```prettyprint function todos(state = initialState, { type, text, id }) { return do { if (type === ADD_TODO) { [...state, { id: state.reduce((maxId, todo) => Math.max(todo.id, maxId), -1) + 1, completed: false, text }]; } if (type === DELETE_TODO) { state.filter(todo => todo.id !== id); } state; } } ``` It's nice that you don't have to see `return ` but once, and we've gotten rid of the `switch`. Not sure which one is better, but it's cool to have different options to build reducers in. Feel free to discuss in the comments below! ### Debugging node and Jest tests with VS Code's debugger URL: https://www.jonathancreamer.com/debugging-node-and-jest-tests-with-vs-codes-debuggger/ Last updated: 2019-11-04T18:26:06.000Z [VS Code](https://code.visualstudio.com/?ref=jonathancreamer.com) has become my favorite IDE for pretty much everything these days. There are so many great features it has to offer including the ability to debug node.js. Here is the `launch.json` I used to get the debugger working for me. ### Getting started with TypeScript, WebPack, and React URL: https://www.jonathancreamer.com/getting-started-with-typescript-webpack-and-react/ Last updated: 2017-09-07T18:14:45.000Z We've been integrating TypeScript more and more into our workflow at Lonely Planet. I wanted to just quickly share how easy it is to get started working with TypeScript and Webpack! It takes a very simple webpack.config... ```prettyprint module.exports = { entry: "./src/index.tsx", devtool: "source-map", output: { filename: "./dist/bundle.js", }, resolve: { extensions: [".ts", ".tsx", ".js", ".jsx"], }, module: { rules: [{ test: /\.tsx?$/, loader: "ts-loader" }], }, }; ``` And a few `npm install`s... ``` "devDependencies": { "@types/react": "^16.0.5", "@types/react-dom": "^15.5.4", "react": "^15.6.1", "react-dom": "^15.6.1", "ts-loader": "^2.3.4", "typescript": "^2.5.2", "webpack": "^3.5.5", "webpack-dev-server": "^2.7.1" } ``` Then start writing TypeScript and React! Full code of the starter at [https://github.com/jcreamer898/typescript-webpack-react](https://github.com/jcreamer898/typescript-webpack-react?ref=jonathancreamer.com). ### Testing typescript classes with Jest and Jest Mocks URL: https://www.jonathancreamer.com/testing-typescript-classes-with-jest-and-jest-mocks/ Last updated: 2020-03-04T14:49:58.000Z Typescript is a powerful way to build applications. It offers type checking to catch errors before they make it to the browser. On top of just utilizing typescript to catch bugs, it's still important to make sure Typescript code is tested. Facebook released a testing framework called Jest a while ago as that contains many built in features. You can kind of compare Jest to Mocha in saying that Jest is to Mocha as Angular is to React. Jest is an entire test framework with built in mocking, code coverage, watching, assertions, etc. With a bit of config, you can easily begin testing Typescript with Jest, including setting up Mocks for testing classes. Let's say we want to create an `EmailService`... ```js export class EmailService implements ICommunicator { sender: ICommunicator; constructor(sender: ICommunicator) { this.sender = sender; } send(item: IEmail) { this.sender.send(item); } } ``` Here we're implementing an interface called `ICommunicator`... ```js export interface ICommunicator { send: (item: T) => void, } ``` This will allow us to implement any kind of communication interface, in the case we'll one for sending emails with `IEmail`... ```js export interface IEmail { to: string, from: string, subject: string, contents: string, } export class Email implements IEmail { to: string; from: string; subject: string; contents: string; constructor(to: string, from: string, subject: string, contents: string) { this.to = to; this.from = from; this.subject = subject; this.contents = contents; } } ``` So, in a unit test we want to test and make sure that when we create an `EmailService` that it will call the `ICommunicator`'s send method... First of all, let's get Jest setup. ```shell yarn add -D jest @types/jest ``` Then you need to add a few things to the `package.json`... ```js "jest": { "moduleFileExtensions": [ "ts", "tsx", "js" ], "transform": { "^.+\\.(ts|tsx)$": "/preprocessor.js" }, "testMatch": [ "**/__tests__/*.(ts|tsx)" ] } ``` Finally, create a `preprocesser.js` file... ```js const tsc = require('typescript'); const tsConfig = require('./tsconfig.json'); module.exports = { process(src, path) { if (path.endsWith('.ts') || path.endsWith('.tsx')) { return tsc.transpile( src, tsConfig.compilerOptions, path, [] ); } return src; }, }; ``` Now we'll be able to start writing some tests! ```js import { EmailService, ICommunicator, IEmail, Email } from "../"; describe("EmailService", () => { it("should send emails", () => { const Mock = jest.fn>(() => ({ send: jest.fn(), })); const mock = new Mock(); const instance = new EmailService(mock); instance.send(new Email("to@foo.com", "from@foo.com", "oh hai", "Some foo email...")); expect(mock.send).toHaveBeenCalled(); }); }); ``` First of all, we'll create a Mock implementation of an Email communicator with... ```js const Mock = jest.fn>(() => ({ send: jest.fn(), })); ``` Then create a couple of instances and call the `send` method... ```js const mock = new Mock(); const emailService = new EmailService(mock); const email = new Email("to@foo.com", "from@foo.com", "oh hai", "Some foo email..."); emailService.send(email); ``` Lastly, we'll make sure that by calling `emailService.send` calls the mock implementation of the `ICommunicator`... ```js expect(mock.send).toHaveBeenCalled(); ``` Hopefully that will help anyone looking to get started with Jest and Typescript! ### Chrome back button in express caching issue solved URL: https://www.jonathancreamer.com/chrome-back-button-in-express-caching-issue-solved/ Last updated: 2017-02-13T22:17:27.000Z While working in a React/express universal app, I created an express route that serves both JSON and HTML sort of like how Rails has a `respond_to` block. That way if I just add `.json` on to the end of some of my requests it will give me the JSON used to render that page. This caused an interesting issue in production. Occasionally when I was surfing, I would click on a link, and go forward in our universal app. Then, when I hit the back button, rather than seeing HTML I saw the JSON for the page? ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2017/02/excuseme.gif) Welp, turns out, Chrome doesn't take the headers into account for caching unless you tell it to. It usually only will use the URL and the request method (get, put, etc). In order for Chrome to cache differently based on the content/type, you have to add a header... ```prettyprint app.use((req, res, next) => { res.header("Vary", "X-Requested-With"); next(); }); ``` This will allow chrome to cache both the HTML and the JSON pages separately and prevent the issue from happening. ### Fetching data in a universal react router app with async actions URL: https://www.jonathancreamer.com/fetching-data-in-a-universal-react-router-app-with-async-actions/ Last updated: 2019-10-25T17:30:10.000Z One of the most difficult things when working in a universal app is fetching data on the client side. With es2017 and the React lifecycle, it can be done easily. I love `async` and `await` in [es2017](http://www.2ality.com/2016/02/async-functions.html?ref=jonathancreamer.com). If you haven't started using them yet. Do it NAOW. It even works in a lot of browsers natively now too! Including Microsoft Edge, Chrome, and node version 8... [http://kangax.github.io/compat-table/es2016plus/#test-async\_functions](http://kangax.github.io/compat-table/es2016plus/?ref=jonathancreamer.com#test-async%5Ffunctions) ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2017/01/asyncarnold.jpeg) Fetching data after a route transition in React Router can be confusing at first, but there's a fairly easy solution using the React lifecyle event `componentDidMount`. If you're working with a universal app, when you hit a component straight at it's url, say `/me/friends`, you'll already have the data via the initial state. But, when you come to that page via a client side react router render, how do you grab the data? For example, on [http://www.lonelyplanet.com/usa/nashville/restaurants/a/poi-eat/362228](http://www.lonelyplanet.com/usa/nashville/restaurants/a/poi-eat/362228?ref=jonathancreamer.com) you'll get a list of some of the best places to eat in Nashville. The list was rendered on the server side. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2017/01/list-1.png) Now when I click [Hattie B's](https://www.lonelyplanet.com/usa/nashville/restaurants/hattie-bs/a/poi-eat/1513072/362228?ref=jonathancreamer.com), we're going to use React Router to transition to a detail page for Hattie B's. All of that rendering will be done on the client side. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2017/01/hattieb.png) This is all done via the client side, and no full page render happens. Somehow though, we have to get the JSON data for Hattie B's so React has the data to render. ```prettyprint // user.js import React from "react"; import { connect } from "react-redux"; import Poi from "./poi"; import { fetchPoi } from "./actions"; export default class Restaurant extends Component { componentDidMount() { if (!this.props.poi) { this.props.fetchPoi(); } } render() { return ( ); } } const mapStateToProps = (state) => ({ poi: state.poi, }); const mapDispatchToProps = dispatch => bindActionCreators({ fetchPoi, }, dispatch); const connected = connect(mapStateToProps, mapDispatchToProps)(Restaurant); export { connected }; ``` The `componentDidMount` method only fires when the component renders on the client side. This means if you go directly to the Hattie B's page, the data will already have been fetch server side, so the `this.props.poi` will already be defined and passed through the page's `intitialState`. However, when you come to the detail page via the list, the POI will be empty because it hasn't been fetch yet. Now we can talk about how to use `async` and `await` from `es2017` to create a nice clean actionCreator. Make sure you have `babel-polyfill`, and either `babel-env` or `babel-preset-es2017` available to be able to use the `async await` goodies. ```prettyprint import { createAction } from "redux-actions"; export const FETCH_POI = "FETCH_POI"; export const FETCH_POI_DONE = "FETCH_POI_DONE"; const fetchPoiAction = createAction(FETCH_POI); const fetchPoiDoneAction = createAction(FETCH_POI_DONE); const fetchPoi = () => async (dispatch) => { dispatch(fetchPoiAction()); const response = await fetch("/path/to/poi.json"); const poi = await response.json(); dispatch(fetchPoiDoneAction(poi)); }; export { fetchPoi, }; ``` So here we create an `async` arrow function. We can use `redux-thunk` to get `dispatch` passed in as an argument. Then use the new [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch%5FAPI?ref=jonathancreamer.com) API to `await` for the `response`, `await` for the JSON to be downloaded, and then dispatch the final data so the reducer can add the POI to the state. From their `connect` simply passes the POI into the component. ### Conclusion Hopefully if you're working on a universal app with React Router, this will help you get your components loaded with data on the client side. Universal apps can definitely be tricky, but in the end, they're extremely powerful and fun to work with! ### How to quickly edit your hosts file on macOS URL: https://www.jonathancreamer.com/how-to-quickly-edit-your-hosts-file-on-macos/ Last updated: 2017-01-17T21:36:06.000Z I had a friend ask me the other day how to edit his hosts file for local development. It's something I take for granted because I'm so used to doing it, but for him it was something he hadn't done before. # Use arrow keys to move to what you want. I decided I'd throw together a quick 60 second video on how to do it... Here are the commands I used... ```prettyprint sudo vim /etc/hosts ``` Then use `i` for entering edit mode, move your cursor to where you want to edit. Enter the IP address and the hostname. Then it's `escape` to exit insert mode, `wq` to save and quit. ### Advanced WebPack Part 3 - Creating a custom notifier plugin URL: https://www.jonathancreamer.com/advanced-webpack-part-3-creating-a-custom-notifier-plugin/ Last updated: 2016-06-08T16:55:13.000Z At Lonely Planet we use the heck out of WebPack. It's an incredibly powerful tool. Sometimes it takes a while though. I decided today that rather than watching the command line while it compiles to know when I'm good to refresh the browser, I'd just learn how to write a plugin to notify me about it. Turns out it's pretty darn simple. ```prettyprint // ./notifier.js "use strict"; class Notifier { apply(compiler) { compiler.plugin("done", (stats) => { const pkg = require("./package.json"); const notifier = require("node-notifier"); const time = ((stats.endTime - stats.startTime) / 1000).toFixed(2); notifier.notify({ title: pkg.name, message: `WebPack is done!\n${stats.compilation.errors.length} errors in ${time}s`, contentImage: "https://path/to/your/logo.png", }); }); } } module.exports = Notifier; ``` Any `class` or `function` or even `object` with an `apply` function defined will automtically recieve an instance of the WebPack [Compiler](https://github.com/webpack/webpack/blob/master/lib/Compiler.js?ref=jonathancreamer.com). You can then hook into one of many different [lifecycle events](https://webpack.github.io/docs/plugins.html?ref=jonathancreamer.com). Then in your `webpack.config.js`... ```prettyprint const Notifier = require("./notifier"); // ... { plugins: [new Notifier()] } ``` There's a whole lot of cool things you can do by creating your own WebPack plugins! ### WebPack Code splitting with ES6 and Babel 6 URL: https://www.jonathancreamer.com/webpack-code-splitting-with-es6-and-babel-6/ Last updated: 2016-02-25T17:22:41.000Z Today is the day that finally, we decided it was time to make the move from Babel 5 to 6\. It's been on our Technical Debt list for quite a while now. Here's what we had to do to get it working! ### Update the package.json First and foremost was to get all the latest and greatest from the Babel folks... ![](http://d.pr/i/15CP5+) ```prettyprint dependencies: { "babel-core": "^6.5.2", "babel-polyfill": "^6.3.14", ... }, devDependencies: { "babel-loader": "^6.2.3", "babel-plugin-transform-decorators-legacy": "^1.3.4", "babel-preset-es2015": "^6.5.0", "babel-preset-react": "^6.5.0", ... } ``` That's easy enough. ### Update webpack.config.js In case you hadn't yet yeard, Babel 6 is quite a bit different in it's philosophy. What started out as 6to5 whose job it was to merely transpile ES6 to ES5, Babel has now evolved into a transpiler of all the things. Highly plugable, faster, and easier to configure, Babel 6 is a fantastic way to usher in a new era of JavaScript tooling. Ok, so in order to get Babel 6 working with your webpack setup, you have to change the loader... ```prettyprint test: /(\.jsx?)$/, loader: "babel", exclude: /node_modules/, query: { "plugins": ["transform-decorators-legacy"], "presets": ["es2015", "react"] } ``` That should get you most of the way there now. ### A thing we ran in to As we were doing the upgrade, we ran into an interesting challenge. We use [Code Splitting](http://jonathancreamer.com/advanced-webpack-part-2-code-splitting/?ref=jonathancreamer.com) to divide up our app into multiple smaller modules. As soon as the webpack and npm updates were in place, everything compiled just fine, however, when it came to the browser, something a bit strange happened. We use ES6 modules for all our JavaScript code. More often than not, we use `export default`. I spent about an hour banging my head on the desk as the modules I was importing during my code splitting just were NOT working. Turns out Kent C. Dodds has a fantastic article on why this was happening... [https://medium.com/@kentcdodds/misunderstanding-es6-modules-upgrading-babel-tears-and-a-solution-ad2d5ab93ce0#.wrsu2x6c5](https://medium.com/@kentcdodds/misunderstanding-es6-modules-upgrading-babel-tears-and-a-solution-ad2d5ab93ce0?ref=jonathancreamer.com#.wrsu2x6c5) This is how we're splitting our code up... ```prettypring require([ "rizzo-next/src/components/things_to_do", "rizzo-next/src/components/sights" /* ... */ ], function(ThingsToDo, Sights, /*...*/) { rizzo.renderComponent(Sights, ".sights"); rizzo.renderComponent(ThingsToDo, ".ttd"); }); ``` All that code worked just fine in Babel 5, but apparently, thanks to Kent's article, I learned that we were all apparently using ES6 modules incorrectly and Babel 6 for better or worse let us know that. Turns out though, the solution was actually not so bad... ```prettyprint rizzo.renderComponent(Sights.default, ".sights"); rizzo.renderComponent(ThingsToDo.default, ".ttd"); ``` The only way to use CodeSplitting in WebPack v1 is with the require syntax. That should change in v2, but that's not out yet. TL;DR Don't mix ES6 and Require syntax in WebPack v1, but if you want to use Code Splitting, add `.default` to your imported modules. ### npm link Another hang up we had was, we like to `npm link` to our main component repository `rizzo-next`. Well, apparently if you `npm link` things, it gets all screwy when it comes to where loaders load things from. There are 2 ways to solve this problem... 1. Install all the same tools in the other repository, aka all teh bablez in both places 2. Use `require.resolve` in the `query` options in WebPack ```prettyprint query: { "plugins": [require.resolve("babel-plugin-transform-decorators-legacy")], "presets": [require.resolve("babel-preset-es2015")] } ``` ### Conclusion All in all, it wasn't a hugely painful upgrade. After all, we knew going into it that it was a major version uprade so there'd most likely be hiccups. After just a few hours, everything was transpiling as it was in Babel 5 and we were on our way! ### Advanced WebPack Part 2 - Code Splitting URL: https://www.jonathancreamer.com/advanced-webpack-part-2-code-splitting/ Last updated: 2016-01-10T22:32:25.000Z WebPack has a feature that utilizes the AMD spec called [Code Splitting](https://webpack.github.io/docs/code-splitting.html?ref=jonathancreamer.com). What it allows you to do is "split your code" (insert troll face). Seriously, it's an amazing feature. What generally happens when you work with a standard WebPack build is, you'll have a few entry points like... ```prettyprint entry: { home: "js/home", tools: "js/tools", common: ["jquery", "kendo"] }, plugins: [new CommonsChunkPlugin({ name: "common" })] ``` With a setup like this, you'll potentially end up with 3 files, `home.js`, `tools.js`, and `common.js`. This might work perfectly for you. However, if you're working with some sort of very large Web App, or you're wanting to split out your code into the "above the fold" vs "below the fold" code, it would be nice to further split out your output files. This is what you can utilize Code Splitting to accomplish. ### Critical Path Page load speed is an extremly important metric for user happiness, let alone SEO, so let's take the above the fold example. There are great tools out there for determining how quickly your site renders including [Page Speed Insights](https://developers.google.com/speed/pagespeed/insights/?ref=jonathancreamer.com), [WebPageTest](webpagetest.org), and even Chrome's native developer tools have a preview of your site's load time. ![](http://d.pr/i/zYks+) Ideally your site will show *something* to the user within 2 seconds so that they are not tempted to leave early and increase your site's bounce rate. Even if the ENTIRE page isn't rendered, you need to get something to the user as quickly as you can. There are a few tools for helping you do this such as Addy Osmani's [critical](https://github.com/addyosmani/critical?ref=jonathancreamer.com), and Scott Jehl/FilamentGroup's [CriticalCSS](https://github.com/filamentgroup/criticalCSS?ref=jonathancreamer.com). With WebPack's code splitting though, you can actually create a setup where you're able to generate an above the fold and below the fold set of bundles. Our new design's at Lonely Planet have a really nice new masthead, and navigation which is what the user sees above the fold. We've architected our JavaScript, and SASS in such a way that we are able to know precicely which modules are needed to render that content... ![](http://d.pr/i/133ur+) I know that we need our Navigation, Search, Masthead, and SubNav components to be included above the fold. ```prettyprint import Masthead from "rizzo-next/src/components/masthead"; import rizzo from "rizzo-next"; import "./main"; // ... require.ensure([ "./below_the_fold" ], function(require) { // Now require it "sync" require("./below_the_fold"); }, "below_the_fold"); ``` So, in the entry point of our JS, we include all those components bundled below in `./main`. Then we introduce the code splitting. You can use `require.ensure` to create a "split point" in your code. We have a file called `below_the_fold.js` which import's all the modules that we know appear below the fold. `require.ensure` can also take a 3rd optional argument to name the "chunk" that get's output... ![](http://d.pr/i/1lkRC+) Also, since my `continents.js`, `countries.js`, and `cities.js` files are all including the same `main.js` file, they will get pulled into the `common.js` file thanks to the `CommonChunksPlugin` that was talked about in [Part 1](http://jonathancreamer.com/advanced-webpack-part-1-the-commonschunk-plugin/?ref=jonathancreamer.com) of this series! ### Splitting up your app You can also utilize code splitting to simply split up your application into smaller chunks that you can load on demand. You can even split the code up and load it dynamically with some type of event handler... ```prettyprint let map; $mapButton.on("click", function() { if (!mapLoaded) { require.ensure([ "../map/index" ], (require) => { if (map) { return map.open(); } let MapComponent = require("../map/index"); map = new MapComponent({ el: ".map_holder" }); map.open(); }, "map"); } }); ``` WebPack will create a bundle called "map" in the output, but it will not actually try to load the bundle until the button is clicked! This is extremely useful for us at Lonely Planet because we're using React for our map. This means we can make sure to not include React when the page initially loads, but we can defer that load until the user actually wants to interact with the map. You could apply the principal to any component you may not need initially when your page loads but can be triggered by some sort of user interaction. Feel free to check out my Nodevember talk where I covered this and several more advanced webpack topics! ### Not the Christmas Eve we expected URL: https://www.jonathancreamer.com/not-the-christmas-even-i-expected/ Last updated: 2015-12-26T16:42:04.000Z It all started as a normal Christmas Eve this year. Woke up, wrapped presents, spent time with my wife and daughter. On my way to my Grandmother's house for our usual Beef on 'Wek Christmas Eve dinner, things got a bit crazy. While attempting to explain firearm safety, my mom Roberta Creamer was accidentally struck with a bullet through her arm and across the front of her chest, thankfully avoiding any internal organs. She was care flighted to the hospital last night in amazingly good spirits and had an exploratory surgery to investigate the damage in her arm as it mostly just grazed the chest area. After a 1.5 hour surgery, she had a few arteries repaired, a bit of nerve damage, but all in all, she will make a full recovery. She is doing extremely well now and we celebrated a wonderful family Christmas. Beyond the physical wounds my mom has faced, the psychological wounds that my dad Matthew Creamer will have are sure to be great as it was he who accidentally discharged the gun. Please wrap all your support and prayers around both of them right now as more than ever they need the support of all their loved ones. Pray for peace and healing. Paul Baldwin, one of our longtime family friends came up with the idea to run this [campaign](https://www.gofundme.com/phgbdztw?ref=jonathancreamer.com) for support of them. I know we would greatly appreciate this message shared, and even if are unable to provide monetary support, please shower them with messages, calls, carrier pigeons, or whatever form of love you can shower on them. ### A letter for my sister and her husband URL: https://www.jonathancreamer.com/a-letter-to-my-sister-and-her-husband/ Last updated: 2015-11-07T16:40:38.000Z ![](https://storage.googleapis.com/jcstuff.jcreamerlive.com/meandrachel.jpg) ### Today is the day. My little sister will no longer be a Creamer, and will join together for the rest of her life with a great man of God. Growing up with her has been so much fun. We played together, cried together, and made it through the best of times and the worst of times. Rachel, I love you so much and will always be hear for you as your brother and your friend. You have always been there for me, and I will all ways be here for you. Ericson, thank you for being a strong and powerful influence. I can tell that you will love my sister with all your heart and will truly lead her first towards the cross, and second towards your heart. To the both of you, Lynnsey and I will walk together with you through your beautiful marriage and will love on you guys forever. Love y'all, Jonathan and Lynnsey ### Document ES6 with ESDoc URL: https://www.jonathancreamer.com/document-es6-with-esdoc/ Last updated: 2015-09-29T18:32:40.000Z I've been writing a lot of ES6(ES2015) at Lonely Planet the past few months. It comes with so many welcome changes to JavaScript, and with tools like Babel, you can use almost all of its features and even ES7(ES2016) features as well. Tools for documenting ES6+ are now rolling out as well, and one great such tool is called [ESDoc](https://esdoc.org/?ref=jonathancreamer.com). ESDoc is itself written in ES6+ code. JSDoc has [issues open](https://github.com/jsdoc3/jsdoc/milestones/3.4.0?ref=jonathancreamer.com) for supporting it, but still has a little ways to go. To use ESDoc, install it as a global... ```prettyprint npm install -g esdoc ``` or local module... ```prettyprint npm install esdoc --save-dev ``` One thing you can do if you save it as a local module is create a script in your package.json file... ```prettyprint { "scripts": { "docs": "./node_modules/.bin/esdoc -c esdoc.json" } } ``` Then you run ESDoc from the command line ```prettyprint esdoc -c esdoc.json # Or if you installed it locally ./node_modules/.bin/esdoc -c esdoc.json # then open path/to/docs/index.html ``` ### Configure You can actually configure ESDoc with a [JSON file](https://esdoc.org/config.html?ref=jonathancreamer.com). The most minimal configuration though is simply... ```prettyprint { "source": "./path/to/src", "destination": "./path/to/esdoc" } ``` ### Features ESDoc supports many of the same [tags](https://esdoc.org/tags.html?ref=jonathancreamer.com) that JSDoc supports. You can document classes like... ```prettyprint /** * What a great class this is! * @example * let myClass = new MyAwesomeClass(); */ class MyAwesomeClass { /** * Constructor for my awesome class * @param {number} foo The foo for my awesome class * @param {string} bar The bar for my awesome class */ constructor(foo, bar) {...} /** * @emits {my.event} Emit an event */ myEvent() {...} } ``` ### Search Out of the box, it comes with a search feature that you can search for specific parts of your code base. #### Tests A really cool feature of ESDoc is it also ties unit tests to documentation. Add some configuration to the `esdoc.json` file... ```prettyprint { "source": "./src", "destination": "./out/esdoc", "test": { "type": "mocha", "source": "./test" } } ``` Now when you run ESDoc you'll see the unit tests for a given class... ![](http://d.pr/i/1jVnH+) #### Coverage Every time you run ESDoc it will generate you a coverage report of how well documented your code is as well... ![](http://d.pr/i/SGnB+) You also get a badge you can use to display your coverage when you use the self hosting described below... #### Self Hosting You can actually go to [https://doc.esdoc.org/-/generate.html](https://doc.esdoc.org/-/generate.html?ref=jonathancreamer.com) and generate your documentation all online, and it will be self-hosted on ESDoc! You're badge will look like... ![](http://d.pr/i/19ipr+) And will be found at [https://doc.esdoc.org/github.com/org/your-repo/badge.svg](https://doc.esdoc.org/github.com/org/your-repo/badge.svg?ref=jonathancreamer.com). ## Conclusion Documentation is a great way to get people up to speed with your code base fast. So, it's important to use some kind of documentation service. ESDoc is still young, but has great potential to be a very useful tool! ### Using ES2016 Decorators to Publish on an Event Bus URL: https://www.jonathancreamer.com/using-es2016-decorators-to-publish-on-an-event-bus/ Last updated: 2015-09-25T18:35:40.000Z One stage 1 feature for ES2016 is the idea of [Decorators](https://github.com/wycats/javascript-decorators?ref=jonathancreamer.com) proposed by [Yehuda Katz](twitter.com/wycats). They allow you to annotate and modify entire classes, methods, and getters/setters. We can use ES2016 decorators to attach behavior to methods on a class. With this we can do things like publishing to an event bus when a method is invoked. First of all, a decorator is nothing more than a function. This function can optionally take arguments as options and return another function that does the decorating. The decorator function will recieve as arguments the target (which is the constructor the method exists on), the name of the function, and the [descriptor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global%5FObjects/Object/defineProperty?ref=jonathancreamer.com) of the function. ```prettyprint import postal from "postal/lib/postal.lodash"; export default function publish(topic, channel) { return function(target, name, descriptor) { const fn = descriptor.value; descriptor.value = function() { let value = fn.apply(this, arguments); postal.channel(channel || target.channel || "/").publish(topic, value); }; }; } ``` Here we're allowing 2 arguments on the decorator itself, the topic and the channel. Then the decorator function is returned. Inside the decorator function we can create a copy of the original method and make it constant. Then we can override the original function and call the original one that was saved. This allows us to recieve the returned value of the function. You can then publish out on your event bus the returned value on the specified topic and channel. [Postal.js](https://github.com/postaljs/postal.js?ref=jonathancreamer.com) is the event bus in these examples which was written by an amazingly talented JavaScript Engineer [Jim Cowart](https://twitter.com/ifandelse?ref=jonathancreamer.com) And here is how you use the decorator. ```prettyprint import publish from "path/to/decorators/publish"; class FooComponent () { @publish("foo.some.message", "component") someMethod() { return { my: "data" }; } @publish("foo.some.other") anotherMethod() { // ... } } ``` So now, when you call `someMethod` or `anotherMethod` it will publish an event... ```prettypring let foo = new FooComponent(); foo.someMethod(); // publish "foo.some.message" on "component" channel with { my: "data" } foo.anotherMethod(); // publish "foo.some.other" on "/" channel with no data ``` ### Advanced WebPack Part 1 - The CommonsChunk Plugin URL: https://www.jonathancreamer.com/advanced-webpack-part-1-the-commonschunk-plugin/ Last updated: 2015-09-02T03:22:36.000Z > "As a front end developer, I want to split my assets up into multiple bundles so that I can load only the JavaScript, and CSS needed for a page" For as long as I can remember in my career as a front end developer, one of the problems I've constantly been faced with was how to properly bundle assets for multi-page applications. There are many approaches to solving this problem, and it seems right now the most common one is to bundle CSS, and JavaScript separately and each into a single file. Generally through grunt, or gulp, all the CSS (SASS, LESS, etc) and JavaScript each get combined together into separate files, minified, and sent down the client. This is a very good solution to the problem at hand, but there are a few tweaks that I think can help improve things. Some issues of this solution are: - There's only 1 file for ALL the CSS in your app - A larger initial download can slow the time to render your site - Unless you load asynchronously, these large files block downloading Enter WebPack. ## WebPack WebPack is a bundler for front end assets. It can bundle lots of things. Not only just JavaScript and CSS either. It can do images, html, coffeescript, typescript, etc. It does this through the use of "loaders". A loader will allow you to target a specific file extension and pass it through that loader. ### Multiple Entries for multi-page Install WebPack as a global node.js module with.. ```prettyprint npm install -g webpack ``` Now create a `webpack.config.js` file ```prettyprint module.exports = { entry: { "home": "js/home", "list": "js/list", "details": "js/details" } }; ``` ``` js/home/index.js js/home/home.scss js/list/index.js js/list/list.scss js/details/index.js js/details/details.scss ``` Here we'll have 3 pages. A good way to organize things is to put each of these into separate folders... Let's add the [babel-loader](github.com/babel/babel-loader) to webpack so we can use ES2015 modules and classes... ```prettyprint loaders: [{ test: /*.js$/, exclude: /node_modules/, loader: "babel" }] ``` Now let's create a few components to use across our pages. Similar to the page organzation, you can create a folder for components, and one for each component... ``` js/components/ js/components/header/index.js js/components/header/header.scss js/components/search/index.js js/components/search/search.scss ... ... ``` This type of organization will allow you to keep all the code for a given component in the same place. Then the code JS code for a module can look like this... ```prettyprint // components/search/index import "search.scss"; // WAT export default class Search { constructor({ el }) { this.$el = el; this.$el.on("focus", ".search__input", this.searchActivate.bind(this)); } searchActivate() { // ... } } ``` Our header might then import the search component, and it's styles ```prettyprint import "header.scss"; import Search from "../search"; export default class Header { constructor({ el }) { this.$el = el; this.search = new Search({ el: this.$el.find(".search") }); } } ``` The search, and header are components that each page would need, so let's import them into each one of our pages... ```prettyprint import "home.scss"; import Header from "../components/header"; const header = new Header({ el: ".header" }); ``` Here's what's great about WebPack, it would seem like since we've used the same module 3 times that when we build the bundle, we'd see it repeated 3 times. That's where WebPack plugins come into play. ### WebPack Plugins There are bunches of different [plugins](http://webpack.github.io/docs/list-of-plugins.html?ref=jonathancreamer.com) for WebPack. One of the coolest ones is the CommonsChunk plugin. WebPack defines each module of your code as a "chunk". The job of the CommonsChunk plugin is to determine which modules (or chunks) of code you use the most, and pull them out into a separate file. That way you can have a common file that contains both CSS and JavaScript that every page in your application needs. To get started... ```prettyprint var CommonsPlugin = new require("webpack/lib/optimize/CommonsChunkPlugin") // ... module.exports = { entry: { common: ["jquery"] }, plugins: [ new CommonsPlugin({ minChunks: 3, name: "common" }); ] }; ``` Require the plugin into your webpack.config file, then add a new `common` entry. You can preload the common chunk with stuff like jQuery that you may want on every page. You then need to create an instance of the plugin down in an array of plugins. You can specify the `minChunk` option in here as well. This option says, if any module is used X or more times, then take it out and pull it into the common chunk. The `name` must match with the key in the `entry` object. Now the next time you run WebPack, you'll have another outputed chunk that contains jQuery as well as any module that you have used 3 or more times. So our header that we've used in every page would be pulled out into the common chunk. ### Conclusion It's always been a challenge to determine what pages need what JavaScript and styles. Thankfully WebPack's CommonsChunk plugin makes it pretty simple to do this out of the box with just a bit of configuration. This is part 1 of Advanced Webpack. There will be more to come! Be sure and check out the [webpack express starter](https://github.com/jcreamer898/webpack-express-starter?ref=jonathancreamer.com) repository which will have some examples of things talked about throughout the series. ### Code on the Beach 2015 URL: https://www.jonathancreamer.com/code-on-the-beach-2015/ Last updated: 2015-08-09T23:04:26.000Z I was honored to be accepted to speak at [Code on the Beach](http://codeonthebeach.com/?ref=jonathancreamer.com) this year! (2015). Code on the Beach is an amazing conference down in Jacksonville, FL. I can't recommend it enough! ### Location The conference takes place at a 4 star resort called the [One Ocean Resort](oneoceanresort.com). It's a beautiful resort inside and out. There's a mini bar in your room with free drinks, and snacks, turn down service, and the hotel employees are super friendly and will do anything you need. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2015/08/oneocean.jpg) The town is called Atlantic Beach. Within 100 feet is the Beaches Town Center. There are a bunch of great restaruants, and shops. [Poe's Tavern](http://poestavern.com/atlantic-beach/?ref=jonathancreamer.com), [Flying Iguana](http://flyingiguana.com/?ref=jonathancreamer.com), and [M Shack](www.mshackburgers.com) were our favorite places, but there's a bunch more! [Lemon Bar](lemonbarjax.com) was also a really cool spot to hang and drink Piña Coladas, steps from the beach. ### Conference The conference was a really great time. The whole thing was very chill, yet organized. Registration was fun with all kinds of beach swag like beach balls, and squirt guns. The talks I attended were mostly in the Web and Cloud track and all had fantastic content. I learned a ton about web performance from [Nik Molnar](http://twitter.com/nikmd23?ref=jonathancreamer.com), brushed up on my React/Flux skills from [Raymond McDermett](http://twitter.com/developerjesus?ref=jonathancreamer.com), and then gave my talk to a PACKED room. > Packed house at [@jcreamer898](https://twitter.com/jcreamer898?ref=jonathancreamer.com) 's [#javascript](https://twitter.com/hashtag/javascript?src=hash&ref=jonathancreamer.com) architecture session at [@CodeOnTheBeach](https://twitter.com/CodeOnTheBeach?ref=jonathancreamer.com) . [pic.twitter.com/Tir5bk3oBi](http://t.co/Tir5bk3oBi?ref=jonathancreamer.com) > > — Michael Crump (@mbcrump) [August 7, 2015](https://twitter.com/mbcrump/status/629745201276391424?ref=jonathancreamer.com) Thanks to [Michael Crump](https://twitter.com/mbcrump/?ref=jonathancreamer.com) for that tweet. We then ended the day with drinks provided by the conference/One Ocean, and Poe's which is a super fun restaraunt themed after Edgar Allan Poe. ![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2015/08/IMG_7843.jpg) Day 2 I woke up and had a great breakfast followed by a hilarious keynote by [Stacey Mulcahy](http://twitter.com/bitchwhocodes?ref=jonathancreamer.com), followed by a bunch of great sessions from guys like [Cecil Phillip](http://twitter.com/cecilphillip?ref=jonathancreamer.com) on ASP.NET MVC6, another by Nik on Azure WebSites, and 2 by my friend [David Neal](http://twitter.com/reverentgeek?ref=jonathancreamer.com), one on Node.js, and the other on Kanban. One of the best parts of day 2 though was the 2.5 hour beach break. There was a corn hole tourney, an Ice Cream Sunday bar, and plenty of time to relax by the ocean or the pool. Day 3 got started with a great talk on F# by [Jeremy Abbbot](http://twitter.com/mrjabbott?ref=jonathancreamer.com), then a fantastic intro to Aurellia by [Ashley Grant](http://twitter.com/ashleymgrant?ref=jonathancreamer.com). The conference was then final keynoted by the always amazing [Elijah Manor](http://twitter.com/elijahmanor?ref=jonathancreamer.com) talking about Growing as a Developer. ### Conclusion All in all, Code on the Beach was an amazing conference, and I cannot recommend it enough! Plus, it was my wife and I's 7th year anniversary so we really enjoyed our time together at the One Ocean Resort! I'm already excited to submit a talk next year so I can come again. ### Setup ESLint with ES6 in Sublime Text URL: https://www.jonathancreamer.com/setup-eslint-with-es6-in-sublime-text/ Last updated: 2015-07-31T11:16:54.000Z [ESLint](http://eslint.org/?ref=jonathancreamer.com) is a JavaScript linter/style checker that has quickly risen in popularity for a number of reasons. First of all, it's easily plugable. Second, it's sort of a hybrid between JSHint, and JSCS. Third, it's written by none other than [Nicholas Zakas](https://twitter.com/slicknet?ref=jonathancreamer.com). ### Get Started with ESLint It's super easy to use ESLint. You should already have Node.js installed, and then intsall ESLint... ``` npm install -g eslint # Or npm install eslint ``` You can install it either globally or locally. It's easier to just have it globally though. ### Configure Create a `.eslintrc` in the root of your project. Then you can add `globals`, set up your environment with `env`, and add [rules](http://eslint.org/docs/rules/?ref=jonathancreamer.com) as well. ```prettyprint { "globals": { // Put things like jQuery, etc "jQuery": true, "$": true }, "env": { // I write for browser "browser": true, // in CommonJS "node": true }, // To give you an idea how to override rule options: "rules": { // Tons of rules you can use, for example... "quotes": [1, "double"] } } ``` #### Rules To use a rule, add it's name, then pass an array. The array is an array of options. The first is either... - 0 - Disable the rule - 1 - Warn about the rule - 2 - Throw error about the rule Then the next arguments can configure different parameters per rule. In this case, we're saying we want to throw a warning when single quotes are used rather than double quotes. See the full list of rules [here](http://eslint.org/docs/rules/?ref=jonathancreamer.com). ## ES6 (2015) Since ESLint is so easily plugable and configurable, there are already ways of using it with the new version of Ecmascript. ### Parsers There are different types of parsers available. One of them is the `babel-eslint`... ```prettyprint "parser": "babel-eslint", ``` This will allow you to utilize ES6 syntax. ## Sublime-Linter There's a great plugin for Sublime Text called `Sublime-Linter`. Once it is installed, there are a bunch of other plugins for it as well, including one for ESLint! Install it with the `Command+Shift+P` shorcut... ![](http://d.pr/i/12f6i+) You then have to do a bit of configuration to get it actually running against your code. Go to `Sublime Text -> Prefences -> Package Settings -> SublimeLinter -> Settings-User`... ![](http://d.pr/i/TtXN+) In here, you have to tell Sublime-Linter where your `node` is installed using the `paths` option. You can do this by running `which node` on the command line. For example if you use `nvm` your config would look like this. ```prettyprint "paths": { "linux": [], "osx": [ "/Users/jonathanc/.nvm/versions/io.js/v2.0.0/bin" ], "windows": [] }, ``` One other thing you might have to set depending on your syntax highlighter is an alias for your syntax highlighter. For ES6, Babel has a sublime plugin for syntax highlighting that you can install by searching for `Babel`. Then in your config for the linter... ```prettyprint "syntax_map": { "JavaScript (Babel)": "javascript", } ``` Now when you have invalid JavaScript including ES6, you'll see warnings in the code... ![](http://d.pr/i/10oU3+) That should help you get started linting, and writing better JavaScript in Sublime Text! ### Comic Con 2015 Friday Highlights URL: https://www.jonathancreamer.com/comic-con-2015-friday-highlights/ Last updated: 2015-07-11T15:06:46.000Z Here's some of the highlights of my (SDCC) Comic Con 2015 photos. I'll add some more as I get them uploaded. Seeing the cast of **The Walking Dead** was definitely one of the highlights as there was a huge rush to grab photos of them as they walked by. ### Hadley's First Laughs URL: https://www.jonathancreamer.com/hadleys-first-laughs/ Last updated: 2015-01-28T14:42:47.000Z Yesterday my cute baby laughed for the first time! We were playing with the dogs, and she started little giggles, and then just started rolling laughing. What a cutie I have! ### What the Flux? URL: https://www.jonathancreamer.com/what-the-flux/ Last updated: 2015-02-17T21:51:58.000Z I've been really diving into wrapping my brain around [React.js](http://facebook.github.io/react/?ref=jonathancreamer.com) and [Flux](http://facebook.github.io/flux/?ref=jonathancreamer.com) architecture the last couple of weeks. Not going to lie, I've been avoiding it. When I look at the docs and blog posts about Flux, and React, my brain goes... ![](http://c2.thejournal.ie/media/2014/10/mark-13.gif) Things like `componentDidMount`, and `Dispatchers`, and `ActionCreators`, etc etc initially scared me off when I tried to get into it. Just seemed so computer sciency™. After a few days of messing with it, I really like it. A lot. Flux in particular took me a few days to really wrap my head around. This image in particular. ![](http://d.pr/i/1gDAc+) When I finally started to wrap my head around it though, I decided to come up with a version of my own to help others get around to figuring it out a bit faster. ![](http://d.pr/i/1inWl+) Let's break this all down... ### Views > Show some stuff. This is where your components render. Your component has only a few jobs, and here's what one might look like... ```prettyprint var NewEmailComponent = React.createClass({ getInitialState() { return { to: "" }; }, render() { return ( ); }, updateToText(event) { this.setState({ to: event.target.value }); }, createNewEmail(event) { var to = this.state.to; EmailActionCreator.createNewEmail(to); } }); ``` So, every component in React has state. Our `NewEmailComponent` will initially have just a `to: ""` in its state. Next, `render` returns JSX to display. Here we have an input, and a button. You'll notice the `onChange={this.updateToText}`, and the `onClick={this.createNewEmail}`. Both of these are adding event handlers to the component, and calling the respective functions. When the text of the `to` input changes, the state gets changed to reflect that `to` has changed. ### Action Creators > Go get some more stuff The action creator creates an "action". Yup. It is also here that you'll typically go get some data from the server if you need it. So, let's go back to the view for a sec... When you click on the button, the `EmailActionCreator.createNewEmail(to)` function fires with whatever kind of data you want to pass as `to`. Could be an ID, name, whatever, in this case it's an email to send to. The `EmailActionCreator.createNewEmail` function will do a couple of things. 1. Create an action for creating a new email 2. Call an API to create a draft ### Actions > Do something with stuff Actions are JavaScript POJOs (plain old javascript objects). Often they will by convention have a `type`, and some `data`. Let's step back to how and why they are created in the first place. So, our ActionCreator will create an "action". Typically there is a file that stores an object of all the different possible action types. ```prettyprint // actions/fooActions.js var keyMirror = require('keymirror'); module.exports = keyMirror({ CREATE_EMAIL: null, EMAIL_CREATED: null, SEND_EMAIL: null }); ``` You use these `actions` to define things you can trigger, whether from the UI or from the server. [keyMirror](https://github.com/STRML/keyMirror?ref=jonathancreamer.com) here is used to make the values of `null`, mirror the upper case key names. Just a quick short cut library. So, back in our `EmailActionCreator`, we'll use these different action types. ```prettyprint // actions/emailActionCreator.js module.exports = { createNewEmail(to) { EmailAppDispatcher.handleViewAction({ type: ActionTypes.CREATE_EMAIL, to: to }); EmailWebService.createEmail({ to: to }); }, emailCreated(mail) { EmailAppDispatcher.handleServerAction({ type: ActionTypes.EMAIL_CREATED, mail: mail }); } } ``` The `EmailWebService` can hit your API however you want... ```prettyprint // data/emailWebService.js module.exports = { createEmail: function(options) { $.ajax({ url: "/api/email", type: "POST" }).done(function(id) { var mail = { to: options.to, id: id }; EmailActionCreator.emailCreated(mail); }); }; }; ``` Here, it's using `jQuery.ajax` to create a new email. Once the email is done being created, another action is created with `EmailActionCreator.emailCreated(mail);` to tell everyone that the email has been created. The type of this action is `ActionTypes.EMAIL_CREATED`. Now let's talk about the dispatcher and that `handleViewAction` and `handleServerAction` stuff. ### Dispatcher > Hey Everyone! There's new stuff! The dispatcher's single job is to publish out some sort of message, or event, or whatever you want to call it, to let anybody who cares know that something just happened. For example, when someone first clicked our `Create Email` button, an action was created with the `EmailActionCreator`, and then sent over to the dispatcher via the `handleViewAction` method on the dispatcher. The dispatcher is boilerplate code that never really needs to change once it's done... ```prettyprint var EmailAppDispatcher = assign(new Dispatcher(), { handleServerAction(action) { var payload = { source: PayloadSources.SERVER_ACTION, action: action }; this.dispatch(payload); }, handleViewAction(action) { var payload = { source: PayloadSources.VIEW_ACTION, action: action }; this.dispatch(payload); } }); module.exports = EmailAppDispatcher; ``` Flux.js comes with a default dispatcher that has `register` and `dispatch` functions among a few others. Our `EmailAppDispatcher` has 2 helper functions, the `handleViewAction`, and `handleServerAction`. Two different functions really aren't entirely neccessary, but they allow you to know exactly where an action came from if you need it. Beyond that though, they just call the built in `dispatch` function from the [default Dispatcher](http://facebook.github.io/flux/docs/dispatcher.html?ref=jonathancreamer.com) in Flux. Elsewhere in the app, any store, which we'll talk about in a second, that uses the Dispatcher's `register` function will be notified that an action is being dispatched. ### Store > Keep track of stuff Stores hold application state and business logic. They are also themselves usually event emitters. That means you can listen to events they publish. So, a view will bind to a store's change event, call different store methods to get data, and in turn update their state with the new data. Ideally, stores will not make any ajax requests because the actions should send in data, and the store just manages it. A store will generally have local variables that hold some kind of data/state. So, in our case below, simply the active email. ```prettyprint var EventEmitter = require("events"), assign = require("react/lib/assign"), Email = require("./core/email"), _ = require("underscore"); var CHANGE_EVENT = "change"; var activeEmail = null; function createNewEmail(mail) { var email = new Email(mail); activeEmail = email; } var EmailStore = assign({}, EventEmitter.prototype, { emitChange() { this.emit(CHANGE_EVENT); }, addChangeListener(callback) { this.on(CHANGE_EVENT, callback); }, removeChangeListener(callback) { this.removeListener(CHANGE_EVENT, callback); }, getActiveEmail() { return activeEmail; } }); ``` The `emitChange`, `addChangeListener`, and `removeChangeListener` are functions you'll see on every store, and are just abstractions to the underlying `EventEmitter` methods `emit`, `on`, and `removeListener`. The `CHANGE_EVENT` just saves a string for the name of the change event. The `getActiveEmail` method just returns the active email. Backing up again to the dispatcher, remember it exposes a `register` function. After we define the methods for our store as above, we `register` with our dispatcher... ```prettyprint EmailStore.dispatchToken = EmailAppDispatcher.register((payload) => { var action = payload.action; switch (action.type) { case ActionTypes.EMAIL_CREATED: createNewEmail(action.mail); EmailStore.emitChange(); break; } }); ``` Every time that the dispatcher sends out ANY action, the `register` function gets called. However, we only care about the action if the type is in our switch statement, and in this case all we have is `ActionTypes.EMAIL_CREATED`. When the action sent out by the dispatcher is `ActionTypes.EMAIL_CREATED`, we'll respond by calling our `createNewEmail` function which merely sets an active email. Then we'll tell the store to emit a change event. Going full circle, let's step back and add something to our view... ```prettyprint var EmailStore = require("../stores/emailStore"); /** ... *./ { componentWillMount() { EmailStore.addChangeListener(this._onChange); }, _onChange() { this.setState({ activeEmail: EmailStore.getActiveEmail() }); } } ``` Our `componentWillMount` function will fire when the component is ready. It's in `componentWillMount` that we can add a change listener on our store's change event. Any time that the store changes, `this._onChange` gets called. In the `_onChange` function we can set the state of the component with the new email that was created! Boom. # TL;DR View Renders -> Click triggers action creator -> Go get data -> Create action for recieved data -> Send action to dispatcher -> Dispacther publishes action -> Store listens for action -> Store updates state -> View updates So, now you're either thinking... ![](http://i.imgur.com/16tx1Ft.gif+) Or... ![](http://i.imgur.com/tfyHIaB.gif+) If you're wanting to flip a table, re-read this article, go look at [these exampes](https://github.com/facebook/flux/blob/master/examples?ref=jonathancreamer.com), and try again. You'll get it, and you'll love it. ### Make your GitHub account hack proof with 2 factor auth URL: https://www.jonathancreamer.com/setting-up-2-factor-authentication-on-github/ Last updated: 2014-11-17T16:36:32.000Z Security. So important these days. Feels like every day there's some new big site being hacked. One of the best ways to be hack proof on any site is to utilize 2 factor authentication. Log in to your account and go to the settings page by clicking the gear at the top right hand part of the screen. ![](http://d.pr/i/1iFbR+) Then go to Security from the left hand nav. ![](http://d.pr/i/1l0EX+) Now click setup 2 factor auth. ![](http://d.pr/i/14BYX+) You'll have the option to use an app or SMS. ![](http://d.pr/i/1cHAl+) The easiest thing to do is to use Google Authenticator for this. [Android](https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&hl=en&ref=jonathancreamer.com) [iOS](https://itunes.apple.com/us/app/google-authenticator/id388497605?mt=8&ref=jonathancreamer.com) Download the app, and then scan the QR code on the page to activate it... ![](http://d.pr/i/1lLR0+) Enter the 6 digit code from Authenticator into the box, and that's it! Take these easy steps and use them to your advantage. After all, especially if you work for a large company that utilizes GitHub private repositories, you want to keep your account nice and safe. ### Require.js packages for building large scale angular applications URL: https://www.jonathancreamer.com/require-js-packages-for-building-large-scale-angular-applications/ Last updated: 2014-09-10T20:14:19.000Z Require.js is one of my favorite ways to work with large JavaScript applications. It allows you to build AMD (asynchronous module definition) style modules to break up your application into small reusable modules. This is a desirable feature in large code bases. It's important to know when and why you may consider require.js. If you're working on a small application that doesn't have a need for many files and little logic, you may or may not need the overhead of require.js. However, you still can get value out of it's organization and structure it provides you. On the other hand there is the use case of the large scale application with many files, lots of logic, tons of code, etc. This type of application is where require.js shines greatly. You'll spend a bit of time getting it set up initially, but the structure, organization, and added maintainability working with require gives you is great for large apps. ### AMD A typical AMD module looks like... ```prettyprint define(['angular'], function() { return { /* API for this module */ }; }); ``` Or you can use the commonjs style AMD module.. ```prettyprint define(function(require) { var angular = require('angular'); return { /* API for this module */ }; }); ``` This pattern ends up helping you create a dependency tree so you know what modules depend on what other modules. ### Packages One of the things available in the require.js library is the ability to create **packages**. A `require.js` package looks almost like what a typical commonjs, node package would look like. This means you'll have an entry module, like `index.js` or in the case of require, you'll have `main.js` for your package. The `main.js` is responsible for loading in the other modules for the package. Then in your requirejs config call you can simply ask for a package... **NOTE: Don't forget that angular is NOT an AMD module, so you have to `shim` it and any other angular plugin you use** ```prettyprint require.config({ packages: ["chat"], paths: { angular: "/app/javascripts/vendor/angular/angular", ngRoute: "/app/javascripts/vendor/angular-route/angular-route" }, shim: { angular: { exports: "angular" }, ngRoute: { deps: ["angular"] } } }); ``` Then you'll have a folder structure like this... ``` /app /app/javascripts /app/javascripts/main.js /app/javascripts/app.js /app/javascripts/chat /app/javascripts/chat/main.js /app/javascripts/chat/chatModule.js /app/javascripts/chat/chatCtrl.js ``` And the `chat/main.js` just loads in the modules it needs... ```prettyprint define(function (require) { var chatCtrl = require("./chatCtrl"); }); ``` Notice the `./` here, that tells require.js to load the module relative to the current package. ### Angular.js modules Angular.js also has a concept of modules that allow you to break up your application. If you combine angular modules with require.js packages, you have a nice system for creating reusable modules. Each package needs to have a `main.js` to load the packages modules, and also it needs to have some *module.js* file for angular. In the chat example, there would be a `chatModule.js`... ```prettyprint define(function (require) { var angular = require("angular"); return angular.module("my.chat", []); }); ``` Now in your `chatCtrl` module above, you use that module to define your controller... ```prettyprint define(function(require) { var chat = require("./chatModule"); function ChatCtrl() { /* Chat controller */ } return chat.controller("ChatCtrl", ChatCtrl); }); ``` Now if I wanted to add a `Chat` service to the package, you simply add `ChatService.js` to the `chat` folder... ```prettyprint define(function(require) { var chat = require("./chatModule"), io = require("socketio"); function Chat() { var socket = io("/"), messages = []; return { messages: messages, send: function(msg) { /* Send messages with socketio */ } }; } return chat.factory("Chat", Chat); }); ``` Then don't forget to go back and add your new service to the `chat/main.js`... ```prettyprint define(function (require) { var chatCtrl = require("./chatCtrl"); var chatService = require("./chatService"); }); ``` ### Use the package Once you define the package as done above in the application's `main.js`, wherever you create your application's module in say, `app.js`, you simply require in the package... ```prettyprint define(function(require) { var angular = require("angular"); require("ngRoute"); require("chat"); var app = angular.module("app", [ "ngRoute", "my.chat" ]); }); ``` The last thing you have to remember to do, and this is with any angular app using a module loader, is remember to manually bootstrap your application. You have to do this because all the JS files load in asynchronously, so Angular won't know when to start the application. You can do this in your `main.js` ```prettyprint require(['angular', 'app/app', function(angular) { angular.bootstrap(document.documentElement, ["app"]); }); ``` # Conclusion It's important to have a solid architecture when working with large scale JavaScript applications. AMD with require.js is one of many ways of accomplishing this. I have really liked working with this style of app, and created a starter application for anyone to get started working with it... [https://github.com/jcreamer898/requirejs-angular-starter](https://github.com/jcreamer898/requirejs-angular-starter?ref=jonathancreamer.com) Feel free to fork, clone, and have fun! ### Using jQuery's $.ajax in an Angular app URL: https://www.jonathancreamer.com/using-jquery-ajax-in-an-angular-app/ Last updated: 2014-09-03T21:35:23.000Z We've all been using jQuery for a long time. It's for this reason that Angular.js works seemlessly with jQuery if it sees it on the page, but also has it's on jQLite available for those who decide to opt out of jQuery. One of the APIs in jQuery that I find myself using the most is `$.ajax`. Now I know that Angular has it's own `$http` service that works great, but I was thinking the other day how I sometimes wish I could just use jQuery's `$.ajax` instead. There's a couple of reasons for this, one is that I already know the API. I have to google `$http` every now and then to make sure I'm using it right. Another reason is I really like to use [jQuery Mockjax](https://github.com/appendto/jquery-mockjax?ref=jonathancreamer.com). It's a very simple way to mock HTTP requests without having to tap into the `$httpBackend` of Angular. I decided to throw together a small Angular service that utilizes jQuery's `$.ajax` for use in services and controllers... ```prettyprint (function($) { function Ajax($rootScope, $dfd) { var ajax = jQuery.ajax; return function(options) { var promise = ajax(options), dfd = $dfd(); promise.done(function(data) { $rootScope.$apply(function() { dfd.resolve(data); }); }).fail(function() { var failArgs = arguments; $rootScope.$apply(function() { dfd.reject.apply(dfd, failArgs); }); }); return dfd.promise(); }; } Ajax.$inject = ['$rootScope', '$dfd']; angular.module("Ajax") .provider("$ajax", function() { this.defaults = {}; this.setOptions = function() { $.ajaxSetup(this.defaults = options); }; this.getOptions = function() { return this.defaults; }; this.$get = Ajax; }); }(jQuery)); (function($) { function Dfd() { return function() { return jQuery.Deferred(); }; } angular.module("Ajax") .factory("$dfd", Dfd); }(jQuery)); ``` So, there's a few pieces here. First of all is the `Ajax` function. This function's job is to return the API for the new `$ajax` services we're creating. The first thing it does is grab a reference to `jQuery.ajax` and then returns a function. We're then immediately invoking `ajax` in that function and getting it's promise back. Next we're using the `$dfd` service which is defined down farther in the code, but essentially just creates a new jQuery promise. We're then callin the `.done` method on the Ajax promise and passing a function. `$rootScope` is injected into the service so when the Ajax promise is resolved we're able to call `$rootScope.$apply()` to ensure that the data returned is used within the angular exectution context. We then resolve the promise we created with `$dfd()`. There is also a `.fail` which also calls `$rootScope.$apply` in case something goes wrong when making the request. At the end of the service we simply return the `dfd.promise()`. This service is actually defined as a `provider` and the reason for this is whatever returns from `$get` in the `provider` becomes the API for the service, but this also gives us a place to configure the service. There are `setOptions` and `getOptions` functions that allow you to conigure the `$.ajax` defaults with jQuery's `$.ajaxSetup` method. ```prettyprint angular.module('Foo',).config(function($ajaxProvider) { $ajaxProvider.setConfig({ /* .. */ }); }); ``` The way you actually utilize this new service in a controller would look like... ```prettyprint angular.module("Ajax", []) .controller("WeatherCtrl", function($ajax, $scope) { $ajax({ url: "http://api.openweathermap.org/data/2.5/weather?q=London,uk", dataType: "JSONP" }).done(function(weather) { $scope.weather = weather; }); }); ``` Here is the example running in a Plnkr... It's very easy to utilize the well known `$.ajax` API from jQuery with this service. ### Adding clarity to scope inheritance in angular URL: https://www.jonathancreamer.com/adding-clarity-to-scope-inheritance-in-angular/ Last updated: 2014-08-13T20:56:13.000Z No matter how old JavaScript gets, the concept of prototypical inheritance still confuses developers. In fact, appendTo just gave a [training course](-functions-and-objects-8-12-2014/) on functions and objects that talked about it yesterday! Not to mention Jordan Kasper's great talk on [OO JavaScript](http://vimeo.com/62353516?ref=jonathancreamer.com). The fact is prototypical inheritance in JavaScript presents confusion to a lot of folks out there. In terms of Angular.js that may explain why the concept of `$scope` is [difficult to grok](http://jonathancreamer.com/working-with-all-the-different-kinds-of-scopes-in-angular/?ref=jonathancreamer.com). Let's take a step back to the root of the problem and try to understand prototypical inheritance a little better. ### POJCF's Let's start with plain old javascript constructor functions (insert rimshot here). This is a function... ```prettyprint function Root() {} ``` Simple. If you plan on using the `new` operator on this `Root` function, well then it's called a `constructor` function. ```prettyprint var root = new Root(); ``` Every JavaScript function has a `prototype`. When you log the `Root.prototype`, you get... ![](http://d.pr/i/p0PN+) Notice a few things here, first of all there's a `constructor` property on Root.prototype, and a mysterious looking `__proto__` member as well. That **proto** represents the prototype that this function is based off, and since this is just a plain JavaScript function with no inheritance set up yet, it refers to the `Object` prototype which is something just built in to JavaScript... [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global\_Objects/Object/prototype](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global%5FObjects/Object/prototype?ref=jonathancreamer.com) This has things like `.toString`, `.toValue`, etc... Technically the `__proto__` thing is deprecated and the way you get an object's prototype is by using the `Object.getPrototypeOf` method like this... ```prettyprint Object.getPrototypeOf(Root.prototype); // Object {} ``` This will make even more sense shortly after the inheritance chain is set up. Stay tuned. An object's prototype is basically it's DNA, but it's nothing more than an object with functions or properties on it! So what happens when you add something to the `Root` prototype? ```prettyprint function Root() {} Root.prototype.add = function(x, y) { return x + y; }; ``` Well, now you've modified the DNA of the `Root` and added a function called `add` to it... ![](http://d.pr/i/A3Lk+) That means when you create an instance of `Root`, you can call add it's add method. ```prettyprint var root = new Root(); var sum = root.add(2, 2); console.log(sum); // 4 ``` You can also add primitives or object's to the `Root` prototype... ```prettyprint Root.prototype.name = "Jonathan"; // String Root.prototype.user = {}; // Object Root.prototype.friends = []; // Array ``` ### Creating a child So, here's where things get interesting... ```prettyprint function Root() {} Root.prototype.add = function(x, y) { return x + y; }; function Child() {} Child.prototype = Object.create(Root.prototype); // Magic Child.prototype.constructor = Child; // Gotta reset this console.log(Child.prototype.add); // Looks up the chain ``` Now then, `Child` officially "inherits" it's prototype from `Root`. And there was much rejoicing. First things first, the `Object.create` [method](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global%5FObjects/Object/create?ref=jonathancreamer.com). Don't get to hung up on this, it's job is *basically* to create a new object based off of whatever you pass in. In this case what we're doing is setting the prototype of `Child` to a new object that looks identical to the `Root` prototype. The next logical step is to reset the `Child.prototype.constructor` and make sure it still points to the `Child` function. You have to do this because if you don't, then `Child.prototype.constructor` would point to the `function Root() {}`. Because we set the `Child` prototype to the `Root` prototype, the `Child.prototype` now has the `add` method available... Have a look here... ![](http://d.pr/i/zZJd+) See how you can see the `Child` has it's `.prototype` property. Then, the `__proto__` helps point to the fact that the prototype of `Child` is based off of the prototype of `Root`, and the prototype of `Root` is based off of `Object`! It's basically a tree... ```prettyprint Child | \ \ Root.prototype - add | | \ \ Object.prototype -toString -valueOf -etc., etc. ``` There's a couple of interesting things worth noting here... When you create an instance of `Child` and call `add`... ```prettyprint var child = new Child(); child.add(2, 2); ``` You haven't actually defined `add` on the `Child` prototype. BUT, you have definited it on the `Root` prototype which `Child` inherits from. So therefore, it will add stuff. If I was to be a crazy person and give an `add` method to `Child` also, well that would rip a whole in the space time continuum right?! ![](http://stream1.gifsoup.com/view4/4648680/doc-brown-o.gif) ```prettyprint Child.prototype.add = function() { return x - y; // troll }; ``` Thankfully it won't. But, what it will do is basically hide the parent's `add` method. The same is true for primitive things on the `Root`. Earlier you saw this... ```prettyprint Root.prototype.name = ""; ``` So, same as the `add`, you can overwrite that `name` primitive string in the `Child` prototype... ```prettyprint Child.prototype.name = "Mike"; // String ``` This again, masks the `name` property on `Root.prototype`. ### What's that have to do with $scope?! EVERYTHING. > Scope in angular is based off of prototypical inheritance. Wherever you use `ng-app`, say on a `` or `` tag, angular is going to create you an instance of the `Scope` constructor function that will be referred to as the `$rootScope`. All other scopes in angular come from this `$rootScope` by way of inheritance in EXACTLY the same way we've been talking about. When you have something like this with an `ng-app` and an `ng-controller`... ```prettyprint
``` Here's what happens. First, there's a `$rootScope` created. Then essentially, the `FooCtrl` gets its own scope that prototypically inherits from the prototype of `$rootScope` which in terms of angular is `Scope.prototype`. Here in the angular source code in `src/ng/rootScope.js` you can find the code for `Scope`... ```prettyprint function Scope() { this.$id = nextUid(); this.$$phase = this.$parent = this.$$watchers = this.$$nextSibling = this.$$prevSibling = this.$$childHead = this.$$childTail = null; this['this'] = this.$root = this; this.$$destroyed = false; this.$$asyncQueue = []; this.$$postDigestQueue = []; this.$$listeners = {}; this.$$listenerCount = {}; this.$$isolateBindings = {}; } // ... Scope.prototype = { constructor: Scope, $new: function() {} // etc, etc... } ``` The `$new` function is where new scopes are created... ```prettyprint $new: function(isolate) { var ChildScope, child; if (isolate) { child = new Scope(); child.$root = this.$root; child.$$asyncQueue = this.$$asyncQueue; child.$$postDigestQueue = this.$$postDigestQueue; } else { if (!this.$$childScopeClass) { this.$$childScopeClass = function() { // blah blah... }; this.$$childScopeClass.prototype = this; } child = new this.$$childScopeClass(); } // more stuff we don't care about right now... return child; }, ``` When an `ng-controller` directive is used, angular will call `$rootScope.$new` and create a new scope. Following down the execution path, you'll see that a controller is not going to be an isolate scope so it'll jump into the `else` block. It then caches a reference to a function called `this.$$childScopeClass`. It then sets the prototype of this function to the prototype of `Scope`... ```prettyprint this.$$childScopeClass.prototype = this; ``` This is similar to calling `Object.create(Scope);`, but in this case `this` is referring to an instance of `Scope`. ### ng-controller Here's some sample code that helps illustrate this... ```prettyprint angular.module("Demo", []) .controller("ChildCtrl", function($rootScope, $scope) { $rootScope.rootyThing = "I am groot"; console.log($scope.rootyThing); console.log(Object.getPrototypeOf($scope)); // Scope }); ``` And this HTML... ```prettyprint
``` Let's break this thing down. `ng-app` is set on the body and uses the `Demo` module. Then `ChildCtrl` is set up on a div. When the controller function fires it's asking for `$rootScope` and setting `rootyThing` up on it. Since `$scope` prototypically inherits from `$rootScope` you'll see how we're immediately able to access `rootyThing` on the local controller scope! ![](http://d.pr/i/Oe5G+) The `Object.getPrototypeOf($scope);` call shows you the controller's `$scope` prototype is `Scope`. Back to the idea of primitives or objects on prototypes, take a look at this... ```prettyprint angular.module("Demo", []) .controller("ChildCtrl", function($scope) { $scope.name = "foo"; $scope.user = {}; $scope.user.name = "bar"; }) .controller("AnotherChildCtrl, function($scope) { $scope.name = "overwrites foo"; $scope.user.name = "changes foo in ChildCtrl"; }); ``` ```prettyprint
``` So, first of all `$scope.name` is set on the `ChildCtrl`. Then it's also set on the `AnotherChildCtrl`. Thinking back on the first examples of this, the `$scope.name` on `ChildCtrl` has in fact been overwritten in the same way as before. If you need access to a variable from `ChildCtrl` inside of `AnotherChildCtrl`, then you have to use an object to do it! Ain't prototypical inheritance neato. In general, try not to just assign stuff directly to `$scope` as primitives. You're `$scope` is a place to PUT the "model", but is not actually the "model". You'll want to do more with objects and always see a "." in your views when referencing a `$scope` property. Aka... ```prettyprint {{user.name}} ``` vs. ```prettyprint {{name}} ``` ### Directive isolated scope One last thing. Take a look at the following video... ![](http://d.pr/i/K3Hx+) This video is based off of this code... ```prettyprint angular.module("Demo", []) .controller("ChildCtrl", function($rootScope, $scope) { console.log("root", $rootScope); $rootScope.rootyThing = "I am groot"; console.log("ctrl", $scope); $scope.childCtrlProp = "heyo"; }) .directive("isoElement", function() { return { restrict: "E", scope: true, link: function(scope) { console.log("directive", scope); scope.foo = "bar"; console.log(scope.childCtrlProp); console.log(scope.rootyThing); } }; }); ``` When `scope: true` is used when creating a directive, you'll get inheritance. If you use `scope: {}` and have properties on there, you've created an "isolated" scope. If you refer back to the source for `Scope.$new` you'll see where that happens. ```prettyprint if (isolate) { child = new Scope(); child.$root = this.$root; child.$$asyncQueue = this.$$asyncQueue; child.$$postDigestQueue = this.$$postDigestQueue; } ``` # Conclusion This stuff seems really complicated when you first get into it. The basic ideas though are all based on prototypical inheritance. If you'll fully grok that, then angular scope inheritance is nothing more than that. ### Node.js and express wrapper for Yahoo Fantasy API URL: https://www.jonathancreamer.com/node-js-and-express-wrapper-for-yahoo-fantasy-api/ Last updated: 2014-08-05T14:32:34.000Z With the NFL season just 31 days away (8/3/2014), I'm proud to release a Node.js wrapper for the Yahoo Fantasy API! It's not a whole lot yet, and basically just helps with the setup and not with individual API calls, but it's still one of the only one's around, and should greatly ease the pain of working with the API. [https://github.com/jcreamer898/fantasy-sports](https://github.com/jcreamer898/fantasy-sports?ref=jonathancreamer.com) You can check out the README on the repository for a more in depth look, but the basic idea is you setup an express app, setup some options, and hook into a few routes to setup and call the API. ```prettyprint var FantasySports = require('FantasySports'); FantasySports.options({ "accessTokenUrl": "https://api.login.yahoo.com/oauth/v2/get_request_token", "requestTokenUrl": "https://api.login.yahoo.com/oauth/v2/get_token", "oauthKey": process.env.OAUTHKEY, "oauthSecret": process.env.OAUTHSECRET, "version": "1.0", "callback": "http://yourwebsite.com//auth/oauth/callback", "encryption": "HMAC-SHA1" }); // app.get("/auth/oauth") exports.oauth = function(req, res) { FantasySports.startAuth(req, res); }; // app.get("/auth/oauth/callback") exports.authorize = function(req, res) { FantasySports.endAuth(req, res); }; exports.myTeams = function(req, res) { FantasySports .request(req, res) .api('http://fantasysports.yahooapis.com/fantasy/v2/users;use_login=1/games;game_keys=nfl/leagues?format=json') .done(function(data) { // Do stuff with data res.json(/* your response */); }); }; ``` Hope it'll ease your pain in setting up your own Fantasy App for your league! As usual per OSS, feel free to have at it and let me know if you have questions or any feature ideas. ### Scope creep, a deep dive into Angular's scope URL: https://www.jonathancreamer.com/working-with-all-the-different-kinds-of-scopes-in-angular/ Last updated: 2014-08-05T14:31:31.000Z Getting used to the concept of `scope` in Angular is among the more difficult concepts to fully grok when first being introduced to the magical land of Angular. ![](http://media0.giphy.com/media/11a9K7FLvTD9Kw/giphy.gif) Like anything else though, "once you understand it, it's really not that bad"... ### $rootScope This is the mother of all scopes. Quite literally. All other scopes in Angular descend from it. The `$rootScope` is created on the DOM element where you set up `ng-app`. ```html ``` You can prove this to yourself by simply adding `ng-app` to the `html` tag of a page, and running this in the console... ```js angular.element(document.documentElement).scope() ``` That will log out the `$rootScope` in the console. If you go run any of the hello world examples for angular out there now, such as one like from the Angular home page... ```html

Hello {{yourName}}!

``` There is a **directive** here on the input called `ng-model`. The job of a directive is to link and element to a scope, and allow you to work with that DOM element by adding behavior, or transforming it. The `ng-model` directive will bind `input`, `select`, and `textarea` elements to a scope. In this case, this directive is linking an element to the `$rootScope` by adding a new `yourName` property to it. Angular attaches to the *change* event of that element. Any time the change event fires, angular updates the value of `yourName` on the scope with the value of the element. This in turn tells anybody interested in the value of `yourName` that its value has changed. This is all done through angular's crazy dirty checking stuff. In this case, there's no `yourName` property defined on `$rootScope` yet, so angular will just create it for you. That concept is all known as **2 way data binding**; the concept of changing a model value which gets reflected in the UI and changing a UI that gets reflected in the model. ### $scope in controllers The first step to understanding `$scope` in an Angular controller is to truly understand what a controller's job is in the first place. > The job of a controller is to pass data from the model, to the view. That's it. Plain and simple. The view (aka user) asks for something from the controller, and the controller turns to the model and says, "hey, I need this thing". Then the controller takes that thing, and hands it back to the view. Controller are meant to be thin, and testable by keeping as much of your problem solving and state manipulation code inside of a "model". I generally think of the "model" in angular as a combination of [angular services](http://jonathancreamer.com/understanding-angular-js-services-the-easy-way/?ref=jonathancreamer.com), and simple objects that you attach to scopes. So in essesnce your model can be whatever you want it to be which can be a little confusing at first. The general idea though is that whatever your "model" is, you use `$scope` to tell your view about it. This is why some people will call `$scope` a "viewModel". A controller's job with respect to Angular is to set up functions and properties that the view can use. The primary way you set these functions and properties up is by injecting `$scope` into the controller. ```js angular.module("MyModule") .controller("FooCtrl", function($scope) { $scope.user = { name: "Foo" }; $scope.doFoo = function() { // Do something! }; }); ``` When you use the `ng-controller` to bind an element to the view, a new `ChildScope` is created which prototypically inherts from the `$rootScope`. You can then attach these values to the view with directives and expressions. ```html {{user.name}} ``` ### Nested Controllers This is where things get weird... ![](http://gifs.joelglovier.com/oh-shit/pirates-runs-away.gif) Controllers can be nested so you have to be careful about a few things. Because of the fact that the scopes do inherit from other scopes, if you create a **primitive** value (string, number, boolean) on a parent scope, the child scope will have an be able to manipulate the value. What will happen though is if a child is changing **primitive** values on a parent, it will end up creating a copy of the value in the child, and break the inheritance chain. This can be avoided... > `ng-model` should always have a "." in the name As a rule of thumb this can help avoid the problem of overwriting a parent's scope values. ```html ``` When you add a `user` object onto a controller's scope, you can edit that user object's properties in the child and not break the inheritance chain from the parent. Another thing you can do when nesting controllers is use the newer **controllerAs** syntax. ```js angular.module("MyModule") .controller("FirstCtrl", function() { this.user = { name: "Dr. Evil" }; }) .controller("SecondCtrl", function() { this.lair = { name: "Underground" }; }); ``` ```html
{{first.user.name}} {{second.lair.name}}
``` Using the **controllerAs** syntax is a small syntax change that can just add some clarity to where different values come from. Notice how in the controller rather than assigning the values to `$scope`, they are actually assigned to `this`. Don't let this fool you too much. When you use this syntax, that simply means it will take the values you put on `this`, and under the covers, assign them to `$scope`. Using `controllerAs` is effectively the same thing as this... ```js angular.module("MyModule") .controller("FirstCtrl", function($scope) { $scope.first = this; $scope.first.user = { name: "Dr. Evil" }; }) .controller("SecondCtrl", function($scope) { $scope.second = this; $scope.second.lair = { name: "Underground" }; }); ``` It's just a little cleaner to not have to use `$scope` when you don't need to do anything but assign values and functions. ### scope in directives Another sticky place when working with scopes in Angular is when writing custom directives. Part of the confusion comes from the fact that when you create a directive, one of the properties you pass on the directive definition is `scope`... ```js angular.module("MyModule") .directive("MyDirective", function() { return { scope: "false|true|{}" } }); ``` There are a few options when setting up scope for a directive. First of all, the default value for this option is `scope: false`. ```js angular.module("MyModule") .directive("MyDirective", function() { return { scope: false, link: function(scope, el) { scope.myNewProp = "OOPS, I'm on the parent!"; } } }); ``` This default can lead to some dangerous issues especially when trying to create re-usable directives... ![](http://i.imgur.com/vpYexIk.jpg) When you have `scope: false`, the a new scope will NOT be created for this directive. That means it will use the parent scope by default. So, if you think you're creating a property on a scope local to your directive, you are in fact actually creating a property on the parent scope. Not a very re-usable solution. Another option you have is to pass `scope: true`. Passing this option will tell angular to create you a new child scope for this directive. This child scope will be similar to how the scope in the `ng-controller` works. It will prototypically inherit from it's parent in the exact same way. This means you'll have access to the parent scope, but also be able to add new properties to a scope local to the directive... ```js angular.module("MyModule") .directive("MyDirective", function() { return { scope: true, link: function(scope, el) { scope.someParentObject.foo = "See I can still change things because of inheritance"; scope.someLocalObject = { foo: "Yay, I'm local only to this direcive!" } } } }); ``` In this example you can see that because of prototypical inheritance, this directive's scope will have access to a parent property. The difference now though is that when a NEW property is added to the scope, it will be created locally within the directive's scope and not directly on the parent scope. The **Isolate Scope** is the most complicated form of scope for directives. ```js angular.module("MyModule") .directive("MyDirective", function() { return { scope: { property: "=", expression: "@" action: "&" }, link: function(scope, el) { scope.someParentObject.foo = "See I can still change things because of inheritance"; scope.someLocalObject = { foo: "Yay, I'm local only to this direcive!" } } } }); ``` When an object literal is passed as the parameter value for `scope` on a directive, a new `isolate` scope is created for the directive. It still inherits from the parent scope, however only the named properties from the `scope` parameter will be passed into this new isolated scope. You can almost think of the `scope` option as a filter with three different ways of filtering. You can see each of the three in the previous example: "@", "=", and "&". Here's examples on how to use each of the three methods. ### \= ```js angular.module("MyApp", []) .controller("UserCtrl", function($scope) { $scope.loggedInUser = { name: "Austin Powers" } }) .directive("myUserDirective", function() { return { restrict: "E", template: "
", scope: { user: "=" }, link: function(scope) { console.log(scope.user) // { name: "Austin Powers" } } }; }); ``` You would then use this directive like... ```html ``` By adding `user: "="` to the `scope` property of your directive, you've basically setup a way to pass an object from the parent scope down to the directive scope by using `user="loggedInUser"` as an attribute on the directive. It's also worth noting that if you wanted to, with any of the three scope properties you can pass an optional alias name along with '=, @, or &', there will be an example of this later. When using the `=` sign as an option you have basically set up a two way binding between the directive and the parent scope. If you then change `user` in the directive, the change will be reflected in the parent's `loggedInUser` property. The `=` option will always be a single string value (no {{}}'s are needed here) representing some property on the parent scope of the directive. So, here in the previous case, `loggedInUser` is a property on the `UserCtrl`'s scope. When you type in the input box generated by the directive, you'll see `ng-model="user.name"`. Changing the value of this text box will actually end up changing the `loggedInUser.name` from `UserCtrl`! ### @ The `@` option of scope is a way to pass in a string value, or a value containing `{{}}`'s that will get interpolated. This is effectively a one way binding. You cannot change the value on the directive scope and have it be reflected in the parent scope, unlike with the `=` option. If you want to reference properties from the parent scope, you must use the curly braces around them. Whatever you pass in as the value of a property that uses the `@` option will be interpolated. Here's an example... ```js angular.module("MyApp", []) .controller("UserCtrl", function($scope) { $scope.loggedInUser = { firstName: "Austin", middleName: "Danger", lastName: "Powers" }; }) .directive("myUserDirective", function() { return { restrict: "E", template: "{{fullName}}", scope: { fullName: "@name" } }; }); ``` And this is how you would use that directive... ```html ``` Notice that in the `name="..."` the `loggedInUser.firstName`, is a value on the parent of the directive in the `UserCtrl`. On the directive scope, because of `fullName: "@name"`, you'll see that you can pass values via the `name` attribute and the `fullName` will be added to the scope. String interpolation will kick in, and `fullName` is going to equal "Austin Danger Powers". That's also what the template `template: "{{fullName}}",` will render. ### & The last option for `scope` in a directive is `&`. This allows you to fully invoke an expression within the context of the parent scope. You basically get a wrapper function that wraps whatever you pass in so you can invoke it in the directive. The following example is a bit trivial, but in order to fully understand this concept, it's a good place to start. ```js angular.module("MyApp", []) .controller("MathCtrl", function($scope) { $scope.add = function(x, y) { return x + y; }; }) .directive("myAddThings", function() { return { restrict: "E", template: "{{result}}", scope: { localFn: "&fn" }, link: function(scope) { scope.result = scope.localFn({ x: 1, y: 2 }); } }; }); ``` So, you can see here the `localFn: "&fn"`. This means that in your `scope`, you'll have a `localFn` available. You can then use that function in the scope of the directive and pass in values via a hash map that will execute in the parent's context. The way this works is by passing in an object that has keys which match the arguments you pass in via the HTML when you invoke the directive... ```js scope.result = scope.localFn({ x: 1, y: 2 }); ``` See how here in the HTML you have... ```html ``` Here's one more practical example of how you can use this option in a directive scope... ```js angular.module("MyApp", []) .controller("MathCtrl", function($scope) { $scope.add = function(x, y) { return parseInt(x, 10) + parseInt(y, 10); }; }) .directive("myAddThings", function() { return { restrict: "E", template: "" + "" + "
{{sum}}
", scope: { localFn: "&fn" } }; }) ``` In this example, there's 2 inputs in the template. One bound to `vals.x`, and one bound to `vals.y`. Since neither of these are previously defined in any scopes, they'll be added to the directives isolated scope. Then the button has an `ng-click='sum = localFn({ x: vals.x, y: vals.y })'` directive. Every time that this button is clicked, it will evaluate the expression. Since the `$scope.add` function is defined in the parent directive, and passed in through the `localFn: "&fn"`, you can then pass in the map of values to execute the add function. Since `$scope.add` simply retuns a value, you can then assign `sum` which will then be another value in the directive scope and can also be used in the template with `{{sum}}`. ### Conclusion Hopefully this helps groking all the scopes in Angular! There's a lot of things to fully wrap your head around with Angular and this is just one of the pieces. Like learning any framework though, it just takes a bit of time and practice to get things really going and fully comprehended. ### Object literals and prototypes in JavaScript URL: https://www.jonathancreamer.com/object-literals-and-prototypes-in-javascript/ Last updated: 2014-08-05T14:32:10.000Z You think you know JavaScript and then little things like this happen... My co-worker [Aaron](https://twitter.com/aaronbushnell?ref=jonathancreamer.com) who is a Visual Design Engineer is in the process of learning JavaScript and he asked me to have a look at a Fiddle. The contents of the fiddle looked like this... ```prettyprint function Pizza (type, slices) { this.type = type; this.slices = slices; } // Using this method of adding to the prototype doesn't log out the constructor correctly on lines 21-22... Pizza.prototype = { divvyUp: function () { alert("The " + this.type + " pizza has been divvied up!"); } }; // ...but this does. // Pizza.prototype.divvyUp = function () { // alert("The " + this.type + " pizza has been divvied up!"); // }; var sausagePizza = new Pizza("sausage", 8); console.log(sausagePizza); console.log(sausagePizza.constructor.prototype); console.log(sausagePizza.constructor.prototype.divvyUp); ``` He was pointing out to me the fact that when he assigns the `Pizza.prototype` to an object, the console doesn't show what he expects to see. Sure enough even at first I was like, wut? ![](http://d.pr/i/YBGT+) Then I stopped to think about it a bit further. Whenever I usually work with assigning things to the prototype, I will either do like his second example... ```prettyprint Pizza.prototype.divvyUp = function () { alert("The " + this.type + " pizza has been divvied up!"); }; ``` Or use jQuery or underscore's extend method... ```prettyprint _.extend(Pizza.prototype, { divvyUp: function() {} }); ``` These two methods usually work just fine. So, why was his example all funkyfied? Well, by assigning an object to the prototype, he decoupled the prototype chain. Now all the sudden `Pizza.prototype.constructor` actually points to the `Object` constructor rather than the `Pizza` constructor. You can see this is the case by logging the instance... ```prettyprint console.log(sausagePizza.constructor === Object); // true ``` When you assign a prototype to an object you've changed the prototypical inheritance of that object. The prototype of the object will no longer have the original constructor function! If you really wanted to use an object literal in this way you can fix it by... ```prettyprint Pizza.prototype.constructor = Pizza; ``` Or you could just use the `$.extend` or `_.extend` if you really had your heart set on using an object literal for setting the prototype chain up. Helping people understand JavaScript is one of the best ways to make sure YOU fully understand JavaScript. ### Understanding Angular.js services the easy way URL: https://www.jonathancreamer.com/understanding-angular-js-services-the-easy-way/ Last updated: 2014-05-15T21:43:17.000Z One of the difficulties when first learning angular is understanding services and what they really are. One of the problem with services is there are several different ways to create one. This leads to many people saying which one should I choose? There's been over 131k views on this Stack Overflow question... [http://stackoverflow.com/questions/15666048/angular-js-service-vs-provider-vs-factory](http://stackoverflow.com/questions/15666048/angular-js-service-vs-provider-vs-factory?ref=jonathancreamer.com) That's a lot. I've been doing some source diving in Angular lately and came across the following code... ![](http://d.pr/i/R0zs+) There it is in less than 10 lines of code exactly what each three are; they are nothing but calls to the underlying provider method! Each one just calls the provider slightly differently. In any of the 3 cases though, the services are singletons, as well as lazy loaded. Let's break them down... ### Factory Here's the factory formatted a bit better. All it is is a call to the underlying `provider` method, and it passes along a function as the `$get` method. ```prettyprint function factory(name, factoryFn) { return provider(name, { $get: factoryFn }); } ``` The `$get` will later be called by the `$provider` whenever someone tries to use the service. Here's how this looks when actually creating a factory. ```prettyprint angular.factory('MyService', function MyService() { return { doSomething: function() { /* ... */} }; }); ``` So essentially the `function MyService()` is passed down and to `$get` and a new `provider` is created... The way the factory works is, whatever you return from the `MyService` function is what you'll get when you inject this service anywhere in your application... ```prettyprint app.controller('HomeController', function(MyService) { MyService.doSomething(); }); ``` ### Service Another way to create an Angular service is with the actual `service` method... ```prettyprint function service(name, constructor) { return factory(name, ['$injector', function($injector) { return $injector.instantiate(constructor); } ]); } ``` The `service` method actually just makes a call to the `factory` method, thereby calling the `provider` method again. In the case of a `service` method, the `$get` will be set to the `factory` function that is returned from the `service` method aka `return factory(name, ['$injector',`. The returned function turns around and asks for the `$injector` as a dependency so that it can create an instance of whatever you pass in as the `constructor` to the `service` method... ```prettyprint angular.factory('MyService', function MyService() { this.doSomething = function() { /* DO IT, DO IT NAOW */ } }); ``` In other words, the function passed in as the second argument just get's called with the `new` operator and the instance of that function is returned to whatever injects the service laster. ### Value The last type of service you can create with angular is a `value` service... ```prettyprint function value(name, val) { return factory(name, valueFn(val)); } ``` The `value` service in turn calls the `factory` function once again. A `value` service is really meant to do nothing more than return whatever is passed in as `val`. That's what the `valueFn(val)` does above... ```prettyprint function valueFn(value) { return function() { return value; }; } ``` You can use the `value` service method as a way to store some state, some configuration options or whatever you may need it for... ```prettyprint angular.value('Values', ['foo', 'bar', 'bam']); ``` Then you can inject it and simply use whatever value is returned... ```prettyprint app.controller('HomeController', function(Values) { Values.forEach(function(val) { console.log(val); }); }); ``` ## Conclusion Hopefully that helps at leat eliminate some of the confusion what Angular.js services are. How you use them is still totally up to you as a developer, but it's good to at least have an understanding of what you're working with. The three different methods have different use cases, but to reiterate are all going to be singleton methods that you can inject anywere else that you can inject things in an angular app. ### The state of angularjs controllers URL: https://www.jonathancreamer.com/the-state-of-angularjs-controllers/ Last updated: 2014-05-06T16:38:17.000Z Angular.js is based on the ever popular MVC architecture pattern. However, like many other JavaScript frameworks, MVC can very easily be muddied up and misused. Understanding the framework you're using, whether that be Angular, Backbone, etc, and how the different M-V-? pieces work together is important to the maintainability and scalability of your application. When the ideas of these different MV? architectures get misconstrued, bad things can happen. For example in Angular, you'll see many examples of controllers holding model state that in reality they probably should not be. > The third part, the controller, accepts input and converts it to commands for the model or view. > \-- Wikipedia (1) The controller's job is not to be the model, the controller's job is to talk to the "model". For example, take an order controller... ```prettyprint angular.module('Store', []) .controller('OrderCtrl', function(Products) { this.products = Products.query(); this.items = []; this.addToOrder = function(item) { this.items.push(item); }; this.removeFromOrder = function(item) { this.items.splice(this.items.indexOf(item), 1); }; this.totalPrice = function() { return this.items.reduce(function(memo, item) { return memo + (item.qty * item.price); }, 0); }; }); ``` What you have here is a controller that has a list of `products` retrieved from the `Products` service. Then there is a list of `items` that is on the `OrderCtrl`. At first glance, this is not unlike much of the code you'll see in Angular's docs [https://docs.angularjs.org/api/ng/directive/ngController](https://docs.angularjs.org/api/ng/directive/ngController?ref=jonathancreamer.com). For a "small prototype", maybe that's fine. For larger apps however, probably not a great idea. What if you need to have a cart controller that displays the total items in your order, or the price? What if you want to have a separate order controller in some other widget? You'll either end up duplicating code, or having to hack some way for the controllers to talk to each other to keep their state up to date. The solution to this problem is to do utilize the "M" of MVC properly and move that state out of your controller into some "model". With angular, there's a little bit of ambiguity in what the "M" really is. I like to think of the "model" as the actual data you retrieve from the backend, and how you retrieve it on the client. With angular the ambiguity in the "model" is made difficult in part because there are lots of different ways to create a "model" aka services, factories, and providers. That's why there are so many [questions](http://stackoverflow.com/questions/15666048/angular-js-service-vs-provider-vs-factory?ref=jonathancreamer.com) on stack overflow about it. One of the easiest ways to deal with it is to use `angular.factory` as a "model". ```prettyprint angular.module('Store') .factory('Order', function() { var add = function(item) { this.items.push(item); }; var remove = function(item) { if (this.items.indexOf(item) > -1) { this.items.splice(this.items.indexOf(item), 1); } }; var total = function() { return this.items.reduce(function(memo, item) { return memo + (item.qty * item.price); }, 0); }; return { items: [], addToOrder: add, removeFromOrder: remove, totalPrice: total }; }); ``` You now have an injectable "model" you can span across multiple controllers, and remove the state from the controller. ```prettyprint angular.module('Store', []) .controller('OrderCtrl', function(Products, Order) { this.products = Products.query(); this.items = Order.items; this.addToOrder = function(item) { Order.addToOrder(item); }; this.removeFromOrder = function(item) { Order.removeFromOrder(item); }; this.totalPrice = function() { return Order.total(); }; }); ``` The controller is now much thinner, and holds no state. This is important because controllers are created and destroyed very often in the lifecycle of an angular app, whereas a `factory` is only created a single time. Now you can use the `Order` "model" in other controllers quite easily. ```prettyprint angular.module('Store', []) .controller('CartCtrl', function($scope, Order) { $scope.items = Order.items; $scope.$watchCollection('items', function() { $scope.totalPrice = Order.totalPrice().toFixed(2); }); }); ``` The `$scope.$watchCollection` can keep an eye out for changes on the `Order.items` array and update the `$scope.totalPrice` when the items change. There are other ways of doing this such as firing an event when a new item is added to the `Order`, but this should work in this case. The main benefit here is that the order has been pulled out into it's own "model" that can more easily be reused in other parts of the application. Here's a JSBin of the code above... [JS Bin](http://jsbin.com/rifob/1/embed?js,output&ref=jonathancreamer.com) 1. [http://en.wikipedia.org/wiki/Model–view–controller](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller?ref=jonathancreamer.com) ### An angular.js event bus with postal.js URL: https://www.jonathancreamer.com/an-angular-event-bus-with-postal-js/ Last updated: 2014-05-06T16:40:53.000Z Ideally in an angular application, controllers are independent units of code that have no reference to any other controllers. There are cases though in which you may need to communicate with other controllers in your application. For example you may have an `Orders` controller, that needs to tell a `Cart` controller that a new item has been added. One of the best ways of accomplishing this type of communication is through the use of an event bus. My favorite event bus framework is [postal.js](http://github.com/postaljs/postal.js?ref=jonathancreamer.com). ### What is it? > Postal.js is an in-memory message bus - very loosely inspired by AMQP - written in JavaScript. Postal.js runs in the browser, or on the server-side using Node.js. It takes the familiar "eventing-style" paradigm (of which most JavaScript developers are familiar) and extends it by providing "broker" and subscriber implementations which are more sophisticated than what you typically find in simple event delegation. You can use postal by publishing messages accross specific channels. In this way you can segregate your messages to things like `app`, `cart`, `ui`, etc. It also uses an *envelope* pattern to prevent having **n** arguments in your subscription callbacks. Using postal.js, you can easily decorate the `$scope` and add a `$bus` that will allow you to communicate with other controllers in your application. ```prettyprint angular.module('myApp') .config(function ($provide) { $provide.decorator('$rootScope', [ '$delegate', function ($delegate) { Object.defineProperty($delegate.constructor.prototype, '$bus', { value: postal, enumerable: false }); return $delegate; }]); }); ``` Now in your controllers you have postal available as `$scope.$bus`... ```prettyprint 'use strict'; angular.module('myApp') .controller('CartCtrl', ['$scope', function ($scope) { $scope.$bus.subscribe({ channel: 'orders', topic: 'order.new', callback: function(data, envelope) { console.log('it worked', data, evenlope); } }); } ]) .controller('OrderCtrl', ['$scope', function ($scope) { $scope.order = function() { $scope.$bus.publish({ channel: 'orders', topic: 'order.new', data: { /* order info */ } }); }; } ]); ``` In the `CartCtrl`, the `$scope.$bus.subscribe` method is called. This subscription gets set up to listen on the `orders` channel for any messages with the `order.new` topic. When a message comes throught that matches that topic, the `callback` will be invoked. In the `OrderCtrl`, the `$scope.$bus.publish` method is called whenever `$scope.order` is called. This publishes a message on the right channel, with the right topic that will trigger the subscription. The `data` sent via the publish will be recieved in the `callback` of the subscription, as well as an `envelope`, which is a wrapper around the data. ### Conclusion Using the `$bus` decorator makes communicating with other controllers extremely simple. Of course you can use any messaging framework you'd like. If you'd like to learn more about postal, check out the repo, clone it, and feel free to give [Jim Cowart](http://twitter.com/ifandelse?ref=jonathancreamer.com) some love for making such a great library! ## UPDATE 5/6/14 A commenter pointed out that there's a potential memory leak since controller instances are created multiple times. I went in and took a look and added some code to listen to the `$destroy` event on the scope and call unsubscribe. ```prettyprint angular.module('introToAngularApp') .config(function ($provide) { $provide.decorator('$rootScope', ['$delegate', function ($delegate) { Object.defineProperty($delegate.constructor.prototype, '$bus', { get: function() { var self = this; return { subscribe: function() { var sub = postal.subscribe.apply(postal, arguments); self.$on('$destroy', function() { sub.unsubscribe(); }); }, channel: postal.channel, publish: postal.publish }; }, enumerable: false }); return $delegate; }]); }); ``` I had to use the `get` of `Object.defineProperty` so that I can have a hold of the correct `this` for the `$on`. Then it simply listens for that event, and calls `sub.unsubscribe()`. That should help alleviate any memory leak problems! ### Epic return to the wonderful land of appendTo URL: https://www.jonathancreamer.com/epic-return-to-the-wonderful-land-of-appendto/ Last updated: 2014-05-22T21:15:16.000Z The last 5 months of my life have been filled with great things while working for the startup over at Tech.pro. Sometimes good things don't last super long though, and today is my first day back with my great friends at [appendTo](http://appendto.com/?ref=jonathancreamer.com). ![](http://d.pr/i/iPiH+) In the last few months I've gotten my mind blown by [Leland Richardson](https://twitter.com/intelligibabble?ref=jonathancreamer.com) and [Jesse Lisby](https://twitter.com/jesselisby?ref=jonathancreamer.com). Armed with my new knowledge, as of today I've returned back to appendTo as a JavaScript Engineer! I'm looking forward to continuing to grow my JavaScript fu with the brilliant people at appendTo. ### Excited to become a Microsoft MVP URL: https://www.jonathancreamer.com/excited-to-become-a-microsoft-mvp/ Last updated: 2014-04-08T20:06:37.000Z Yesterday I was honored to join the list of Microsoft MVP's! I am now an MVP for Internet Explorer. [http://mvp.microsoft.com/en-us/mvp/Jonathan%20Creamer-5000770](http://mvp.microsoft.com/en-us/mvp/Jonathan%20Creamer-5000770?ref=jonathancreamer.com) An IE MVP is recognized for supporting the web community at large, meaning I'm an MVP of JavaScript, HTML, and CSS, etc. I hope to continue growing as a developer, and getting better at reaching larger audiences to help others grow. My long term goals as a programmer have always been to learn as much as possible, and then turn right around and spread that knowledge to others to help THEM grow as much as possible. By getting awards like the MVP award, joining programs like the [IEuserAgents](http://useragents.ie/?ref=jonathancreamer.com), [tweeting](http://twitter.com/jcreamer898?ref=jonathancreamer.com), [blogging](http://tech.pro/jonathancreamer?ref=jonathancreamer.com), attending user groups and conferences, I hope to able to reach out to more and more people in the community. Thanks to those of you in the community who follow me! ### The most beautiful sound in the world URL: https://www.jonathancreamer.com/the-most-beautiful-sound-in-the-world/ Last updated: 2014-03-28T16:29:56.000Z Went to the doctor for our 13 week baby appointment today. Doctor said everything was all good! Here's the heartbeat of little Creamer... So excited for this October when our family will grow by one... ![](http://d.pr/i/aWKx+) ### Javascript Patterns Mediator vs Observer URL: https://www.jonathancreamer.com/javascript-patterns-mediator-vs-observer/ Last updated: 2013-11-04T23:04:14.000Z An approach to separating your Ajax requests from your DOM is to use **messaging**. Messaging allows different parts of an application to communicate with each other without knowing that the others even exist. This means that various pieces of the application are hot swapable! So, if you have an Ajax request class that is separate from your DOM logic, all you have to do to change the Ajax logic is, change the Ajax logic. You don't have to go fishing through all the places that your Ajax piece was used. Instead just change it in one place and it's done. There are different types of messaging patterns such as observer, and mediator. In either pattern there would be some object emitting messages (or events). In observer based messaging, it might look like... ```prettyprint var user = new User(); var app = { init: function() { user.on( "login", this.userLoggedIn ); }, userLoggedIn: function() { // user is now logged in. } }; app.init(); // somewhere else in the app user.trigger( "login" ); ``` So, in this example, the app has a direct reference to the user so you can say, "the app is observing the events on the user". Using the mediator pattern would look like... ```prettyprint // the user is some kind of an emitter var user = new User(); var app = { events: new SomeKindOfEmitter(), init: function() { app.events.on( "login", this.userLoggedIn ); }, userLoggedIn: function() { // user is now logged in. } }; app.init(); // somewhere else in the app app.events.trigger( "login" ); ``` Using a mediator allows the app logic to be separated from the user logic. This could also allow any other object in the application to listen for the login event by just listening for events triggered from app.events. ### Building Decoupled JavaScript Applications with Postal.js URL: https://www.jonathancreamer.com/building-decoupled-javascrip-applications-with-postal-js/ Last updated: 2014-01-07T19:59:57.000Z Working with asynchronous code is one of the best features of JavaScript. With Ajax, it's very simple to request some JSON API, add a callback and update the UI with the result of that request very easily. Especially when using jQuery. However, writing an application that is testable, decoupled, and manageable, is a whole other beast. The typical approach to making Ajax requests in an application is to use jQuery's Ajax, pass a success callback and update the DOM with the response. That's great if you have some small application that doesn't need a lot of updates and manageability. What happens in an enterprise environment when someone writes that kind of shortcut Ajax code, makes a mess of the DOM in the success callback? You've just coupled your DOM directly to your request. What if the new boss comes in and says, "I HATE TEH JQUERY!"? Well, at that point you have to find the code in the success callback that messed with the DOM, rip it out, rewrite your Ajax logic, etc... It's a mess. A great way to solve this problem is through messaging. A great open-source JavaScript messaging library is postal. Take a look at how 3 objects having separate responsibilities can use postal and not know that each other exist, thereby decoupling the application, and making it testable as well! The weather is from [http://openweathermap.org](http://openweathermap.org/?ref=jonathancreamer.com) ```prettyprint var Weather = function() { this.channel = postal.channel( "weather" ); this.channel.subscribe( "fetch", this.fetch ).withContext( this ); }; _.extend(Weather.prototype, { fetch: function( city ) { $.ajax({ url: "http://openweathermap.org/data/2.1/find/name?q=" + city + "&units=imperial", dataType: "jsonp", success: _.bind(function( data ) { this.channel.publish( "fetched", data.list[ 0 ] ); }, this ) }); } }); var weather = new Weather(); ``` The Weather class listens on a channel called "weather". When a "fetch" topic comes across, it preforms an ajax call to retrieve the weather. When the request comes back, the data is published back onto the weather channel with a topic of "fetched". Here's the app object. ```prettyprint var App = function() { this.channel = postal.channel( "weather" ); this.channel.subscribe( "fetched", this.gotWeather ).withContext( this ); }; _.extend(App.prototype, { getWeather: function( city ) { this.channel.publish( "fetch", city ); }, gotWeather: function( data ) { console.log( "weather retrieved" ); } }); var app = new App(); UI.init(); app.getWeather( "Thompsons station, tn" ); ``` The app channel is listening on the "weather" channel for the "fetched" topic. When the "fetched" topic comes across, the gotWeather method is fired. The UI code looks like... ```prettyprint var UI = { init: function() { postal.channel( "weather" ).subscribe( "fetched", UI.showWeather ); }, showWeather: function( data ) { var weather = data.weather[0], displayWeather = ""; displayWeather += "The weather in " + data.name + " is " + weather.description + "."; displayWeather += "The wind is " + data.wind.speed + "mpg, with gusts of " + data.wind.gust + "."; $( "#weather" ).html( displayWeather ); } }; ``` The UI sets up a subscription in the init method to also listen on the "weather" channel for the "fetched" topic. By having postal in the app, the UI and the app are able to both receive information from the Weather object, yet none of the three objects know that each other exist! This means if ever there was a new portion of the application that needed the same weather information, another subscription could be created and have no effect on any other parts of the application. The three different pieces could easily be unit tested completely separate from one another. Here's the fiddle of the working sample. [http://jsfiddle.net/jcreamer898/xfRWt/](http://jsfiddle.net/jcreamer898/xfRWt/?ref=jonathancreamer.com) ### A new blog, and a new adventure URL: https://www.jonathancreamer.com/a-new-blog-and-a-new-adventure/ Last updated: 2013-11-01T21:43:29.000Z ### A new blog I'm happy to finally have launched a new website for myself. Thanks to the new Ghost blogging platform! I gotta say this is a great platform. Super easy, clean, and extremely easy to modify and plug in to. I'd been using Wordpress for a longtime, but I accidentally got myself got up in a redirect loop updating a MySql database and never got around to fixing it. Then I migrated to Blogger just to have something up in the mean time. When Ghost came out, I tried it out and loved it. Writing blogs in Markdown is so full of win it's ridiculous. ### A new adventure I've spent the last \~1.5 years working for [appendTo](http://appendto.com/?ref=jonathancreamer.com), and I'm announcing bittersweetly that I'll be departing on November 5, 2013 and beginning a new adventure with [http://tech.pro](http://tech.pro/?ref=jonathancreamer.com). Working for appendTo has been one of the greatest experiences of my life and I've been honored and humbled by my time there. [Mike Hostetler](http://twitter.com/mikehostetler?ref=jonathancreamer.com) is an incredible CEO and runs one of the best companies a developer could possibly work for. So much great stuff comes out of appendTo and it was an incredibly difficult decision to leave, but I felt like this was an opportunity that I just HAD to be a part of! AppendTo has become a family to me and I'll be sad to depart. [Tech.pro](http://tech.pro/?ref=jonathancreamer.com) will provide me with an entirely new set of problems to solve. I'll be back in the ASP.NET MVC world for the first time in a while so I'm extremely excited about that. Working entirely with JavaScript for the past couple years has been a real pleasure, but I'm also ready to work in the server side world again. ### Thanks God has blessed me with incredible opportunites and amazing influencers in my life. I've been extremely fortunate in my short progamming career to work with some of the best in the business! [Elijah Manor](http://twitter.com/elijahmanor?ref=jonathancreamer.com) and [Jim Cowart](http://tiwtter.com/jimcowart?ref=jonathancreamer.com) reached out and helped me get my position at appendTo and I'm forever greatful to them for that as well as for just being great dudes and great friends! Guys like [Doug Neiner](http://twitter.com/dougneiner?ref=jonathancreamer.com), [Ryan Niemeyer](http://twitter.com/rpniemeyer?ref=jonathancreamer.com), [Eli Perelman](http://twitter.com/eliperelman?ref=jonathancreamer.com), [Jonathan Sampson](http://twitter.com/jonathansampson?ref=jonathancreamer.com), and [Alex Robson](http://twitter.com/a%5Frobson?ref=jonathancreamer.com) are amazingly smart minds and have taught me a lot during my time working with them, and of course there's many more too! You don't get to work for bosses much better than [John Kellar](http://twitter.com/johnkellar?ref=jonathancreamer.com) and Mike. Thanks to all of appendTo for making me feel apart of a special company.