Giter Club home page Giter Club logo

replete's Introduction

Replete

Replete brings interactive programming to JavaScript. It is an evaluator for JavaScript modules, supporting a variety of environments including the browser, Node.js, Deno, Bun, and Txiki.

Once integrated with your text editor, Replete becomes part of your development environment. Source code is sent directly from your editor to Replete, where it is evaluated. Anything from a mere expression to a whole file may be evaluated at a time. The resulting value (or an exception) is reported back for perusal.

Replete encourages the development of modules in isolation, rather than in the context of a running application. Modules written in this way tend to be more independent and hence more reusable, more testable and hence more robust.

Replete is in the Public Domain, and does not come with a warranty. It is at least as dangerous as the source code it is asked to import or evaluate, so be careful.

Communication

Replete operates as a heirarchy of communicating processes, as shown in the diagram below.

                  +------------------------------------------+
                  |                                          |
                  |             Your application             |
                  |         (such as a text editor)          |
                  |                                          |
                  +----------------+-------------------------+
                                   |        ^
                                   |        |
                  Command messages |        | Result messages
                                   |        |
                                   V        |
+-------------------------------------------+-----------------------------+
|                                                                         |
|                                    Replete                              |
|                                                                         |
+-------+----------------+--------------+-------------+------------+------+
        |                |              |             |            |
        |                |              |             |            |
        |                |              |             |            |
+-------+------+ +-------+------+ +-----+-----+ +-----+----+ +-----+------+
| Browser REPL | | Node.js REPL | | Deno REPL | | Bun REPL | | Txiki REPL |
+--------------+ +--------------+ +-----------+ +----------+ +------------+

The Replete process is responsible for coordinating the REPL processes. It can run in Deno, Node.js, or Bun. When Replete runs in a Deno process, for example, we say that Replete is hosted by Deno.

It is important to understand that the choice of host runtime imposes no constraints on the choice of REPLs running underneath. For example, a Deno-hosted Replete can spawn a Node.js REPL just as easily as a Node.js-hosted Replete can spawn a Deno REPL.

Replete communicates by sending and receiving command and result messages. Messages are JSON-encodable objects.

A command message is an object with the following properties:

  • source: The source code to be evaluated, as a string. The source may contain import and export statements.
  • locator: The locator of the module containing the source. It is required if the source contains any relative imports.
  • platform: Either "browser", "node", "deno", "bun", or "tjs". This property determines which REPL evaluates the source.
  • scope: The name of the scope, which can be any string. If undefined, the scope "" is chosen. The scope is created if it does not exist.
  • id: If defined, this property is copied verbatim onto the corresponding result messages. It can be used to associate a result with its command. It can be any value.

A scope holds the value of every variable or function declared during evaluation, allowing them to be used in future evaluations. Distinct scopes provide a degree of isolation, however the same global object is shared by all scopes.

A result message is an object with one of the following properties, each of which is a string representation of a value:

  • evaluation: The evaluated value, if evaluation was completed successfully.
  • exception: The exception, if evaluation failed.
  • out: Any arguments passed to console.log, or bytes written to stdout.
  • err: An exception that occurred outside of evaluation, or bytes written to stderr.

In addition, a result may contain the id property described above.

Here are some examples of commands and the results they might induce.

COMMAND {platform: "browser", source: "navigator.vendor"}
RESULT  {evaluation: "Google Inc."}

COMMAND {platform: "node", source: "process.version"}
RESULT  {evaluation: "v14.4.0"}

COMMAND {platform: "browser", source: "process.version"}
RESULT  {exception: "ReferenceError: process is not defined..."}

COMMAND {platform: "deno", source: "console.log(0 / 0, 1 / 0)"}
RESULT  {out: "NaN Infinity\n"}
RESULT  {evaluation: "undefined"}

COMMAND {platform: "browser", source: "1 + 1", "id": 42}
RESULT  {evaluation: "2", id: 42}

Notable files

Replete is distributed as a collection of source files. The modules listed below contain their own usage instructions.

  • replete.js: Replete as a program. It takes command line arguments for basic configuration.

  • run.js: Replete as a process. This module exports a function that starts a Replete instance and binds it to the current process's stdin and stdout. Use this module if you wish to configure Replete programmatically.

  • make.js: Replete as a module. It exports a function that can be used to create multiple Replete instances. Each instance coordinates REPLs for a variety of environments.

  • browser_repl.js, node_repl.js, deno_repl.js, bun_repl.js, tjs_repl.js: Modules, each exporting a constructor for a REPL specialized to a particular environment.

  • repl.js: A module exporting the constructor for a generic REPL. This is the heart of Replete.

  • node_resolve.js: A module exporting a function that resolves an import specifier to a file in some "node_modules" directory.

  • webl/: A directory containing source code for the WEBL, used by the browser REPL. The WEBL is a standalone tool for remotely evaluating source code in the browser. See webl/README.md.

  • cmdl.js: Like the WEBL but for command-line runtimes such as Node.js.

  • package.json: A Node.js package manifest. It declares Replete's dependencies and compels Node.js to interpret these files as modules.

  • import_map.json: A Deno import map declaring Replete's dependencies. It supports Deno's ability to run Replete without installation, directly over HTTP.

Configuration

The function exported by run.js takes an options object containing any of the properties listed below. The replete.js program accepts a subset of these options as command line arguments.

Browser REPL

The browser REPL evaluates code in a browser tab. All modern browsers are supported. When multiple tabs are connected, the same code is evaluated in all tabs concurrently.

On startup, a message like

Waiting for WEBL: http://localhost:9325

is output by Replete. To connect, open the URL in a browser. A blank page with the title "WEBL" should appear.

Because the browser REPL has access to the DOM, it can be used to develop user interfaces. For example, evaluating the following code renders an interactive button on the page:

const button = document.createElement("button");
button.textContent = "Click me";
button.onclick = function () {
    alert("You clicked me.");
};
document.body.append(button);

The browser REPL is also capable of serving static files, so long as a suitable options.headers function is provided. For example, passing

function headers(locator) {
    if (locator.endsWith(".js")) {
        return {"Content-Type": "text/javascript"};
    }
    if (locator.endsWith(".jpg")) {
        return {"Content-Type": "image/jpeg"};
    }
}

as options.headers makes it possible to render a JPEG image on the page, where the image file is resolved relative to the current module:

const melon_url = import.meta.resolve("./melon.jpg");
const img = document.createElement("img");
img.src = melon_url;
document.body.append(img);

options.browser_port, --browser_port

The port number of the browser REPL. If omitted, the browser REPL will be unavailable.

options.browser_hostname, --browser_hostname

The hostname of the browser REPL. When this option is omitted, the browser REPL listens only on localhost.

A hostname of "0.0.0.0" exposes the browser REPL to the local network, making it possible to evaluate code in mobile browsers.

When exposing the browser REPL to the network, care should be taken to configure options.headers such that sensitive files are not accessible.

Node.js REPL

Node.js is a command-line runtime based on Google's V8 JavaScript engine.

options.which_node, --which_node

The path to the Node.js binary, node. If Node.js is in the PATH (see options.node_env), this can simply be "node". Not required if Node.js is hosting Replete.

options.node_args

An array of command line arguments provided to the node process running the Node.js REPL, for example ["--inspect=7227"]. Run node --help for a list of available arguments.

options.node_env

An object containing environment variables made available to the Node.js REPL. If omitted, the environment is inherited from the Replete process.

Deno REPL

Like Node.js, Deno is a command-line runtime based on V8, but it aims to behave more like a browser.

options.which_deno, --which_deno

The path to the Deno binary, deno. If Deno is in the PATH (see options.deno_env), this can simply be "deno". Not required if Deno is hosting Replete.

options.deno_args

An array of command line arguments to follow deno run, for example ["--allow-all"]. By default, this array is empty and so the Deno REPL runs with no permissions. Run deno help run for a list of available arguments.

options.deno_env

Same as options.node_env, but for the Deno REPL.

Bun REPL

Bun is a command-line runtime based on Apple's JavaScriptCore, also used by Safari. JavaScriptCore implements Proper Tail Calls, making it the only JavaScript engine to achieve ES6 compliance.

The Bun REPL restarts whenever an unhandled exception or Promise rejection occurs outside of evaluation.

options.which_bun, --which_bun

The path to the Bun binary, bun. If Bun is in the PATH (see options.bun_env), this can simply be "bun". Not required if Bun is hosting Replete.

options.bun_args

An array of command line arguments to follow bun run, for example ["--smol"]. Run bun --help for a list of available arguments.

options.bun_env

Same as options.node_env, but for the Bun REPL.

Txiki REPL

Txiki is a command-line runtime based on Fabrice Bellard's QuickJS engine. QuickJS sacrifices execution speed for reduced size and startup times.

Txiki is unable to host Replete because it does not implement a Node.js compatibility layer.

options.which_tjs, --which_tjs

The path to the Txiki binary, tjs. If Txiki is in the PATH (see options.tjs_env), this can simply be "tjs".

options.tjs_args

An array of command line arguments provided to the tjs process running the Txiki REPL, for example ["--stack-size", "100"].

options.tjs_env

Same as options.node_env, but for the Txiki REPL.

All REPLs

The remaining configuration options apply to all of the REPLs.

options.command(message)

Modifies a command message prior to evaluation, for example by transforming its source code or locator. The returned Promise resolves to the modified message, with the "source" property containing JavaScript source code.

options.command({
    source: "1 < 2 < 3",
    locator: "file:///yummy/cinnamon.coffee",
    ...
});
-> {
    source: "(1 < 2 && 2 < 3);",
    locator: "file:///yummy/cinnamon.coffee",
    ...
}

It is safe for options.command to mutate message.

options.locate(specifier, parent_locator)

Resolves a module specifier. The specifier parameter is the specifier string of a module to be located. The parent_locator parameter is the locator of the module that contains the specifier, and is optional if specifier is fully qualified. The returned Promise resolves to the locator.

A specifier is the string portion of a module's import statement, for example "../my_module.js".

A locator is a URL string containing sufficient information to locate a file. Locators that refer to a file on disk should begin with a regular file URL, but can be suffixed with arbitrary information such as a query string.

options.locate("./apple.js", "file:///yummy/orange.js");
-> "file:///yummy/apple.js"

options.locate("fs", "file:///yummy/orange.js");
-> "node:fs"

options.locate("yucky", "file:///yummy/orange.js");
-> "file:///yummy/node_modules/yucky/yucky.js"

options.locate("https://yum.my/noodles.js", "file:///yummy/orange.js");
-> "https://yum.my/noodles.js"

options.read(locator)

Reads the contents of a file on disk. The locator is a file URL. The returned Promise resolves to a Uint8Array or a string.

options.read("file:///yummy/apple.js");
-> A string containing JavaScript.

options.read("file:///yummy/cinnamon.coffee");
-> A string containing JavaScript, transpiled from CoffeeScript.

options.read("file:///yummy/bread.png");
-> A Uint8Array containing PNG image data.

options.watch(locator)

Detects when a file on disk is modified. The returned Promise resolves when the file designated by locator next changes. This does not trigger any visible action. It simply informs Replete that it should drop the file from its cache.

options.headers(locator), --content_type

Determines the response headers for a file served over HTTP. If an object is returned, the file is served with those headers. If undefined is returned, the file is not served. Note that, unlike the other options, options.headers does not return a Promise.

options.headers("file:///yummy/apple.js");
-> {"Content-Type": "text/javascript"}
options.headers("file:///yummy/cinnamon.coffee");
-> {"Content-Type": "text/javascript"}
options.headers("file:///yummy/spaghetti.jpg");
-> {"Content-Type": "image/jpeg"}
options.headers("file:///yummy/secret.key");
-> undefined

If this option is absent, only files with a ".js" extension will be served. The headers (in particular Content-Type) should be consistent with the value produced by calling options.read with locator, not necessarily the file as it exists on disk.

A simplified form of this option may be provided on the command line like --content_type=<ext>:<type>. Each appearance of --content_type maps a file extension to its Content-Type. For example, the following would configure Replete to serve .js, .css, and .html files with appropriate Content-Type headers.

--content_type=js:text/javascript
--content_type=css:text/css
--content_type="html:text/html; charset=utf-8"

options.out(string)

Called with a string representation of any arguments passed to console.log or bytes written to stdout.

options.err(string)

Called with a string representation of any exceptions that occur outside of evaluation, or of any bytes written to stderr.

options.root_locator, --root_locator

The file URL string of the "root" directory. Files inside this directory may be read and served over the network by Replete. Files outside this directory will not be accessible. Defaults to the current working directory of the Replete process if not specified.

For example, suppose options.root_locator was chosen to be

file:///home/me/code

and then Replete attempted to read the file locators

file:///etc/passwd
file:///etc/config.json
file:///home/me/tool.json
file:///home/me/code/project/bundle.json

Only the last attempt (bundle.json) could succeed, and only if options.headers recognized JSON files, which it does not do by default.

It is your responsibility to choose options.root_locator, options.headers, and options.browser_hostname such that sensitive files are not exposed.

Links

replete's People

Contributors

jamesdiacono avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

replete's Issues

VS Code Plugin

I tried several different options to set the path to replete.js but none worked. Starting Replete from VS Code throws an error that it can't find C:\C:\path\to\replete. If I try relative path it tries to find it under C:\Program Files\Microsoft VS Code\ directory. I move the directory there but it also didn't work. I tried to set the path using file:///C:/ but didn't work also.

How should I specify the absolute path to replete.js on windows? Assume it is in C:\Replete for example.

I can't wait to try this plugin, thanks for working on it James.

VSCode plugin issue/question

The README for the VSCode plugin says:

Restart VSCode. The extension must now be configured by navigating to

Settings -> User -> Extensions -> Replete

and providing the required paths. Make sure you point to an installation of
Node.js that is version 18.6.0 or higher.

What does "providing the required paths" mean? I don't see anything related to that on the extension page or the extension settings.

Screenshot 2023-03-13 at 1 20 59 PM

Screenshot 2023-03-13 at 1 21 17 PM

TypeScript

I'd like to be able to evaluate TS files without manually removing type annotations.

Implementation following nREPL protocol

I'm curious about why did you took your own approach to creating a plugabble REPL for node instead of implementing client/server for nREPL protocol? which is well tested, battle hardened, and stable protocol for this purpose

Too many open files

Hi :),

First of all, thank you for this gem.

I'm playing with REPLETE and I have found an issue with this simple code while using Openlayers library:

/*jslint browser */

import Map from "ol/Map.js";
import View from "ol/View.js";
import TileLayer from "ol/layer/Tile.js";
import XYZ from "ol/source/XYZ.js";

const map = new Map({
    layers: [
        new TileLayer({
            source: new XYZ({
                url: "https://tile.openstreetmap.org/{z}/{x}/{y}.png"
            })
        })
    ],
    view: new View({
        center: [0, 0],
        zoom: 2
    })
});

export default Object.freeze(map);

after evaluating this code I'm getting:

Waiting for WEBL: http://localhost:3000
WEBL found.
WEBL found.
WEBL found.
error: Uncaught Error: Too many open files (os error 24)
    at new FsWatcher (ext:runtime/40_fs_events.js:24:17)
    at Object.watchFs (ext:runtime/40_fs_events.js:86:10)
    at ext:deno_node/_fs/_fs_watch.ts:57:21
    at eventLoopTick (ext:core/01_core.js:203:13)

I suppose this is a file descriptor problem related to node/deno.

Thank you in advance for your help.

Text editor plugins

This is a place where people can share their plugins.

Generally, a plugin has these responsibilities:

  1. Start and stop the Replete process (replete.js).
  2. Send source code to Replete.
  3. Display Replete's results.

Here are the plugins I have made:

Text editor Written in Link Licence Updated
VSCode JavaScript Marketplace Public domain 2024-06-16
Sublime Text 4 Python sublime_replete.zip Public domain 2024-06-16
Emacs Lisp replete.el GPL v3.0 2024-06-16
Neovim Lua neovim_replete.zip MIT 2024-06-16

If you download a plugin, unzip it and follow the instructions within. These plugins should work on any operating system. Please let me know if you experience problems.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.