Lua runner with rich builtin stdlib
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
a/docs/require.md

110 lines
4.2 KiB

# Modules & `require`
`a` provides a sandboxed `require` for splitting a script into multiple files.
It follows stock Lua's conventions — dot-separated module names, one execution
per module, cached results — so editors and the Lua language server understand
the imports without special setup.
```lua
local helper = require "lib.helper" -- lib/helper.lua
local db = require "db" -- db.lua or db/init.lua
local mod, path = require "lib.helper" -- second value: the resolved file path
```
## Module names
A name is one or more dot-separated segments; each segment may contain
`A–Z a–z 0–9 _ -`. Dots map to directory separators, so `require "db.migrations"`
looks for `db/migrations.lua`. Paths (`/`, `..`), absolute names, and empty
segments are rejected — a module can only ever be loaded from inside the
search roots.
## Search roots
Names are resolved against a fixed list of directories, in order:
1. **The main script's directory.** Symlinks to the script are resolved first,
so a script symlinked into `~/.local/bin` still loads modules (and its
`.env`, below) from next to the real file. In the REPL, the current working
directory is used instead.
2. **Each entry of `$A_INCLUDE_PATHS`**, colon-separated like `$PATH`.
In every root, two candidates are tried (the Lua language server's default
templates):
```
<root>/<name with dots as slashes>.lua
<root>/<name with dots as slashes>/init.lua
```
The first hit wins. If a later root (or the `init.lua` form) also matches, a
warning is logged about the shadowed file. When nothing matches, the error
lists every path that was tried, in the stock Lua format.
## `.env`
Before resolving anything, `a` loads a `.env` file from the main script's
real directory (the CWD in the REPL), if one exists. Variables already set in
the real environment take precedence. This is the intended place to set
`A_INCLUDE_PATHS` for a project:
```
# myproject/.env
A_INCLUDE_PATHS=/home/me/lua-libs:/opt/shared-lua
```
Since the script's symlink is resolved first, `myproject/main.lua` symlinked
as `~/.local/bin/mytool` still picks up `myproject/.env`.
## Semantics
- **A module file is a plain chunk.** Its `local`s are private to the file; it
shares globals with the rest of the program; whatever it returns is the
module's value (conventionally a table). Like stock Lua, the chunk receives
the module name and the resolved file path as `...`.
- **Executed once.** The return value is cached by the file's canonical path,
so two names or symlinks reaching the same file share one instance, and
`require` of an already-loaded module is just a table lookup. A module that
returns nothing is cached as `true`.
- **Return values.** `require` returns the module value and, for file modules,
the resolved path as a second value.
- **Built-ins.** `require "http"`, `require "utils"`, etc. return the same
table as the corresponding global, so code written for stock Lua's habits
works unchanged.
- **Cycles.** A require cycle (`a` requires `b` requires `a`) raises a
`circular require` error instead of recursing forever.
- **Errors.** If a module raises while loading, the error propagates to the
requiring code and nothing is cached — a later `require` retries the load.
- Module files may use the async stdlib (`http`, `os.sleep`, …) at top level,
and may start with a shebang and/or UTF-8 BOM, same as the main script.
## Editor / LSP setup
The resolution rules match the
[Lua language server](https://luals.github.io/) defaults, so a minimal
`.luarc.json` next to your project gives go-to-definition and type inference
across `require` boundaries:
```json
{
"runtime.version": "Lua 5.4",
"runtime.path": ["?.lua", "?/init.lua"],
"workspace.library": ["/home/me/lua-libs", "/opt/shared-lua"]
}
```
List the same directories in `workspace.library` as in `A_INCLUDE_PATHS`;
modules under the script's own directory are found via the workspace root.
## Example layout
```
myproject/
├── main.lua -- #!/usr/bin/env a; symlinked to ~/.local/bin/mytool
├── .env -- A_INCLUDE_PATHS=...
├── .luarc.json
└── lib/
├── helper.lua -- require "lib.helper"
└── db/
└── init.lua -- require "lib.db"
```