Skip to content

Commit 96cc357

Browse files
authored
Merge pull request #1 from mschmicking/revive-2.0
Revive for modern Node.js: Node-API, vendored Lua, tests, CI
2 parents d5498fc + 4dc9ede commit 96cc357

84 files changed

Lines changed: 17571 additions & 1563 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [master, main]
6+
pull_request:
7+
workflow_dispatch:
8+
9+
jobs:
10+
build:
11+
name: Node ${{ matrix.node }} on ${{ matrix.os }}
12+
runs-on: ${{ matrix.os }}
13+
strategy:
14+
fail-fast: false
15+
matrix:
16+
# macos-latest is arm64, which is exactly the configuration that could not
17+
# build before Lua was vendored.
18+
os: [ubuntu-latest, macos-latest, windows-latest]
19+
node: [20, 22, 24]
20+
21+
steps:
22+
- uses: actions/checkout@v4
23+
24+
- uses: actions/setup-node@v4
25+
with:
26+
node-version: ${{ matrix.node }}
27+
28+
- name: Install dependencies
29+
run: npm install --ignore-scripts
30+
31+
# Build with an explicitly pinned node-gyp rather than the copy npm bundles.
32+
# npm on Node 20 and 22 bundles node-gyp 11.x, whose Visual Studio probe
33+
# overflows the child-process stdio buffer on the windows-latest image (which
34+
# now ships VS 18) and then reports 'unknown version "undefined"'. There is no
35+
# working override for the bundled copy -- modern npm ignores
36+
# $npm_config_node_gyp -- so invoke node-gyp ourselves. node-gyp 12 handles
37+
# VS 18 and supports every Node version in this matrix.
38+
- name: Build the addon
39+
run: npx --yes node-gyp@12 rebuild
40+
41+
- name: Test
42+
run: npm test
43+
44+
- name: Check the published tarball contents
45+
run: npm pack --dry-run

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ build/
22
node_modules/
33
npm-debug.log
44
yarn-error.log
5+
*.tgz

.npmignore

Lines changed: 0 additions & 2 deletions
This file was deleted.

LICENSE.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,38 @@ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1414
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1515
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1616
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17+
18+
---
19+
20+
## Bundled third-party software
21+
22+
This package compiles the following third-party sources into the addon. Both are
23+
distributed under the MIT license; their full notices ship alongside the code.
24+
25+
### Lua 5.1.5 — `vendor/lua/` (see `vendor/lua/COPYRIGHT`)
26+
27+
Copyright (C) 1994-2012 Lua.org, PUC-Rio.
28+
29+
Permission is hereby granted, free of charge, to any person obtaining a copy
30+
of this software and associated documentation files (the "Software"), to deal
31+
in the Software without restriction, including without limitation the rights
32+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
33+
copies of the Software, and to permit persons to whom the Software is
34+
furnished to do so, subject to the following conditions:
35+
36+
The above copyright notice and this permission notice shall be included in
37+
all copies or substantial portions of the Software.
38+
39+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
40+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
41+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
42+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
43+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
44+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
45+
THE SOFTWARE.
46+
47+
### LuaFileSystem 1.8.0 — `vendor/lfs/` (see `vendor/lfs/LICENSE`)
48+
49+
Copyright (c) 2003-2020 Kepler Project.
50+
51+
Distributed under the MIT license, on the same terms as above.

README.md

Lines changed: 177 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,108 @@
11
# node-lua-runner
22

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

6-
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+.
5+
Lua and [LuaFileSystem](https://github.com/lunarmodules/luafilesystem) are compiled directly into
6+
the addon, so there is no system Lua to install and nothing to configure — `npm install` builds
7+
everything from source on Linux, macOS and Windows, on both x64 and ARM64.
78

8-
---
9+
## Installation
910

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

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

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

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

21-
## 1.1.0 2023-04-21
24+
> **Windows with Visual Studio 2026:** `node-gyp` 11.x cannot detect that version and fails with
25+
> `find VS unknown version "undefined"`. It is what npm bundles on Node.js 20 and 22. Install
26+
> node-gyp 12 and point npm at it — note the explicit `@12`, since `@latest` still resolves to
27+
> 11.x on those Node versions:
28+
>
29+
> ```
30+
> npm install -g node-gyp@12
31+
> set npm_config_node_gyp=%APPDATA%\npm\node_modules\node-gyp\bin\node-gyp.js
32+
> npm install node-lua-runner
33+
> ```
2234
23-
- Added LuaFileSystem for Windows (x64) compiled against luajit. See Examples for usage.
35+
## Quick start
2436
25-
## About
37+
```javascript
38+
const nodelua = require('node-lua-runner');
2639
40+
const lua = new nodelua.LuaState();
2741
28-
Using Lua5.1 C interface: https://www.lua.org/manual/5.1/manual.html with luajit compiler
42+
lua.DoString('print("Hello from Lua!")');
2943
30-
**Based on:**
31-
- nodelua ( https://github.com/brettlangdon/NodeLua )
32-
- node-luajit ( https://github.com/whtiehack/node-luajit )
33-
- LuaFileSystem ( https://github.com/lunarmodules/luafilesystem )
44+
// Expose a JavaScript function to Lua
45+
lua.RegisterFunction('add', function () {
46+
const a = lua.ToValue(1);
47+
const b = lua.ToValue(2);
48+
lua.Pop(2);
49+
lua.Push(a + b);
50+
return 1;
51+
});
3452
35-
**Features:**
36-
- Low-Level API
37-
- For now Sync only
53+
lua.DoString('print("2 + 3 = " .. add(2, 3))');
54+
55+
lua.Close();
56+
```
57+
58+
## Breaking changes in 2.0.0
3859

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

40-
**Compilation:**
41-
- On Windows and Mac : Luajit library already included in the package
42-
- On Linux: Compilation with following parameters:
43-
- Include directory: (find /usr/include /usr/local/include $NODELUA_INCLUDE -name lua.h | sed s/lua.h//)
44-
- Library directory: "/usr/local/lib"
45-
- Library: "/usr/local/lib/libluajit-5.1.so"
63+
| Change | Before | Now |
64+
|---|---|---|
65+
| `SetField(index, key, value)` | Wrote the *key* into the field, ignoring the value | Writes the value, and resolves a relative `index` before pushing |
66+
| `LoadFile` / `LoadString` | Executed the chunk, identical to `DoFile` / `DoString` | Compile and push the chunk without running it; call `Call(0, 0)` to run it |
67+
| Lua booleans read into JS | Arrived as the numbers `1` and `0` | Arrive as `true` and `false` |
68+
| `Push(3.5)` | Truncated to `3` | Keeps the fractional value |
69+
| `AddPackagePath` | Appended without a separator, corrupting `package.path`, so `require` usually failed | Appends correctly, and no longer breaks on paths containing quotes |
70+
| `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 |
71+
| Calling a method after `Close()` | Use-after-free | Throws |
72+
| `Resume(args)` | Returned `undefined` | Returns the Lua status code |
4673

47-
NOTE (Linux only): Don't forget to set your LD_LIBRARY_PATH to /usr/local/lib so that node-lua can find luajit.
74+
Other things worth knowing if you are upgrading:
75+
76+
- **LuaJIT has been replaced by stock Lua 5.1.5.** Windows builds used to link LuaJIT 2.0.3, so
77+
Windows users lose JIT compilation. In exchange, macOS on Apple Silicon and Linux on ARM64 work
78+
at all, which they previously did not. The Lua C API and language are unchanged.
79+
- **`require('lfs')` now works on every platform.** It used to be a Windows-only prebuilt DLL
80+
loaded through an `LUA_CPATH` hack; LuaFileSystem is now compiled into the addon.
81+
- Node.js 18 or newer is required.
82+
83+
## About
84+
85+
Built on the [Lua 5.1 C API](https://www.lua.org/manual/5.1/manual.html). The binding uses
86+
[Node-API](https://nodejs.org/api/n-api.html), which is ABI-stable — a compiled build keeps
87+
working across future Node.js releases instead of breaking on each major version.
88+
89+
**Based on:**
90+
- nodelua ( https://github.com/brettlangdon/NodeLua )
91+
- node-luajit ( https://github.com/whtiehack/node-luajit )
92+
- LuaFileSystem ( https://github.com/lunarmodules/luafilesystem )
93+
94+
**Features:**
95+
- Low-level API mapping closely onto the Lua C API
96+
- Synchronous only
4897

4998
## Examples
5099

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

55104
## API
56105

57-
58106
```javascript
59107

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

87135

88136
/**
89-
* [Sets the function f as the new value of global name]
137+
* [Compiles the given file and pushes it onto the stack WITHOUT running it.
138+
* Use Call to run it.]
139+
* @type {String} file
140+
* @throws {Exception}
141+
*/
142+
lua.LoadFile(__dirname + "/test.lua");
143+
144+
145+
/**
146+
* [Compiles the given string and pushes it onto the stack WITHOUT running it.
147+
* Use Call to run it.]
148+
* @type {String} str
149+
* @throws {Exception}
150+
*/
151+
lua.LoadString("print('Hello world!')");
152+
153+
154+
/**
155+
* [Sets the function f as the new value of global name.
156+
* Arguments are read off the Lua stack with ToValue; the return value is the
157+
* number of results the function pushed.]
90158
* @param {String} name [name of the global in lua]
91159
* @param {Function} f [function to set]
92160
*/
@@ -114,19 +182,24 @@ lua.GetGlobal('myVar');
114182

115183

116184
/**
117-
* [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.]
185+
* [Does the equivalent to t[k] = v, where t is the value at the given valid index.
186+
* Unlike the raw C API, v is passed as an argument rather than taken from the
187+
* top of the stack.]
118188
* @type {Number} index
119189
* @type {String} key
190+
* @type {*} value
191+
* @throws {Exception} [if the value at index is not a table]
120192
*/
121-
lua.SetField(index, "t");
193+
lua.SetField(index, "key", value);
122194

123195

124196
/**
125197
* [Pushes onto the stack the value t[key], where t is the value at the given valid index.]
126198
* @type {Number} index
127199
* @type {String} key
200+
* @throws {Exception} [if the value at index is not a table]
128201
*/
129-
lua.GetField(index, "t");
202+
lua.GetField(index, "key");
130203

131204

132205
/**
@@ -156,6 +229,7 @@ lua.Yield(args);
156229
/**
157230
* [Starts and resumes a coroutine in a given thread.]
158231
* @type {Number} args
232+
* @return {Number} [status code]
159233
*/
160234
lua.Resume(args);
161235

@@ -196,4 +270,70 @@ lua.SetTop(index);
196270
lua.Replace(index);
197271

198272

273+
/**
274+
* [Returns the status of the state]
275+
* @return {Number} [compare against nodelua.STATUS.*]
276+
*/
277+
lua.Status();
278+
279+
280+
/**
281+
* [Controls the Lua garbage collector]
282+
* @type {Number} what [one of nodelua.GC.*]
283+
* @return {Number}
284+
*/
285+
lua.CollectGarbage(nodelua.GC.COLLECT);
286+
287+
288+
/**
289+
* [Destroys the Lua state and frees its memory. Safe to call more than once.
290+
* Any further use of the state throws.]
291+
*/
292+
lua.Close();
293+
294+
```
295+
296+
### Constants
297+
298+
```javascript
299+
nodelua.INFO.VERSION // "Lua 5.1"
300+
nodelua.INFO.VERSION_NUM // 501
301+
nodelua.INFO.COPYRIGHT
302+
nodelua.INFO.AUTHORS
303+
304+
nodelua.LUA.GLOBALSINDEX // pseudo-index of the globals table
305+
nodelua.LUA.REGISTRYINDEX // pseudo-index of the registry
306+
307+
nodelua.STATUS.YIELD
308+
nodelua.STATUS.ERRRUN
309+
nodelua.STATUS.ERRSYNTAX
310+
nodelua.STATUS.ERRMEM
311+
nodelua.STATUS.ERRERR
312+
313+
nodelua.GC.STOP
314+
nodelua.GC.RESTART
315+
nodelua.GC.COLLECT
316+
nodelua.GC.COUNT
317+
nodelua.GC.COUNTB
318+
nodelua.GC.STEP
319+
nodelua.GC.SETPAUSE
320+
nodelua.GC.SETSTEPMUL
199321
```
322+
323+
## Caveats
324+
325+
This is a thin wrapper over the Lua C API, and it does not shield you from every way of misusing
326+
that API. Some stack operations on values of an unexpected type raise an *unprotected* Lua error,
327+
which aborts the process rather than throwing a JavaScript exception. `SetField` and `GetField`
328+
guard against this explicitly; other methods do not. Keep track of what is on the stack.
329+
330+
## Development
331+
332+
```
333+
npm install
334+
npm test
335+
```
336+
337+
## License
338+
339+
ISC — see [LICENSE.md](LICENSE.md), which also covers the vendored Lua and LuaFileSystem sources.

0 commit comments

Comments
 (0)