npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@clinth/repl-pad

v0.0.7

Published

Javascript REPL

Downloads

7

Readme

repl-pad

Repl-pad takes some Javascript and presents it in an editable REPL-like environment. It is meant for easily making example snippets runnable and editable. Online demo

From this code snippet in your HTML:

<pre>
  Math.random();
  const x = Math.random();
  for (let i=0;i<10;i++) {
    i;
  }

  const fn = (x) => x * 2;

  setTimeout(() => {
    console.log(`result: ${fn(10)}`);
  }, 5000);
</pre>

You can produce this repl-pad, with the source magically pulled from the HTML:

Screenshot

Repl-pad gives the illusion that each line of code is executed independently, with the result shown to the side. But actually each line is executed along with every line that precedes it.

Statements that generate an error are skipped over optimistically:

Screenshot

This is so the REPL experience can be more 'sketchy', but it's worth noting that this behaviour is not how JS code is usually executed.

URL-based imports can be used as well:

Screenshot

Console messages are intercepted and displayed in a mini-console at the bottom of the viewport. This makes it more useful on tablet and mobile devices that lack access to DevTools.

Screenshot

Caveats

Under the hood, eval is used, limiting the use of async/await.

for await ... of and async functions are handled by wrapping the whole snippet in this immediately-invoked function expression. This allows code to run, but not necessarily with the same behaviour as you'd expect.

Examples

Before:

for await (...) { ... }

After:

(async () => { for await (..) { ... } })()

Before:

async function hello() {
  console.log(`hello?`);
}
hello();

After:

(async () => {
  async function hello() {
    let x = 1;
  console.log(`hello?`);
  }
  hello();
});

Usage

npm install --save @clinth/repl-pad

Repl-pad is a vanilla web component. It can be used programatically, but it was meant to be hosted in a stand-alone simple HTML page. Code source comes in, base64-encoded via a URI anchor:

pad.html#ugly-base-64-string

A small helper function allows you to generate this URL automatically, from the basis of a HTML element's innerText. This is meant to convert a static code example into something runnable.

In your HTML:

<pre id="eg1">
const x = 1;
</pre>

In your JS:

import {fromInnerText} from '@clinth/repl-pad';
const el = document.getElementById(`eg1`);

// Yields: pad.html#...
const uri = fromInnerText(el, `pad.html`);

// Create an edit link and add after example
const link = document.createElement(`a`);
link.href = uri;
link.innerText = `Edit`;
el.parentNode.insertBefore(link, el.nextSibling);

One can add edit links for all elements that match a query.

Eg, you have HTML with:

<pre>
  const x = 1;
</pre>

<pre>
  const x = Math.random();
</pre>

And then in JS:

import {fromQuery} from '@clinth/repl-pad/link.js';
const r = fromQuery(`pre`, `pad.html`);

for (const {el, uri} of r) {
  const link = document.createElement(`a`);
  link.href = uri;
  link.innerText = `Edit`;
  el.parentNode.insertBefore(link, el.nextSibling);
}

See this in action in docs/index.html.

Programmatic usage

It's also possible to send JS code programmatically:

const r = new ReplPad(`
  const x = 1;
  x;
`);

Styling

See docs/pad.html for an example.

repl-pad {
  display: flex;
  flex: 1;
}

CSS variables:

--mono-font: a font family
--ui-font: a font family
--padding

Colours assignable via CSS variables:

--ui-bg
--ui-fg
--ui-pinstripe
--output
--ui-highlight-bg
--ui-highlight-fg
--console-bg
--console-fg
--console-error
--console-warn

Re-evaluation

Repl-pad evaluates each line along with its preceding lines. The consequence is that some lines of code are executed several times.

eg, if we have three source lines:

0 console.log(`a`);
1 console.log(`b`);
2 console.log(`c`);

Line #0 will be executed three times. The first time when that statement is being executed, the second to precede line #1 and the third to precede line #2. Likewise, line #1 will be executed twice. Line #2 only runs once.

This may lead to unexpected outcomes if the statement produces side-effects - like printing to the console. Here we'd see:

a
a
b
a
b
c

Repl-pad will not re-evaluate any line starting with console. to avoid confusion. For this reason, avoid changing anything within a console.:

// Bad: data is changed within console. statement
console.log(i++);

// Good: console. console. statement only produces console output.
i++;
console.log(i);

The reevalConsole option can disable this default. To prevent this behaviour for other cases, consider the option reevalUndef=false. When set, any statement that returns undefined will be skipped over when re-evaluating.

Options

Options can be specified as URL parameters. Eg: pad.html?reevalUndef=false#base-64

Or as attributes for the web component. Eg:

<repl-pad reevalUndef="false" reevalConsole></repl-pad>

reevalUndef (true/false. default true)

  • If true, statements that return undefined are re-evaluated. If false, they are skipped. See section above on re-evaluation.

reevalConsole (true/false. default false)

  • If true, statements starting with console. are re-evaluated.

Developing

To work on repl-pad, run a live server and open docs in a browser. tsup is used for building.

Build to ./dist:

npm run build

Continually recompile to ./dist, copying files to docs/dist:

npm run dev