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

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.

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 locals 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 defaults, so a minimal .luarc.json next to your project gives go-to-definition and type inference across require boundaries:

{
    "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"