🎓 A1Learner
🔍
0 of 52 lessons completed

Lua Package Path

package.path is a list of templates that tells require where to look for a module file. Each template uses a placeholder for the module name, and dots in the name become directories.

Dots become directories and the placeholder is replaced

Dots become slashes in the file path. The template placeholder is then replaced by that path.

local name = "shop.cart"
local file = name:gsub("%." , "/")
local template = "lib/?.lua"
local expanded = template:gsub("%?", file)
print(file)
print(expanded)

Requiring twice loads the module once

The loader is registered, then the module is required twice. The load count proves the loader ran once.

local loads = 0
package.preload["shop.cart"] = function()
  loads = loads + 1
  local M = {}
  function M.total(a, b)
    return a + b
  end
  return M
end
local first = require("shop.cart")
local second = require("shop.cart")
print(loads)
print(first.total(2, 3))
print(first == second)

Several templates expand the same name differently

Two templates are tried in order. Each one expands the same module name to a different file path.

local name = "shop.cart"
local file = name:gsub("%." , "/")
for template in string.gmatch("lib/?.lua;lib/?/init.lua", "[^;]+") do
  local expanded = template:gsub("%?", file)
  print(expanded)
end

Example

local loads = 0
package.preload["shop.cart"] = function()
  loads = loads + 1
  local M = {}
  local prices = {apple = 2, bread = 3}
  function M.bill(a, b)
    return prices[a] + prices[b]
  end
  return M
end
local name = "shop.cart"
local file = name:gsub("%." , "/")
local template = "lib/?.lua"
local expanded = template:gsub("%?", file)
print(expanded)
local cart1 = require(name)
local cart2 = require(name)
print(loads)
print(cart1.bill("apple", "bread"))
print(cart1 == cart2)
print(package.loaded[name] == cart1)

Key points

  • Template list: package.path holds templates separated by semicolons, and require tries each one in order.
  • Placeholder: the question mark in a template is replaced by the module name when require searches.
  • Dots to directories: each dot in a module name becomes a directory separator in the file path.
  • Preload table: package.preload holds loader functions keyed by module name for modules registered in code.
  • Loaded cache: package.loaded holds modules already loaded, so a second require returns the same table.
  • Load once: a loader function runs only on the first require, and later requires reuse the cached table.

In this example, the module name expands to a library file path, the preload loader registers the cart module, and two requires return the same cached table after a single load.

Note

at this level package.preload registers a module in the same file, so no outside file is needed.

Try it Yourself

Edit the code, then press Run.

Running is simulated

Exercise

A module is registered in package.preload and required twice. How many times does its loader function run?