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.
111 lines
4.3 KiB
111 lines
4.3 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, which keeps `require` compatible with the sandbox: it is the
|
|
only file access scripts have.
|
|
|
|
## 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"
|
|
```
|
|
|