You are browsing as a guest. Sign up (or log in) to start making projects!

Open comments for this post

6h 23m 50s logged

CLI Remote Script

You can now run Hadronize CLI in a terminal without cloning the repo. If you run the command below, Deno will automatically fetch all the dependencies.

deno run -A http://ethmarks.github.io/hadronize/cli.ts

Node doesn’t support remote scripts because it lacks Deno’s awesomeness, but because of the Deno binary on npm, you can run Deno (and therefore Hadronize) through any of the other package managers!

npx deno run -A http://ethmarks.github.io/hadronize/cli.ts
pnpm dlx deno run -A http://ethmarks.github.io/hadronize/cli.ts
bunx deno run -A http://ethmarks.github.io/hadronize/cli.ts

For example, in the attached screenshot, I SSH-ed into one of my home servers that hadn’t cloned the Hadronize repo nor installed Deno, and tried to run Hadronize as a remote script via pnpm. After resolving dependencies for a few seconds, it ran flawlessly.

How it works

I first encountered Deno remote scripts in Lume’s init command, and my implementation is heavily inspired by it. Basically, all that the cli.ts file does is fetch the CLI code from JSDelivr, which fetches it directly from the GitHub repo. From there, Deno is smart enough to resolve all of the CLI code’s dependencies and run it.

Compatibility

Before today’s changes, running Hadronize via Deno worked perfectly fine. deno task cli behaved identically to pnpm run cli. However, that’s because Deno has an automatic Node compatibility layer, not because Deno can actually run the code natively. That compatibility layer doesn’t really work with remote scripts (because Deno can’t find the package.json), so when I first tried to run the remote script while developing it, it crashed and burned immediately. This was because, without the compatibility layer, Deno and Node handle things fundamentally differently. There were several differences, some of which I expected and some of which I didn’t, but the easiest one to understand is NPM imports.

NPM imports

My CLI code relies on the picocolors library for getting ANSI escape codes. When I was first writing the code, I added the picocolors package via NPM and imported it in the normal Node way:

import pc from "picocolors";

The problem is that Deno can’t parse bare imports like that. Deno expects this format, which Node cannot parse:

import pc from "npm:picocolors";

I tried configuring an import map in deno.json, but it didn’t work. I considered just hardcoding the ANSI escape sequences, but I decided that I would need to figure out how to polyglottally import NPM packages in both Node and Deno at some point, so I might as well figure it out now.

My solution was to create a deps/picocolors.ts file to proxy the picocolors import by dynamically importing it based on whether it’s being run in Deno or not:

const { default: pc } =
  "Deno" in globalThis
    ? await import("npm:picocolors")
    : await import("picocolors");

export default pc;

NPM imports were only one of the issues that sprung up. Frustratingly, the only way to test whether or not a solution worked was to commit, push to main, and test on production. The whole Deno compatibility saga required 12 commits in total.

Next Steps

I’m still working on writing unit tests for the game logic. I already have a few tests, but they’re definitely not comprehensive. I’ll write a devlog about the tests once I’m finished with them.

0
1

Comments 0

No comments yet. Be the first!