{"id":21739450,"url":"https://github.com/daviddesimone/ng-js","last_synced_at":"2025-07-01T12:04:43.230Z","repository":{"id":83864482,"uuid":"535300371","full_name":"DavidDeSimone/ng-js","owner":"DavidDeSimone","description":null,"archived":false,"fork":false,"pushed_at":"2022-09-21T13:25:08.000Z","size":18,"stargazers_count":8,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-06-09T13:02:11.734Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/DavidDeSimone.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2022-09-11T13:02:55.000Z","updated_at":"2025-05-03T08:16:43.000Z","dependencies_parsed_at":null,"dependency_job_id":"1bd52f2d-9b48-4e4f-b1bc-542a59675821","html_url":"https://github.com/DavidDeSimone/ng-js","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/DavidDeSimone/ng-js","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DavidDeSimone%2Fng-js","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DavidDeSimone%2Fng-js/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DavidDeSimone%2Fng-js/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DavidDeSimone%2Fng-js/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DavidDeSimone","download_url":"https://codeload.github.com/DavidDeSimone/ng-js/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DavidDeSimone%2Fng-js/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":262959560,"owners_count":23391057,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-26T06:09:06.539Z","updated_at":"2025-07-01T12:04:43.221Z","avatar_url":"https://github.com/DavidDeSimone.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# emacs ng-js\n## Run JavaScript and TypeScript in Vanilla emacs\n\nThis project is an emacs dynamic module that will allow you to run JavaScript and TypeScript in any emacs module that supports dynamic modules.\n\nThis module is still highly under development and currently experimental. PRs are welcome. The goal will be to eventually list on MELPA and be installable via `cargo install`, however those are still WIP.\n\n## Build\n\nYou will need stable [rust](https://www.rust-lang.org/) to install this project. \n\nIn the top directory, run:\n\n`cargo build --release`\n\nThis will output our dynamic module in `./target/release/libng_js[EXT]` where EXT is your system's dynamic module extension. For most, this will be a *.so \n\nYou can run the following command when launching emacs to load the module:\n\n`emacs -l target/release/libng_js.so -l src/lisp/ng-js-mod.el`\n\nWithin the editor, run\n\n`(require 'ng-js-mod)`\n\n## Usage\n\nWithin elisp, you can run:\n\n```lisp\n(ng-js-eval \"let x = 2 + 3;\")\n```\n\nThis will synchronously evaluate your JavaScript or TypeScript and return the result. \n\nYou can call lisp functions from JavaScript using the special \"lisp\" object. You can do this via\n\n```js\nlisp.bufferMenu();\n```\n\nIn JavaScript, camelCase names will automatically be changed to their kebab case invocations. If you want to execute a kebab case function without that logic, the following works:\n\n```js\nlisp['buffer-menu']();\n```\n\nNOTE: The JavaScript/TypeScript runtime is running on a separate thread from the Lisp runtime. It is running in parallel. This means that invocations of lisp functions are done ASYNCHRONOUSLY. The lisp function returns a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) that you can chain execution off of. An example would be:\n\n```js\nlisp.bufferMenu()\n.then(async () =\u003e {\n    await lisp.getBufferCreate(\"newBuffer\");\n    await lisp.switchToBuffer(\"newBuffer\");\n    await lisp.setBuffer(\"newBuffer\");\n});\n```\n\nThis can be combined with JS async/await. \n\nUnder the hood, the JavaScript/TypeScript engine is still [deno](deno.land). You can run deno modules. A simple example is using sqlite in emacs:\n\n```js\n// Credit to https://deno.land/x/sqlite@v3.5.0 for docs\nimport { DB } from \"https://deno.land/x/sqlite/mod.ts\";\n\nconst db = new DB(\"test.db\");\ndb.execute(`\n  CREATE TABLE IF NOT EXISTS people (\n    id INTEGER PRIMARY KEY AUTOINCREMENT,\n    name TEXT\n  )\n`);\n\nconst names = [\"Peter Parker\", \"Clark Kent\", \"Bruce Wayne\"];\n\nfor (const name of names) {\n  db.query(\"INSERT INTO people (name) VALUES (?)\", [name]);\n}\n\n\nlisp.getOrCreateBuffer(\"sqldb\")\n```\n\n## Technology and Performance\n\nUnder the hood, this is implemented by running a custom instance of deno in process on another thread. Messages from lisp -\u003e js and js -\u003e lisp are passed via async channels. The primary means of of communicating from js to lisp hooks into lisp filewatcher API. JS updated a sentinel file to let lisp know that there are queue'd lisp commands to run. Once that sentinel fires, lisp will drain the queue of commands. \n\nSince JavaScript runs on another thread, it should generally not impact emacs execution. There is also a degree of isolation in the event there is a logic error within JavaScript - it shouldn't prevent lisp from being executed further.\n\nThis allows for some interesting possibilities for performance. The following is a basic test - it is NOT a formal benchmark, nor is it ANY statement on the performance of this module, or elisp vs. js. This is simply some code you can run on your machine, and a very simple observation of speed possibility of some calculations:\n\n```lisp\n(defun fibonacci(n)\n  (if (\u003c= n 1)\n      n\n    (+ (fibonacci (- n 1)) (fibonacci (- n 2)))))\n\n(let ((time (current-time)))\n  (fibonacci 35)\n  (message \"%.06f\" (float-time (time-since time))))\n\n  (let ((time (current-time)))\n  (ng-js-eval \"const fib = (n) =\u003e { if (n \u003c= 1) { return n; } return fib(n - 1) + fib(n - 2); }; fib(35);\")\n  (message \"%.06f\" (float-time (time-since time))))\n```\n\nOn my machine, this produces:\n\n```bash\nTesting...\n3.487964\n0.086469\n```\n\n\n## Motivation\n\nThis project is based off the work contained in the [emacs-ng](https://github.com/emacs-ng/emacs-ng) fork of emacs. That project included [deno](https://github.com/denoland/deno) into the internals of the emacs editor. \n\nThis projects includes deno as well, in a slightly different way (see [Technology and Performance](#technology-and-performance))\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdaviddesimone%2Fng-js","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdaviddesimone%2Fng-js","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdaviddesimone%2Fng-js/lists"}