Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: CI

on:
push:
branches: [master, main]
pull_request:
workflow_dispatch:

jobs:
build:
name: Node ${{ matrix.node }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
# macos-latest is arm64, which is exactly the configuration that could not
# build before Lua was vendored.
os: [ubuntu-latest, macos-latest, windows-latest]
node: [20, 22, 24]

steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}

- name: Install dependencies
run: npm install --ignore-scripts

# Build with an explicitly pinned node-gyp rather than the copy npm bundles.
# npm on Node 20 and 22 bundles node-gyp 11.x, whose Visual Studio probe
# overflows the child-process stdio buffer on the windows-latest image (which
# now ships VS 18) and then reports 'unknown version "undefined"'. There is no
# working override for the bundled copy -- modern npm ignores
# $npm_config_node_gyp -- so invoke node-gyp ourselves. node-gyp 12 handles
# VS 18 and supports every Node version in this matrix.
- name: Build the addon
run: npx --yes node-gyp@12 rebuild

- name: Test
run: npm test

- name: Check the published tarball contents
run: npm pack --dry-run
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ build/
node_modules/
npm-debug.log
yarn-error.log
*.tgz
2 changes: 0 additions & 2 deletions .npmignore

This file was deleted.

35 changes: 35 additions & 0 deletions LICENSE.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,38 @@ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

---

## Bundled third-party software

This package compiles the following third-party sources into the addon. Both are
distributed under the MIT license; their full notices ship alongside the code.

### Lua 5.1.5 — `vendor/lua/` (see `vendor/lua/COPYRIGHT`)

Copyright (C) 1994-2012 Lua.org, PUC-Rio.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

### LuaFileSystem 1.8.0 — `vendor/lfs/` (see `vendor/lfs/LICENSE`)

Copyright (c) 2003-2020 Kepler Project.

Distributed under the MIT license, on the same terms as above.
214 changes: 177 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,60 +1,108 @@
# node-lua-runner

> **Maintenance status:** This project is no longer actively maintained.
> It remains available as a standalone continuation of `medaeus245/node-lua` for users who need LuaJIT support with newer Node.js versions.
Embed **Lua 5.1** in your Node.js programs.

This project started as a fork of `medaeus245/node-lua`, which appeared to be unmaintained. I updated it to compile with newer Node.js versions and tested it with Windows 10 and Node.js 18+.
Lua and [LuaFileSystem](https://github.com/lunarmodules/luafilesystem) are compiled directly into
the addon, so there is no system Lua to install and nothing to configure — `npm install` builds
everything from source on Linux, macOS and Windows, on both x64 and ARM64.

---
## Installation

This Package allows you to use LUA inside Node.js.
See [`examples`](#examples).
```
npm install node-lua-runner
```

I refactored the code so that it will compile with newer Node.JS versions.
I tested it with Windows 10 and Node.JS 18+.
Other Operating Systems or Combinations are not tested yet.
The addon is compiled at install time, so you need a working C/C++ toolchain:

## Installation
- **Linux** — `build-essential` (or your distribution's equivalent) and Python 3
- **macOS** — the Xcode Command Line Tools (`xcode-select --install`)
- **Windows** — the "Desktop development with C++" workload from Visual Studio Build Tools

`npm install node-lua-runner`
Nothing else. Earlier versions needed you to build and install LuaJIT yourself on Linux; that is
no longer the case.

## 1.1.0 2023-04-21
> **Windows with Visual Studio 2026:** `node-gyp` 11.x cannot detect that version and fails with
> `find VS unknown version "undefined"`. It is what npm bundles on Node.js 20 and 22. Install
> node-gyp 12 and point npm at it — note the explicit `@12`, since `@latest` still resolves to
> 11.x on those Node versions:
>
> ```
> npm install -g node-gyp@12
> set npm_config_node_gyp=%APPDATA%\npm\node_modules\node-gyp\bin\node-gyp.js
> npm install node-lua-runner
> ```

- Added LuaFileSystem for Windows (x64) compiled against luajit. See Examples for usage.
## Quick start

## About
```javascript
const nodelua = require('node-lua-runner');

const lua = new nodelua.LuaState();

Using Lua5.1 C interface: https://www.lua.org/manual/5.1/manual.html with luajit compiler
lua.DoString('print("Hello from Lua!")');

**Based on:**
- nodelua ( https://github.com/brettlangdon/NodeLua )
- node-luajit ( https://github.com/whtiehack/node-luajit )
- LuaFileSystem ( https://github.com/lunarmodules/luafilesystem )
// Expose a JavaScript function to Lua
lua.RegisterFunction('add', function () {
const a = lua.ToValue(1);
const b = lua.ToValue(2);
lua.Pop(2);
lua.Push(a + b);
return 1;
});

**Features:**
- Low-Level API
- For now Sync only
lua.DoString('print("2 + 3 = " .. add(2, 3))');

lua.Close();
```

## Breaking changes in 2.0.0

2.0.0 is a maintenance release that makes the package build and run on current Node.js. It fixes
several long-standing bugs, and those fixes change behaviour:

**Compilation:**
- On Windows and Mac : Luajit library already included in the package
- On Linux: Compilation with following parameters:
- Include directory: (find /usr/include /usr/local/include $NODELUA_INCLUDE -name lua.h | sed s/lua.h//)
- Library directory: "/usr/local/lib"
- Library: "/usr/local/lib/libluajit-5.1.so"
| Change | Before | Now |
|---|---|---|
| `SetField(index, key, value)` | Wrote the *key* into the field, ignoring the value | Writes the value, and resolves a relative `index` before pushing |
| `LoadFile` / `LoadString` | Executed the chunk, identical to `DoFile` / `DoString` | Compile and push the chunk without running it; call `Call(0, 0)` to run it |
| Lua booleans read into JS | Arrived as the numbers `1` and `0` | Arrive as `true` and `false` |
| `Push(3.5)` | Truncated to `3` | Keeps the fractional value |
| `AddPackagePath` | Appended without a separator, corrupting `package.path`, so `require` usually failed | Appends correctly, and no longer breaks on paths containing quotes |
| `ToValue` on a table | Only converted correctly when the table was at the top of the stack | Works at any stack index, including nested tables |
| Calling a method after `Close()` | Use-after-free | Throws |
| `Resume(args)` | Returned `undefined` | Returns the Lua status code |

NOTE (Linux only): Don't forget to set your LD_LIBRARY_PATH to /usr/local/lib so that node-lua can find luajit.
Other things worth knowing if you are upgrading:

- **LuaJIT has been replaced by stock Lua 5.1.5.** Windows builds used to link LuaJIT 2.0.3, so
Windows users lose JIT compilation. In exchange, macOS on Apple Silicon and Linux on ARM64 work
at all, which they previously did not. The Lua C API and language are unchanged.
- **`require('lfs')` now works on every platform.** It used to be a Windows-only prebuilt DLL
loaded through an `LUA_CPATH` hack; LuaFileSystem is now compiled into the addon.
- Node.js 18 or newer is required.

## About

Built on the [Lua 5.1 C API](https://www.lua.org/manual/5.1/manual.html). The binding uses
[Node-API](https://nodejs.org/api/n-api.html), which is ABI-stable — a compiled build keeps
working across future Node.js releases instead of breaking on each major version.

**Based on:**
- nodelua ( https://github.com/brettlangdon/NodeLua )
- node-luajit ( https://github.com/whtiehack/node-luajit )
- LuaFileSystem ( https://github.com/lunarmodules/luafilesystem )

**Features:**
- Low-level API mapping closely onto the Lua C API
- Synchronous only

## Examples

- [Simple](https://github.com/0x7878/node-lua-runner/blob/master/examples/simple/index.js)
- [Using lua require function](https://github.com/0x7878/node-lua-runner/blob/master/examples/lua_require/index.js)
- [using lua file system](https://github.com/0x7878/node-lua-runner/tree/master/examples/lua_lfs)
- [Simple](https://github.com/mschmicking/node-lua-runner/blob/master/examples/simple/index.js)
- [Using the lua require function](https://github.com/mschmicking/node-lua-runner/blob/master/examples/lua_require/index.js)
- [Using LuaFileSystem](https://github.com/mschmicking/node-lua-runner/tree/master/examples/lua_lfs)

## API


```javascript

const nodelua = require('node-lua-runner');
Expand Down Expand Up @@ -86,7 +134,27 @@ lua.DoString("print('Hello world!')");


/**
* [Sets the function f as the new value of global name]
* [Compiles the given file and pushes it onto the stack WITHOUT running it.
* Use Call to run it.]
* @type {String} file
* @throws {Exception}
*/
lua.LoadFile(__dirname + "/test.lua");


/**
* [Compiles the given string and pushes it onto the stack WITHOUT running it.
* Use Call to run it.]
* @type {String} str
* @throws {Exception}
*/
lua.LoadString("print('Hello world!')");


/**
* [Sets the function f as the new value of global name.
* Arguments are read off the Lua stack with ToValue; the return value is the
* number of results the function pushed.]
* @param {String} name [name of the global in lua]
* @param {Function} f [function to set]
*/
Expand Down Expand Up @@ -114,19 +182,24 @@ lua.GetGlobal('myVar');


/**
* [Does the equivalent to t[k] = v, where t is the value at the given valid index and v is the value at the top of the stack. This function pops the value from the stack.]
* [Does the equivalent to t[k] = v, where t is the value at the given valid index.
* Unlike the raw C API, v is passed as an argument rather than taken from the
* top of the stack.]
* @type {Number} index
* @type {String} key
* @type {*} value
* @throws {Exception} [if the value at index is not a table]
*/
lua.SetField(index, "t");
lua.SetField(index, "key", value);


/**
* [Pushes onto the stack the value t[key], where t is the value at the given valid index.]
* @type {Number} index
* @type {String} key
* @throws {Exception} [if the value at index is not a table]
*/
lua.GetField(index, "t");
lua.GetField(index, "key");


/**
Expand Down Expand Up @@ -156,6 +229,7 @@ lua.Yield(args);
/**
* [Starts and resumes a coroutine in a given thread.]
* @type {Number} args
* @return {Number} [status code]
*/
lua.Resume(args);

Expand Down Expand Up @@ -196,4 +270,70 @@ lua.SetTop(index);
lua.Replace(index);


/**
* [Returns the status of the state]
* @return {Number} [compare against nodelua.STATUS.*]
*/
lua.Status();


/**
* [Controls the Lua garbage collector]
* @type {Number} what [one of nodelua.GC.*]
* @return {Number}
*/
lua.CollectGarbage(nodelua.GC.COLLECT);


/**
* [Destroys the Lua state and frees its memory. Safe to call more than once.
* Any further use of the state throws.]
*/
lua.Close();

```

### Constants

```javascript
nodelua.INFO.VERSION // "Lua 5.1"
nodelua.INFO.VERSION_NUM // 501
nodelua.INFO.COPYRIGHT
nodelua.INFO.AUTHORS

nodelua.LUA.GLOBALSINDEX // pseudo-index of the globals table
nodelua.LUA.REGISTRYINDEX // pseudo-index of the registry

nodelua.STATUS.YIELD
nodelua.STATUS.ERRRUN
nodelua.STATUS.ERRSYNTAX
nodelua.STATUS.ERRMEM
nodelua.STATUS.ERRERR

nodelua.GC.STOP
nodelua.GC.RESTART
nodelua.GC.COLLECT
nodelua.GC.COUNT
nodelua.GC.COUNTB
nodelua.GC.STEP
nodelua.GC.SETPAUSE
nodelua.GC.SETSTEPMUL
```

## Caveats

This is a thin wrapper over the Lua C API, and it does not shield you from every way of misusing
that API. Some stack operations on values of an unexpected type raise an *unprotected* Lua error,
which aborts the process rather than throwing a JavaScript exception. `SetField` and `GetField`
guard against this explicitly; other methods do not. Keep track of what is on the stack.

## Development

```
npm install
npm test
```

## License

ISC — see [LICENSE.md](LICENSE.md), which also covers the vendored Lua and LuaFileSystem sources.
Loading
Loading