DEV Community

Craig Morten
Craig Morten

Posted on • Edited on

Running Code From READMEs In 10 Lines Of Deno

Recently I was reading through the Deno Pogo server framework README and was about to create a new folder and files so I could test out some of the README examples when I wondered - wouldn't it be great if we could just run the examples straight from a README without having to copy the code?

Perhaps a small program could find markdown blocks and offer the ability to run examples, perhaps a little like a python Jupyter notebook, but driven off of a README using existing snippets?

Now this was starting to sound a little complicated, but as a good start, why not build something that will execute code based on a start and end line number?

Writing a README runner

const [url, startStr, endStr] = Deno.args;
const start = parseInt(startStr);
const end = parseInt(endStr);
Enter fullscreen mode Exit fullscreen mode

First we would need a way to provide the README url and the start / end line arguments - we can do this by making use of the Deno.args property which contains an array of all passed args.

The args are stored as strings, so we parse them into integers.

const response = await fetch(url);
const page = await response.text();
Enter fullscreen mode Exit fullscreen mode

Deno supports browser APIs meaning we can using the fetch method to then retrieve the page from the url.

If we were to log the page string at this point we would see the text for the entire webpage.

We would then need to split out the desired section of the README based on our start and end line numbers.

const lines = page.split("\n");
const script = lines.slice(start - 1, end).join("\n");
Enter fullscreen mode Exit fullscreen mode

For this we can split the page contents by the newline \n character so we create an array of lines, and then create a script string by using the array slice method to extract the lines from our start number up to our end number. Because lines start from "line 1" and JavaScript arrays' start from index 0, some care is needed to make sure we shift the start down by 1.

We line join the sliced out part of the array back together using newline characters.

If we were to console.log out the script object, we should now be able to see just the part of the README we wanted!

const tmpFilePath = await Deno.makeTempFile({ suffix: ".ts" });
await Deno.writeTextFile(tmpFilePath, script);
await import(`file://${tmpFilePath}`);
Enter fullscreen mode Exit fullscreen mode

Finally, we create a temporary typescript file and write our script to this file using the Deno.makeTempFile() and Deno.writeTextFile() methods. Take care to remember to use await as most of Deno's APIs are asynchronous.

We can now use a dynamic import to run our new script which we've saved to a temporary file!

Here's all the code in one block:

const [url, startStr, endStr] = Deno.args;
const start = parseInt(startStr);
const end = parseInt(endStr);

const response = await fetch(url);
const page = await response.text();

const lines = page.split("\n");
const script = lines.slice(start - 1, end).join("\n");

const tmpFilePath = await Deno.makeTempFile({ suffix: ".ts" });
await Deno.writeTextFile(tmpFilePath, script);
await import(`file://${tmpFilePath}`);
Enter fullscreen mode Exit fullscreen mode

And that's it, we can now run a script from a snippet of any valid url with 10 lines of Deno code.

We could then run one of the Pogo examples mentioned before like so:

deno run --allow-net --allow-read --allow-write --reload ./mod.ts https://raw.githubusercontent.com/sholladay/pogo/master/README.md 31 39
Enter fullscreen mode Exit fullscreen mode

Where we have saved the code in a file called mod.ts.

Note: We have had to provide quite a few permission flags. The --allow-net flag allows us to run the fetch command, --allow-read allows us to read the provided args and import the file we create, and --allow-write allows us to create the temporary file. Always check the code you are about to run to make sure it is safe to do so, and never add permissions to a Deno script unless you are sure it is safe.

This would run the first example in the Pogo README which currently looks like:

import pogo from 'https://deno.land/x/pogo/main.ts';

const server = pogo.server({ port : 3000 });

server.router.get('/', () => {
    return 'Hello, world!';
});

server.start();
Enter fullscreen mode Exit fullscreen mode

Note: If / when the Pogo README changes, the above example may stop working. You might need to adjust the start and end line numbers passed to the command.

For ease, this script is available on GitHub in the readme-runner repository, meaning you can run the above example by also using:

deno run --allow-net --allow-read --allow-write --reload deno run --allow-net --allow-read --allow-write --reload https://raw.githubusercontent.com/asos-craigmorten/readme-runner/main/mod.ts https://raw.githubusercontent.com/sholladay/pogo/master/README.md 31 39 https://raw.githubusercontent.com/sholladay/pogo/master/README.md 31 39
Enter fullscreen mode Exit fullscreen mode

What are you currently doing with Deno? Do you have any cool ideas of how you can utilise Deno's ability to import code from urls?

Drop your projects, ideas and comments below - it's great to hear what everyone is doing!

That's it for now 👋

Top comments (0)