{"id":15888146,"url":"https://github.com/ceifa/wasmoon","last_synced_at":"2025-05-14T18:03:02.602Z","repository":{"id":37246657,"uuid":"310045541","full_name":"ceifa/wasmoon","owner":"ceifa","description":"A real lua 5.4 VM with JS bindings made with webassembly","archived":false,"fork":false,"pushed_at":"2025-02-26T23:38:16.000Z","size":1200,"stargazers_count":552,"open_issues_count":13,"forks_count":37,"subscribers_count":12,"default_branch":"main","last_synced_at":"2025-04-15T00:41:27.248Z","etag":null,"topics":["fengari","javascript","lua","nodejs","webassembly"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ceifa.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","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},"funding":{"github":["ceifa"]}},"created_at":"2020-11-04T15:40:00.000Z","updated_at":"2025-04-11T11:01:22.000Z","dependencies_parsed_at":"2023-02-10T06:05:21.596Z","dependency_job_id":"14ae5bfd-8f52-457d-9873-7808c8c36d50","html_url":"https://github.com/ceifa/wasmoon","commit_stats":{"total_commits":139,"total_committers":3,"mean_commits":"46.333333333333336","dds":"0.17985611510791366","last_synced_commit":"b8d08d8c79f60cf1031c8a8f697b644a167c8c96"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ceifa%2Fwasmoon","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ceifa%2Fwasmoon/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ceifa%2Fwasmoon/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ceifa%2Fwasmoon/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ceifa","download_url":"https://codeload.github.com/ceifa/wasmoon/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254198452,"owners_count":22030964,"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":["fengari","javascript","lua","nodejs","webassembly"],"created_at":"2024-10-06T06:06:29.400Z","updated_at":"2025-05-14T18:02:57.586Z","avatar_url":"https://github.com/ceifa.png","language":"TypeScript","funding_links":["https://github.com/sponsors/ceifa"],"categories":["Languages","TypeScript"],"sub_categories":["Lua"],"readme":"[![Build Status](https://github.com/ceifa/wasmoon/actions/workflows/publish.yml/badge.svg)](https://github.com/ceifa/wasmoon/actions/workflows/publish.yml)\n[![npm](https://img.shields.io/npm/v/wasmoon.svg)](https://npmjs.com/package/wasmoon)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\n# Wasmoon\n\nThis package aims to provide a way to:\n\n- Embed Lua to any Node.js, Deno or Web Application.\n- Run lua code in any operational system\n- Interop Lua and JS without memory leaks (including the DOM)\n\n## API Usage\n\nTo initialize, create a new Lua state, register the standard library, set a global variable, execute a code and get a global variable:\n\n```js\nconst { LuaFactory } = require('wasmoon')\n\n// Initialize a new lua environment factory\n// You can pass the wasm location as the first argument, useful if you are using wasmoon on a web environment and want to host the file by yourself\nconst factory = new LuaFactory()\n// Create a standalone lua environment from the factory\nconst lua = await factory.createEngine()\n\ntry {\n    // Set a JS function to be a global lua function\n    lua.global.set('sum', (x, y) =\u003e x + y)\n    // Run a lua string\n    await lua.doString(`\n    print(sum(10, 10))\n    function multiply(x, y)\n        return x * y\n    end\n    `)\n    // Get a global lua function as a JS function\n    const multiply = lua.global.get('multiply')\n    console.log(multiply(10, 10))\n} finally {\n    // Close the lua environment, so it can be freed\n    lua.global.close()\n}\n```\n\n## CLI Usage\n\nAlthough Wasmoon has been designed to be embedded, you can run it on command line as well, but, if you want something more robust on this, we recommend to take a look at [demoon](https://github.com/ceifa/demoon).\n\n```sh\n$: wasmoon [options] [file] [args]\n```\n\nAvailable options are:\n\n- `-l`: Include a file or directory\n- `-i`: Enter interactive mode after running the files\n\n### Example:\n\n```sh\n$: wasmoon -i sum.lua 10 30\n```\n\nAnd if you are in Unix, you can also use it as a script interpreter with [Shebang](\u003chttps://en.wikipedia.org/wiki/Shebang_(Unix)\u003e):\n\n```lua\n#!/usr/bin/env wasmoon\nreturn arg[1] + arg[2]\n```\n\n```sh\n$: ./sum.lua 10 30\n```\n\n## When to use wasmoon and fengari\n\nWasmoon compiles the [official Lua code](https://github.com/lua/lua) to webassembly and creates an abstraction layer to interop between Lua and JS, instead of [fengari](https://github.com/fengari-lua/fengari), that is an entire Lua VM rewritten in JS.\n\n### Performance\n\nBecause of wasm, wasmoon will run Lua code much faster than fengari, but if you are going to interop a lot between JS and Lua, this may be not be true anymore, you probably should test on you specific use case to take the prove.\n\nThis is the results running a [heap sort code](https://github.com/ceifa/wasmoon/blob/main/bench/heapsort.lua) in a list of 2k numbers 10x(less is better):\n\n| wasmoon  | fengari   |\n| -------- | --------- |\n| 15.267ms | 389.923ms |\n\n### Size\n\nFengari is smaller than wasmoon, which can improve the user experience if in web environments:\n\n|             | wasmoon | fengari |\n| ----------- | ------- | ------- |\n| **plain**   | 393kB   | 214kB   |\n| **gzipped** | 130kB   | 69kB    |\n\n## Fixing common errors on web environment\n\nBundle/require errors can happen because wasmoon tries to safely import some node modules even in a browser environment, the bundler is not prepared to that since it tries to statically resolve everything on build time.\nPolyfilling these modules is not the right solution because they are not actually being used, you just have to ignore them:\n\n### Webpack\n\nAdd the `resolve.fallback` snippet to your config:\n\n```js\nmodule.exports = {\n    entry: './src/index.js', // Here is your entry file\n    resolve: {\n        fallback: {\n            path: false,\n            fs: false,\n            child_process: false,\n            crypto: false,\n            url: false,\n            module: false,\n        },\n    },\n}\n```\n\n### Rollup\n\nWith the package [rollup-plugin-ignore](https://www.npmjs.com/package/rollup-plugin-ignore), add this snippet to your config:\n\n```js\nexport default {\n    input: 'src/index.js', // Here is your entry file,\n    plugins: [ignore(['path', 'fs', 'child_process', 'crypto', 'url', 'module'])],\n}\n```\n\n### Angular\n\nAdd the section browser on `package.json`:\n\n```json\n{\n    \"main\": \"src/index.js\",\n    \"browser\": {\n        \"child_process\": false,\n        \"fs\": false,\n        \"path\": false,\n        \"crypto\": false,\n        \"url\": false,\n        \"module\": false\n    }\n}\n```\n\n## How to build\n\nFirstly download the lua submodule and install the other Node.JS dependencies:\n\n```sh\ngit submodule update --init # download lua submodule\nnpm i # install dependencies\n```\n\n### Windows / Linux / MacOS (Docker way)\n\nYou need to install [docker](https://www.docker.com/) and ensure it is on your `PATH`.\n\nAfter cloned the repo, to build you just have to run these:\n\n```sh\nnpm run build:wasm:docker:dev # build lua\nnpm run build # build the js code/bridge\nnpm test # ensure everything it's working fine\n```\n\n### Ubuntu / Debian / MacOS\n\nYou need to install [emscripten](https://emscripten.org/) and ensure it is on your `PATH`.\n\nAfter cloned the repo, to build you just have to run these:\n\n```sh\nnpm run build:wasm:dev # build lua\nnpm run build # build the js code/bridge\nnpm test # ensure everything it's working fine\n```\n\n## Edge Cases\n\n### Null\n\n`null` is injected as userdata type if `injectObjects` is set to `true`. This works as expected except that it will evaluate to `true` in Lua.\n\n### Promises\n\nPromises can be await'd from Lua with some caveats detailed in the below section. To await a Promise call `:await()` on it which will yield the Lua execution until the promise completes.\n\n```js\nconst { LuaFactory } = require('wasmoon')\nconst factory = new LuaFactory()\nconst lua = await factory.createEngine()\n\ntry {\n    lua.global.set('sleep', (length) =\u003e new Promise((resolve) =\u003e setTimeout(resolve, length)))\n    await lua.doString(`\n        sleep(1000):await()\n    `)\n} finally {\n    lua.global.close()\n}\n```\n\n### Async/Await\n\nIt's not possible to await in a callback from JS into Lua. This is a limitation of Lua but there are some workarounds. It can also be encountered when yielding at the top-level of a file. An example where you might encounter this is a snippet like this:\n\n```js\nlocal res = sleep(1):next(function ()\n    sleep(10):await()\n    return 15\nend)\nprint(\"res\", res:await())\n```\n\nWhich will throw an error like this:\n\n```\nError: Lua Error(ErrorRun/2): cannot resume dead coroutine\n    at Thread.assertOk (/home/tstableford/projects/wasmoon/dist/index.js:409:23)\n    at Thread.\u003canonymous\u003e (/home/tstableford/projects/wasmoon/dist/index.js:142:22)\n    at Generator.throw (\u003canonymous\u003e)\n    at rejected (/home/tstableford/projects/wasmoon/dist/index.js:26:69)\n```\n\nOr like this:\n\n```\nattempt to yield across a C-call boundary\n```\n\nYou can workaround this by doing something like below:\n\n```lua\nfunction async(callback)\n    return function(...)\n        local co = coroutine.create(callback)\n        local safe, result = coroutine.resume(co, ...)\n\n        return Promise.create(function(resolve, reject)\n            local function step()\n                if coroutine.status(co) == \"dead\" then\n                    local send = safe and resolve or reject\n                    return send(result)\n                end\n\n                safe, result = coroutine.resume(co)\n\n                if safe and result == Promise.resolve(result) then\n                    result:finally(step)\n                else\n                    step()\n                end\n            end\n\n            result:finally(step)\n        end)\n    end\nend\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fceifa%2Fwasmoon","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fceifa%2Fwasmoon","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fceifa%2Fwasmoon/lists"}