[{"data":1,"prerenderedAt":3793},["ShallowReactive",2],{"page-\u002Flua\u002F08-modules-and-packages":3},{"id":4,"title":5,"body":6,"description":3784,"extension":3785,"meta":3786,"navigation":100,"path":3787,"seo":3788,"stem":3791,"__hash__":3792},"content\u002Flua\u002F08-modules-and-packages.md","Lua 08 — Modules, Packages & Dependency Management",{"type":7,"value":8,"toc":3767},"minimark",[9,14,36,44,2724,2728,3763],[10,11,13],"h1",{"id":12},"_08-modules-packages-dependency-management","08 — Modules, Packages & Dependency Management",[15,16,17,18,22,23,27,28,31,32,35],"p",{},"A ",[19,20,21],"strong",{},"module"," is a chunk that returns a value (usually a table). ",[24,25,26],"code",{},"require(name)"," loads, executes, caches, and returns the module. The search path, the cache (",[24,29,30],{},"package.loaded","), and the searchers (",[24,33,34],{},"package.searchers",") control resolution. Understanding the cache semantics, circular dependency resolution, and hot-reload mechanics is essential for structuring non-trivial Lua applications.",[37,38,40,43],"h2",{"id":39},"require-mechanics",[24,41,42],{},"require()"," Mechanics",[45,46,48,292,299,961,965,1122,1126,1362,1370,1559,1563,1976,1980],"code-wrapper",{"language":47},"lua",[49,50,54],"pre",{"className":51,"code":52,"language":47,"meta":53,"style":53},"language-lua shiki shiki-themes github-light github-dark","-- require(name):\n-- 1. Check package.loaded[name] → if present, return cached value (no re-execution)\n-- 2. If not cached: search package.searchers for a loader function\n-- 3. The loader finds the file (via package.searchpath), compiles it, executes it\n-- 4. The return value is cached in package.loaded[name] and returned\n-- 5. If the module returns nil (or nothing), true is cached\n\nlocal utils = require(\"utils\")       -- first call: loads utils.lua, executes, caches\nlocal utils2 = require(\"utils\")      -- second call: returns CACHED value\nprint(utils == utils2)               -- true (same object)\n\n-- package.loaded: the module cache (table)\nfor name, mod in pairs(package.loaded) do\n  print(name, mod)  -- \"utils\" → table, \"string\" → string lib, etc.\nend\n\n-- Evict from cache to force reload:\npackage.loaded[\"utils\"] = nil\nlocal utils3 = require(\"utils\")      -- re-executes the chunk, returns new value\nprint(utils == utils3)              -- false (different object after reload)\n","",[24,55,56,65,71,77,83,89,95,102,133,155,173,178,184,209,221,227,232,238,256,277],{"__ignoreMap":53},[57,58,61],"span",{"class":59,"line":60},"line",1,[57,62,64],{"class":63},"sdCPZ","-- require(name):\n",[57,66,68],{"class":59,"line":67},2,[57,69,70],{"class":63},"-- 1. Check package.loaded[name] → if present, return cached value (no re-execution)\n",[57,72,74],{"class":59,"line":73},3,[57,75,76],{"class":63},"-- 2. If not cached: search package.searchers for a loader function\n",[57,78,80],{"class":59,"line":79},4,[57,81,82],{"class":63},"-- 3. The loader finds the file (via package.searchpath), compiles it, executes it\n",[57,84,86],{"class":59,"line":85},5,[57,87,88],{"class":63},"-- 4. The return value is cached in package.loaded[name] and returned\n",[57,90,92],{"class":59,"line":91},6,[57,93,94],{"class":63},"-- 5. If the module returns nil (or nothing), true is cached\n",[57,96,98],{"class":59,"line":97},7,[57,99,101],{"emptyLinePlaceholder":100},true,"\n",[57,103,105,109,113,116,120,123,127,130],{"class":59,"line":104},8,[57,106,108],{"class":107},"svdQ7","local",[57,110,112],{"class":111},"ssxIu"," utils ",[57,114,115],{"class":107},"=",[57,117,119],{"class":118},"snvgF"," require",[57,121,122],{"class":111},"(",[57,124,126],{"class":125},"sJ6F3","\"utils\"",[57,128,129],{"class":111},")       ",[57,131,132],{"class":63},"-- first call: loads utils.lua, executes, caches\n",[57,134,136,138,141,143,145,147,149,152],{"class":59,"line":135},9,[57,137,108],{"class":107},[57,139,140],{"class":111}," utils2 ",[57,142,115],{"class":107},[57,144,119],{"class":118},[57,146,122],{"class":111},[57,148,126],{"class":125},[57,150,151],{"class":111},")      ",[57,153,154],{"class":63},"-- second call: returns CACHED value\n",[57,156,158,161,164,167,170],{"class":59,"line":157},10,[57,159,160],{"class":118},"print",[57,162,163],{"class":111},"(utils ",[57,165,166],{"class":107},"==",[57,168,169],{"class":111}," utils2)               ",[57,171,172],{"class":63},"-- true (same object)\n",[57,174,176],{"class":59,"line":175},11,[57,177,101],{"emptyLinePlaceholder":100},[57,179,181],{"class":59,"line":180},12,[57,182,183],{"class":63},"-- package.loaded: the module cache (table)\n",[57,185,187,190,193,196,199,201,203,206],{"class":59,"line":186},13,[57,188,189],{"class":107},"for",[57,191,192],{"class":111}," name, mod ",[57,194,195],{"class":107},"in",[57,197,198],{"class":118}," pairs",[57,200,122],{"class":111},[57,202,30],{"class":118},[57,204,205],{"class":111},") ",[57,207,208],{"class":107},"do\n",[57,210,212,215,218],{"class":59,"line":211},14,[57,213,214],{"class":118},"  print",[57,216,217],{"class":111},"(name, mod)  ",[57,219,220],{"class":63},"-- \"utils\" → table, \"string\" → string lib, etc.\n",[57,222,224],{"class":59,"line":223},15,[57,225,226],{"class":107},"end\n",[57,228,230],{"class":59,"line":229},16,[57,231,101],{"emptyLinePlaceholder":100},[57,233,235],{"class":59,"line":234},17,[57,236,237],{"class":63},"-- Evict from cache to force reload:\n",[57,239,241,243,246,248,251,253],{"class":59,"line":240},18,[57,242,30],{"class":118},[57,244,245],{"class":111},"[",[57,247,126],{"class":125},[57,249,250],{"class":111},"] ",[57,252,115],{"class":107},[57,254,255],{"class":118}," nil\n",[57,257,259,261,264,266,268,270,272,274],{"class":59,"line":258},19,[57,260,108],{"class":107},[57,262,263],{"class":111}," utils3 ",[57,265,115],{"class":107},[57,267,119],{"class":118},[57,269,122],{"class":111},[57,271,126],{"class":125},[57,273,151],{"class":111},[57,275,276],{"class":63},"-- re-executes the chunk, returns new value\n",[57,278,280,282,284,286,289],{"class":59,"line":279},20,[57,281,160],{"class":118},[57,283,163],{"class":111},[57,285,166],{"class":107},[57,287,288],{"class":111}," utils3)              ",[57,290,291],{"class":63},"-- false (different object after reload)\n",[37,293,295,296],{"id":294},"search-path-packagesearchpath","Search Path & ",[24,297,298],{},"package.searchpath",[45,300,301,478,485],{"language":47},[49,302,304],{"className":51,"code":303,"language":47,"meta":53,"style":53},"-- package.path: semicolon-separated template paths for Lua modules\n-- ? is replaced with the module name (dots → slashes for subdirectories)\n-- Example: \".\u002F?.lua;.\u002F?\u002Finit.lua;\u002Fusr\u002Fshare\u002Flua\u002F5.4\u002F?.lua\"\n\n-- require(\"a.b.c\") searches:\n--   .\u002Fa\u002Fb\u002Fc.lua\n--   .\u002Fa\u002Fb\u002Fc\u002Finit.lua\n--   \u002Fusr\u002Fshare\u002Flua\u002F5.4\u002Fa\u002Fb\u002Fc.lua\n--   ...\n\n-- package.cpath: same for C modules (.so\u002F.dll)\n-- require(\"cjson\") searches cpath for cjson.so, loads via luaopen_cjson\n\n-- Add a custom search path:\npackage.path = package.path .. \";.\u002Flib\u002F?.lua;.\u002Flib\u002F?\u002Finit.lua\"\n\n-- package.searchpath(name, path, sep?, rep?): find file, return path or nil+err\nlocal path, err = package.searchpath(\"mylib.utils\", package.path)\n-- path = \".\u002Flib\u002Fmylib\u002Futils.lua\" (or nil with error listing tried paths)\n\n-- Custom search with a different separator:\nlocal path = package.searchpath(\"mylib::utils\", package.path, \"::\", \"\u002F\")\n-- treats :: as separator, replaces with \u002F for file path\n",[24,305,306,311,316,321,325,330,335,340,345,350,354,359,364,368,373,390,394,399,424,429,433,439,472],{"__ignoreMap":53},[57,307,308],{"class":59,"line":60},[57,309,310],{"class":63},"-- package.path: semicolon-separated template paths for Lua modules\n",[57,312,313],{"class":59,"line":67},[57,314,315],{"class":63},"-- ? is replaced with the module name (dots → slashes for subdirectories)\n",[57,317,318],{"class":59,"line":73},[57,319,320],{"class":63},"-- Example: \".\u002F?.lua;.\u002F?\u002Finit.lua;\u002Fusr\u002Fshare\u002Flua\u002F5.4\u002F?.lua\"\n",[57,322,323],{"class":59,"line":79},[57,324,101],{"emptyLinePlaceholder":100},[57,326,327],{"class":59,"line":85},[57,328,329],{"class":63},"-- require(\"a.b.c\") searches:\n",[57,331,332],{"class":59,"line":91},[57,333,334],{"class":63},"--   .\u002Fa\u002Fb\u002Fc.lua\n",[57,336,337],{"class":59,"line":97},[57,338,339],{"class":63},"--   .\u002Fa\u002Fb\u002Fc\u002Finit.lua\n",[57,341,342],{"class":59,"line":104},[57,343,344],{"class":63},"--   \u002Fusr\u002Fshare\u002Flua\u002F5.4\u002Fa\u002Fb\u002Fc.lua\n",[57,346,347],{"class":59,"line":135},[57,348,349],{"class":63},"--   ...\n",[57,351,352],{"class":59,"line":157},[57,353,101],{"emptyLinePlaceholder":100},[57,355,356],{"class":59,"line":175},[57,357,358],{"class":63},"-- package.cpath: same for C modules (.so\u002F.dll)\n",[57,360,361],{"class":59,"line":180},[57,362,363],{"class":63},"-- require(\"cjson\") searches cpath for cjson.so, loads via luaopen_cjson\n",[57,365,366],{"class":59,"line":186},[57,367,101],{"emptyLinePlaceholder":100},[57,369,370],{"class":59,"line":211},[57,371,372],{"class":63},"-- Add a custom search path:\n",[57,374,375,378,381,384,387],{"class":59,"line":223},[57,376,377],{"class":118},"package.path",[57,379,380],{"class":107}," =",[57,382,383],{"class":118}," package.path",[57,385,386],{"class":107}," ..",[57,388,389],{"class":125}," \";.\u002Flib\u002F?.lua;.\u002Flib\u002F?\u002Finit.lua\"\n",[57,391,392],{"class":59,"line":229},[57,393,101],{"emptyLinePlaceholder":100},[57,395,396],{"class":59,"line":234},[57,397,398],{"class":63},"-- package.searchpath(name, path, sep?, rep?): find file, return path or nil+err\n",[57,400,401,403,406,408,411,413,416,419,421],{"class":59,"line":240},[57,402,108],{"class":107},[57,404,405],{"class":111}," path, err ",[57,407,115],{"class":107},[57,409,410],{"class":118}," package.searchpath",[57,412,122],{"class":111},[57,414,415],{"class":125},"\"mylib.utils\"",[57,417,418],{"class":111},", ",[57,420,377],{"class":118},[57,422,423],{"class":111},")\n",[57,425,426],{"class":59,"line":258},[57,427,428],{"class":63},"-- path = \".\u002Flib\u002Fmylib\u002Futils.lua\" (or nil with error listing tried paths)\n",[57,430,431],{"class":59,"line":279},[57,432,101],{"emptyLinePlaceholder":100},[57,434,436],{"class":59,"line":435},21,[57,437,438],{"class":63},"-- Custom search with a different separator:\n",[57,440,442,444,447,449,451,453,456,458,460,462,465,467,470],{"class":59,"line":441},22,[57,443,108],{"class":107},[57,445,446],{"class":111}," path ",[57,448,115],{"class":107},[57,450,410],{"class":118},[57,452,122],{"class":111},[57,454,455],{"class":125},"\"mylib::utils\"",[57,457,418],{"class":111},[57,459,377],{"class":118},[57,461,418],{"class":111},[57,463,464],{"class":125},"\"::\"",[57,466,418],{"class":111},[57,468,469],{"class":125},"\"\u002F\"",[57,471,423],{"class":111},[57,473,475],{"class":59,"line":474},23,[57,476,477],{"class":63},"-- treats :: as separator, replaces with \u002F for file path\n",[37,479,481,482,484],{"id":480},"searchers-packagesearchers","Searchers (",[24,483,34],{},")",[45,486,487,755,759,764],{"language":47},[49,488,490],{"className":51,"code":489,"language":47,"meta":53,"style":53},"-- package.searchers (5.2+, called package.loaders in 5.1): ordered list of functions\n-- Each searcher(name) returns: loader_fn | \"error message\" | nil (try next)\n-- Default searchers (in order):\n-- 1. package.preload[name] — check preloaded loaders (programmatic registration)\n-- 2. package.path searcher — find .lua file on package.path\n-- 3. package.cpath searcher — find .so\u002F.dll on package.cpath, call luaopen_name\n-- 4. all-in-one loader — find submodules of C packages\n\n-- Register a module without a file (useful for testing\u002Fmocking):\npackage.preload[\"fake_http\"] = function()\n  return {\n    get = function(url) return \"mocked response for \" .. url end,\n  }\nend\nlocal http = require(\"fake_http\")  -- uses preload, no file needed\n\n-- Custom searcher: load from a database or embedded string\ntable.insert(package.searchers, 1, function(name)\n  local source = embedded_modules[name]  -- your custom source map\n  if not source then return nil end  -- not found, try next searcher\n  local fn, err = load(source, name, \"t\")  -- compile\n  if not fn then return err end\n  return fn  -- loader function (will be called by require)\nend)\n",[24,491,492,497,502,507,512,517,522,527,531,536,556,564,595,600,604,625,629,634,656,672,697,720,738,748],{"__ignoreMap":53},[57,493,494],{"class":59,"line":60},[57,495,496],{"class":63},"-- package.searchers (5.2+, called package.loaders in 5.1): ordered list of functions\n",[57,498,499],{"class":59,"line":67},[57,500,501],{"class":63},"-- Each searcher(name) returns: loader_fn | \"error message\" | nil (try next)\n",[57,503,504],{"class":59,"line":73},[57,505,506],{"class":63},"-- Default searchers (in order):\n",[57,508,509],{"class":59,"line":79},[57,510,511],{"class":63},"-- 1. package.preload[name] — check preloaded loaders (programmatic registration)\n",[57,513,514],{"class":59,"line":85},[57,515,516],{"class":63},"-- 2. package.path searcher — find .lua file on package.path\n",[57,518,519],{"class":59,"line":91},[57,520,521],{"class":63},"-- 3. package.cpath searcher — find .so\u002F.dll on package.cpath, call luaopen_name\n",[57,523,524],{"class":59,"line":97},[57,525,526],{"class":63},"-- 4. all-in-one loader — find submodules of C packages\n",[57,528,529],{"class":59,"line":104},[57,530,101],{"emptyLinePlaceholder":100},[57,532,533],{"class":59,"line":135},[57,534,535],{"class":63},"-- Register a module without a file (useful for testing\u002Fmocking):\n",[57,537,538,541,543,546,548,550,553],{"class":59,"line":157},[57,539,540],{"class":118},"package.preload",[57,542,245],{"class":111},[57,544,545],{"class":125},"\"fake_http\"",[57,547,250],{"class":111},[57,549,115],{"class":107},[57,551,552],{"class":107}," function",[57,554,555],{"class":111},"()\n",[57,557,558,561],{"class":59,"line":175},[57,559,560],{"class":107},"  return",[57,562,563],{"class":111}," {\n",[57,565,566,570,572,574,577,580,583,586,589,592],{"class":59,"line":180},[57,567,569],{"class":568},"sIsaT","    get",[57,571,380],{"class":107},[57,573,552],{"class":107},[57,575,576],{"class":111},"(url) ",[57,578,579],{"class":107},"return",[57,581,582],{"class":125}," \"mocked response for \" ",[57,584,585],{"class":107},"..",[57,587,588],{"class":111}," url ",[57,590,591],{"class":107},"end",[57,593,594],{"class":111},",\n",[57,596,597],{"class":59,"line":186},[57,598,599],{"class":111},"  }\n",[57,601,602],{"class":59,"line":211},[57,603,226],{"class":107},[57,605,606,608,611,613,615,617,619,622],{"class":59,"line":223},[57,607,108],{"class":107},[57,609,610],{"class":111}," http ",[57,612,115],{"class":107},[57,614,119],{"class":118},[57,616,122],{"class":111},[57,618,545],{"class":125},[57,620,621],{"class":111},")  ",[57,623,624],{"class":63},"-- uses preload, no file needed\n",[57,626,627],{"class":59,"line":229},[57,628,101],{"emptyLinePlaceholder":100},[57,630,631],{"class":59,"line":234},[57,632,633],{"class":63},"-- Custom searcher: load from a database or embedded string\n",[57,635,636,639,641,643,645,648,650,653],{"class":59,"line":240},[57,637,638],{"class":118},"table.insert",[57,640,122],{"class":111},[57,642,34],{"class":118},[57,644,418],{"class":111},[57,646,647],{"class":118},"1",[57,649,418],{"class":111},[57,651,652],{"class":107},"function",[57,654,655],{"class":111},"(name)\n",[57,657,658,661,664,666,669],{"class":59,"line":258},[57,659,660],{"class":107},"  local",[57,662,663],{"class":111}," source ",[57,665,115],{"class":107},[57,667,668],{"class":111}," embedded_modules[name]  ",[57,670,671],{"class":63},"-- your custom source map\n",[57,673,674,677,680,682,685,688,691,694],{"class":59,"line":279},[57,675,676],{"class":107},"  if",[57,678,679],{"class":107}," not",[57,681,663],{"class":111},[57,683,684],{"class":107},"then",[57,686,687],{"class":107}," return",[57,689,690],{"class":118}," nil",[57,692,693],{"class":107}," end",[57,695,696],{"class":63},"  -- not found, try next searcher\n",[57,698,699,701,704,706,709,712,715,717],{"class":59,"line":435},[57,700,660],{"class":107},[57,702,703],{"class":111}," fn, err ",[57,705,115],{"class":107},[57,707,708],{"class":118}," load",[57,710,711],{"class":111},"(source, name, ",[57,713,714],{"class":125},"\"t\"",[57,716,621],{"class":111},[57,718,719],{"class":63},"-- compile\n",[57,721,722,724,726,729,731,733,736],{"class":59,"line":441},[57,723,676],{"class":107},[57,725,679],{"class":107},[57,727,728],{"class":111}," fn ",[57,730,684],{"class":107},[57,732,687],{"class":107},[57,734,735],{"class":111}," err ",[57,737,226],{"class":107},[57,739,740,742,745],{"class":59,"line":474},[57,741,560],{"class":107},[57,743,744],{"class":111}," fn  ",[57,746,747],{"class":63},"-- loader function (will be called by require)\n",[57,749,751,753],{"class":59,"line":750},24,[57,752,591],{"class":107},[57,754,423],{"class":111},[37,756,758],{"id":757},"module-patterns","Module Patterns",[760,761,763],"h3",{"id":762},"pattern-1-table-module-most-common","Pattern 1: Table Module (most common)",[45,765,767],{"language":47,"filename":766},"math_utils.lua",[49,768,770],{"className":51,"code":769,"language":47,"meta":53,"style":53},"-- Module = table of exported functions. Local helpers stay private.\nlocal M = {}  -- the module table (exported)\n\n-- Local (private) helper — not in M, not accessible externally\nlocal function validate(x)\n  assert(type(x) == \"number\", \"expected number\")\n  return x\nend\n\n-- Public functions assigned to M\nfunction M.add(a, b)\n  validate(a); validate(b)\n  return a + b\nend\n\nfunction M.multiply(a, b)\n  validate(a); validate(b)\n  return a * b\nend\n\nreturn M  -- the require() return value\n",[24,771,772,777,792,796,801,813,838,845,849,853,858,874,888,901,905,909,922,932,943,947,951],{"__ignoreMap":53},[57,773,774],{"class":59,"line":60},[57,775,776],{"class":63},"-- Module = table of exported functions. Local helpers stay private.\n",[57,778,779,781,784,786,789],{"class":59,"line":67},[57,780,108],{"class":107},[57,782,783],{"class":111}," M ",[57,785,115],{"class":107},[57,787,788],{"class":111}," {}  ",[57,790,791],{"class":63},"-- the module table (exported)\n",[57,793,794],{"class":59,"line":73},[57,795,101],{"emptyLinePlaceholder":100},[57,797,798],{"class":59,"line":79},[57,799,800],{"class":63},"-- Local (private) helper — not in M, not accessible externally\n",[57,802,803,805,807,810],{"class":59,"line":85},[57,804,108],{"class":107},[57,806,552],{"class":107},[57,808,809],{"class":568}," validate",[57,811,812],{"class":111},"(x)\n",[57,814,815,818,820,823,826,828,831,833,836],{"class":59,"line":91},[57,816,817],{"class":118},"  assert",[57,819,122],{"class":111},[57,821,822],{"class":118},"type",[57,824,825],{"class":111},"(x) ",[57,827,166],{"class":107},[57,829,830],{"class":125}," \"number\"",[57,832,418],{"class":111},[57,834,835],{"class":125},"\"expected number\"",[57,837,423],{"class":111},[57,839,840,842],{"class":59,"line":97},[57,841,560],{"class":107},[57,843,844],{"class":111}," x\n",[57,846,847],{"class":59,"line":104},[57,848,226],{"class":107},[57,850,851],{"class":59,"line":135},[57,852,101],{"emptyLinePlaceholder":100},[57,854,855],{"class":59,"line":157},[57,856,857],{"class":63},"-- Public functions assigned to M\n",[57,859,860,862,865,868,871],{"class":59,"line":175},[57,861,652],{"class":107},[57,863,864],{"class":568}," M",[57,866,867],{"class":111},".",[57,869,870],{"class":568},"add",[57,872,873],{"class":111},"(a, b)\n",[57,875,876,879,882,885],{"class":59,"line":180},[57,877,878],{"class":118},"  validate",[57,880,881],{"class":111},"(a); ",[57,883,884],{"class":118},"validate",[57,886,887],{"class":111},"(b)\n",[57,889,890,892,895,898],{"class":59,"line":186},[57,891,560],{"class":107},[57,893,894],{"class":111}," a ",[57,896,897],{"class":107},"+",[57,899,900],{"class":111}," b\n",[57,902,903],{"class":59,"line":211},[57,904,226],{"class":107},[57,906,907],{"class":59,"line":223},[57,908,101],{"emptyLinePlaceholder":100},[57,910,911,913,915,917,920],{"class":59,"line":229},[57,912,652],{"class":107},[57,914,864],{"class":568},[57,916,867],{"class":111},[57,918,919],{"class":568},"multiply",[57,921,873],{"class":111},[57,923,924,926,928,930],{"class":59,"line":234},[57,925,878],{"class":118},[57,927,881],{"class":111},[57,929,884],{"class":118},[57,931,887],{"class":111},[57,933,934,936,938,941],{"class":59,"line":240},[57,935,560],{"class":107},[57,937,894],{"class":111},[57,939,940],{"class":107},"*",[57,942,900],{"class":111},[57,944,945],{"class":59,"line":258},[57,946,226],{"class":107},[57,948,949],{"class":59,"line":279},[57,950,101],{"emptyLinePlaceholder":100},[57,952,953,955,958],{"class":59,"line":435},[57,954,579],{"class":107},[57,956,957],{"class":111}," M  ",[57,959,960],{"class":63},"-- the require() return value\n",[760,962,964],{"id":963},"pattern-2-closure-module-true-privacy","Pattern 2: Closure Module (true privacy)",[45,966,968],{"language":47,"filename":967},"counter.lua",[49,969,971],{"className":51,"code":970,"language":47,"meta":53,"style":53},"-- No table returned; the module's value is a single function (or a few closures)\n-- All state is in upvalues — truly private, not even accessible via M\n\nlocal count = 0  -- private upvalue\n\nlocal function inc() count = count + 1; return count end\nlocal function get() return count end\nlocal function reset() count = 0 end\n\nreturn {\n  inc = inc,\n  get = get,\n  reset = reset,\n}\n-- count is NOT accessible — it's an upvalue of the closures, not in the returned table\n",[24,972,973,978,983,987,1002,1006,1036,1054,1072,1076,1082,1092,1102,1112,1117],{"__ignoreMap":53},[57,974,975],{"class":59,"line":60},[57,976,977],{"class":63},"-- No table returned; the module's value is a single function (or a few closures)\n",[57,979,980],{"class":59,"line":67},[57,981,982],{"class":63},"-- All state is in upvalues — truly private, not even accessible via M\n",[57,984,985],{"class":59,"line":73},[57,986,101],{"emptyLinePlaceholder":100},[57,988,989,991,994,996,999],{"class":59,"line":79},[57,990,108],{"class":107},[57,992,993],{"class":111}," count ",[57,995,115],{"class":107},[57,997,998],{"class":118}," 0",[57,1000,1001],{"class":63},"  -- private upvalue\n",[57,1003,1004],{"class":59,"line":85},[57,1005,101],{"emptyLinePlaceholder":100},[57,1007,1008,1010,1012,1015,1018,1020,1022,1024,1027,1030,1032,1034],{"class":59,"line":91},[57,1009,108],{"class":107},[57,1011,552],{"class":107},[57,1013,1014],{"class":568}," inc",[57,1016,1017],{"class":111},"() count ",[57,1019,115],{"class":107},[57,1021,993],{"class":111},[57,1023,897],{"class":107},[57,1025,1026],{"class":118}," 1",[57,1028,1029],{"class":111},"; ",[57,1031,579],{"class":107},[57,1033,993],{"class":111},[57,1035,226],{"class":107},[57,1037,1038,1040,1042,1045,1048,1050,1052],{"class":59,"line":97},[57,1039,108],{"class":107},[57,1041,552],{"class":107},[57,1043,1044],{"class":568}," get",[57,1046,1047],{"class":111},"() ",[57,1049,579],{"class":107},[57,1051,993],{"class":111},[57,1053,226],{"class":107},[57,1055,1056,1058,1060,1063,1065,1067,1069],{"class":59,"line":104},[57,1057,108],{"class":107},[57,1059,552],{"class":107},[57,1061,1062],{"class":568}," reset",[57,1064,1017],{"class":111},[57,1066,115],{"class":107},[57,1068,998],{"class":118},[57,1070,1071],{"class":107}," end\n",[57,1073,1074],{"class":59,"line":135},[57,1075,101],{"emptyLinePlaceholder":100},[57,1077,1078,1080],{"class":59,"line":157},[57,1079,579],{"class":107},[57,1081,563],{"class":111},[57,1083,1084,1087,1089],{"class":59,"line":175},[57,1085,1086],{"class":111},"  inc ",[57,1088,115],{"class":107},[57,1090,1091],{"class":111}," inc,\n",[57,1093,1094,1097,1099],{"class":59,"line":180},[57,1095,1096],{"class":111},"  get ",[57,1098,115],{"class":107},[57,1100,1101],{"class":111}," get,\n",[57,1103,1104,1107,1109],{"class":59,"line":186},[57,1105,1106],{"class":111},"  reset ",[57,1108,115],{"class":107},[57,1110,1111],{"class":111}," reset,\n",[57,1113,1114],{"class":59,"line":211},[57,1115,1116],{"class":111},"}\n",[57,1118,1119],{"class":59,"line":223},[57,1120,1121],{"class":63},"-- count is NOT accessible — it's an upvalue of the closures, not in the returned table\n",[760,1123,1125],{"id":1124},"pattern-3-class-like-module","Pattern 3: Class-Like Module",[45,1127,1129],{"language":47,"filename":1128},"point.lua",[49,1130,1132],{"className":51,"code":1131,"language":47,"meta":53,"style":53},"-- Module is a class (metatable); require returns the class table\nlocal Point = {}\nPoint.__index = Point\n\nfunction Point.new(x, y)\n  return setmetatable({x = x, y = y}, Point)\nend\n\nfunction Point:distance(other)\n  local dx, dy = self.x - other.x, self.y - other.y\n  return math.sqrt(dx * dx + dy * dy)\nend\n\nfunction Point:__tostring()\n  return string.format(\"(%d, %d)\", self.x, self.y)\nend\n\nreturn Point  -- callers do: local Point = require(\"point\"); local p = Point.new(1, 2)\n",[24,1133,1134,1139,1151,1164,1168,1183,1203,1207,1211,1226,1268,1293,1297,1301,1314,1344,1348,1352],{"__ignoreMap":53},[57,1135,1136],{"class":59,"line":60},[57,1137,1138],{"class":63},"-- Module is a class (metatable); require returns the class table\n",[57,1140,1141,1143,1146,1148],{"class":59,"line":67},[57,1142,108],{"class":107},[57,1144,1145],{"class":111}," Point ",[57,1147,115],{"class":107},[57,1149,1150],{"class":111}," {}\n",[57,1152,1153,1156,1159,1161],{"class":59,"line":73},[57,1154,1155],{"class":111},"Point.",[57,1157,1158],{"class":568},"__index",[57,1160,380],{"class":107},[57,1162,1163],{"class":111}," Point\n",[57,1165,1166],{"class":59,"line":79},[57,1167,101],{"emptyLinePlaceholder":100},[57,1169,1170,1172,1175,1177,1180],{"class":59,"line":85},[57,1171,652],{"class":107},[57,1173,1174],{"class":568}," Point",[57,1176,867],{"class":111},[57,1178,1179],{"class":568},"new",[57,1181,1182],{"class":111},"(x, y)\n",[57,1184,1185,1187,1190,1193,1195,1198,1200],{"class":59,"line":91},[57,1186,560],{"class":107},[57,1188,1189],{"class":118}," setmetatable",[57,1191,1192],{"class":111},"({x ",[57,1194,115],{"class":107},[57,1196,1197],{"class":111}," x, y ",[57,1199,115],{"class":107},[57,1201,1202],{"class":111}," y}, Point)\n",[57,1204,1205],{"class":59,"line":97},[57,1206,226],{"class":107},[57,1208,1209],{"class":59,"line":104},[57,1210,101],{"emptyLinePlaceholder":100},[57,1212,1213,1215,1217,1220,1223],{"class":59,"line":135},[57,1214,652],{"class":107},[57,1216,1174],{"class":568},[57,1218,1219],{"class":111},":",[57,1221,1222],{"class":568},"distance",[57,1224,1225],{"class":111},"(other)\n",[57,1227,1228,1230,1233,1235,1238,1240,1243,1246,1249,1251,1253,1256,1258,1261,1263,1265],{"class":59,"line":157},[57,1229,660],{"class":107},[57,1231,1232],{"class":111}," dx, dy ",[57,1234,115],{"class":107},[57,1236,1237],{"class":118}," self",[57,1239,867],{"class":111},[57,1241,1242],{"class":568},"x",[57,1244,1245],{"class":107}," -",[57,1247,1248],{"class":111}," other.",[57,1250,1242],{"class":568},[57,1252,418],{"class":111},[57,1254,1255],{"class":118},"self",[57,1257,867],{"class":111},[57,1259,1260],{"class":568},"y",[57,1262,1245],{"class":107},[57,1264,1248],{"class":111},[57,1266,1267],{"class":568},"y\n",[57,1269,1270,1272,1275,1278,1280,1283,1285,1288,1290],{"class":59,"line":175},[57,1271,560],{"class":107},[57,1273,1274],{"class":118}," math.sqrt",[57,1276,1277],{"class":111},"(dx ",[57,1279,940],{"class":107},[57,1281,1282],{"class":111}," dx ",[57,1284,897],{"class":107},[57,1286,1287],{"class":111}," dy ",[57,1289,940],{"class":107},[57,1291,1292],{"class":111}," dy)\n",[57,1294,1295],{"class":59,"line":180},[57,1296,226],{"class":107},[57,1298,1299],{"class":59,"line":186},[57,1300,101],{"emptyLinePlaceholder":100},[57,1302,1303,1305,1307,1309,1312],{"class":59,"line":211},[57,1304,652],{"class":107},[57,1306,1174],{"class":568},[57,1308,1219],{"class":111},[57,1310,1311],{"class":568},"__tostring",[57,1313,555],{"class":111},[57,1315,1316,1318,1321,1323,1326,1328,1330,1332,1334,1336,1338,1340,1342],{"class":59,"line":223},[57,1317,560],{"class":107},[57,1319,1320],{"class":118}," string.format",[57,1322,122],{"class":111},[57,1324,1325],{"class":125},"\"(%d, %d)\"",[57,1327,418],{"class":111},[57,1329,1255],{"class":118},[57,1331,867],{"class":111},[57,1333,1242],{"class":568},[57,1335,418],{"class":111},[57,1337,1255],{"class":118},[57,1339,867],{"class":111},[57,1341,1260],{"class":568},[57,1343,423],{"class":111},[57,1345,1346],{"class":59,"line":229},[57,1347,226],{"class":107},[57,1349,1350],{"class":59,"line":234},[57,1351,101],{"emptyLinePlaceholder":100},[57,1353,1354,1356,1359],{"class":59,"line":240},[57,1355,579],{"class":107},[57,1357,1358],{"class":111}," Point  ",[57,1360,1361],{"class":63},"-- callers do: local Point = require(\"point\"); local p = Point.new(1, 2)\n",[760,1363,1365,1366,1369],{"id":1364},"pattern-4-module-with-_env-export-by-assignment","Pattern 4: Module with ",[24,1367,1368],{},"_ENV"," (export-by-assignment)",[45,1371,1373],{"language":47,"filename":1372},"config.lua",[49,1374,1376],{"className":51,"code":1375,"language":47,"meta":53,"style":53},"-- Change _ENV so all globals write to the module table — no `M.` prefix needed\nlocal M = {}\ndo\n  local _ENV = M  -- all unqualified assignments go to M, not _G\n\n  host = \"localhost\"      -- M.host (not _G.host)\n  port = 8080            -- M.port\n\n  function connect()      -- M.connect\n    return \"connecting to \" .. host .. \":\" .. port\n  end\n\n  function reload(new_host, new_port)\n    host = new_host or host  -- _ENV.host, but _ENV = M, so M.host\n    port = new_port or port\n  end\nend\nreturn M\n-- Clean syntax, no M. prefix; but reads of `host` inside the block also\n-- go through _ENV (M), so they read M.host (correct)\n",[24,1377,1378,1383,1393,1397,1411,1415,1428,1441,1445,1459,1482,1487,1491,1501,1520,1534,1538,1542,1549,1554],{"__ignoreMap":53},[57,1379,1380],{"class":59,"line":60},[57,1381,1382],{"class":63},"-- Change _ENV so all globals write to the module table — no `M.` prefix needed\n",[57,1384,1385,1387,1389,1391],{"class":59,"line":67},[57,1386,108],{"class":107},[57,1388,783],{"class":111},[57,1390,115],{"class":107},[57,1392,1150],{"class":111},[57,1394,1395],{"class":59,"line":73},[57,1396,208],{"class":107},[57,1398,1399,1401,1404,1406,1408],{"class":59,"line":79},[57,1400,660],{"class":107},[57,1402,1403],{"class":118}," _ENV",[57,1405,380],{"class":107},[57,1407,957],{"class":111},[57,1409,1410],{"class":63},"-- all unqualified assignments go to M, not _G\n",[57,1412,1413],{"class":59,"line":85},[57,1414,101],{"emptyLinePlaceholder":100},[57,1416,1417,1420,1422,1425],{"class":59,"line":91},[57,1418,1419],{"class":111},"  host ",[57,1421,115],{"class":107},[57,1423,1424],{"class":125}," \"localhost\"      ",[57,1426,1427],{"class":63},"-- M.host (not _G.host)\n",[57,1429,1430,1433,1435,1438],{"class":59,"line":97},[57,1431,1432],{"class":111},"  port ",[57,1434,115],{"class":107},[57,1436,1437],{"class":118}," 8080",[57,1439,1440],{"class":63},"            -- M.port\n",[57,1442,1443],{"class":59,"line":104},[57,1444,101],{"emptyLinePlaceholder":100},[57,1446,1447,1450,1453,1456],{"class":59,"line":135},[57,1448,1449],{"class":107},"  function",[57,1451,1452],{"class":568}," connect",[57,1454,1455],{"class":111},"()      ",[57,1457,1458],{"class":63},"-- M.connect\n",[57,1460,1461,1464,1467,1469,1472,1474,1477,1479],{"class":59,"line":157},[57,1462,1463],{"class":107},"    return",[57,1465,1466],{"class":125}," \"connecting to \" ",[57,1468,585],{"class":107},[57,1470,1471],{"class":111}," host ",[57,1473,585],{"class":107},[57,1475,1476],{"class":125}," \":\" ",[57,1478,585],{"class":107},[57,1480,1481],{"class":111}," port\n",[57,1483,1484],{"class":59,"line":175},[57,1485,1486],{"class":107},"  end\n",[57,1488,1489],{"class":59,"line":180},[57,1490,101],{"emptyLinePlaceholder":100},[57,1492,1493,1495,1498],{"class":59,"line":186},[57,1494,1449],{"class":107},[57,1496,1497],{"class":568}," reload",[57,1499,1500],{"class":111},"(new_host, new_port)\n",[57,1502,1503,1506,1508,1511,1514,1517],{"class":59,"line":211},[57,1504,1505],{"class":111},"    host ",[57,1507,115],{"class":107},[57,1509,1510],{"class":111}," new_host ",[57,1512,1513],{"class":107},"or",[57,1515,1516],{"class":111}," host  ",[57,1518,1519],{"class":63},"-- _ENV.host, but _ENV = M, so M.host\n",[57,1521,1522,1525,1527,1530,1532],{"class":59,"line":223},[57,1523,1524],{"class":111},"    port ",[57,1526,115],{"class":107},[57,1528,1529],{"class":111}," new_port ",[57,1531,1513],{"class":107},[57,1533,1481],{"class":111},[57,1535,1536],{"class":59,"line":229},[57,1537,1486],{"class":107},[57,1539,1540],{"class":59,"line":234},[57,1541,226],{"class":107},[57,1543,1544,1546],{"class":59,"line":240},[57,1545,579],{"class":107},[57,1547,1548],{"class":111}," M\n",[57,1550,1551],{"class":59,"line":258},[57,1552,1553],{"class":63},"-- Clean syntax, no M. prefix; but reads of `host` inside the block also\n",[57,1555,1556],{"class":59,"line":279},[57,1557,1558],{"class":63},"-- go through _ENV (M), so they read M.host (correct)\n",[37,1560,1562],{"id":1561},"circular-dependencies","Circular Dependencies",[45,1564,1565,1771,1775],{"language":47},[49,1566,1568],{"className":51,"code":1567,"language":47,"meta":53,"style":53},"-- Problem: A requires B, B requires A. When A is loading, it requires B.\n-- B then requires A — but A is still loading (incomplete). B gets the\n-- partially-constructed A (or the true sentinel if A hasn't returned yet).\n\n-- a.lua\nlocal B = require(\"b\")     -- B not loaded yet → loads b.lua\nlocal M = {}\nM.value = 1\nfunction M.use_b() return B.value end  -- uses B (which is loaded by now)\nreturn M\n\n-- b.lua\nlocal A = require(\"a\")     -- A is ALREADY loading (in package.loaded as a sentinel)\n-- At this point, A is the chunk's return value — but a.lua hasn't returned yet!\n-- So A might be nil (if a.lua hasn't reached `return M`)\nlocal M = {}\nM.value = 2\nfunction M.use_a()\n  -- SAFE: defer the access to call time (A is fully loaded by now)\n  return A.value  -- A is the complete module at call time\nend\nreturn M\n",[24,1569,1570,1575,1580,1585,1589,1594,1616,1626,1639,1664,1670,1674,1679,1700,1705,1710,1720,1731,1744,1749,1761,1765],{"__ignoreMap":53},[57,1571,1572],{"class":59,"line":60},[57,1573,1574],{"class":63},"-- Problem: A requires B, B requires A. When A is loading, it requires B.\n",[57,1576,1577],{"class":59,"line":67},[57,1578,1579],{"class":63},"-- B then requires A — but A is still loading (incomplete). B gets the\n",[57,1581,1582],{"class":59,"line":73},[57,1583,1584],{"class":63},"-- partially-constructed A (or the true sentinel if A hasn't returned yet).\n",[57,1586,1587],{"class":59,"line":79},[57,1588,101],{"emptyLinePlaceholder":100},[57,1590,1591],{"class":59,"line":85},[57,1592,1593],{"class":63},"-- a.lua\n",[57,1595,1596,1598,1601,1603,1605,1607,1610,1613],{"class":59,"line":91},[57,1597,108],{"class":107},[57,1599,1600],{"class":111}," B ",[57,1602,115],{"class":107},[57,1604,119],{"class":118},[57,1606,122],{"class":111},[57,1608,1609],{"class":125},"\"b\"",[57,1611,1612],{"class":111},")     ",[57,1614,1615],{"class":63},"-- B not loaded yet → loads b.lua\n",[57,1617,1618,1620,1622,1624],{"class":59,"line":97},[57,1619,108],{"class":107},[57,1621,783],{"class":111},[57,1623,115],{"class":107},[57,1625,1150],{"class":111},[57,1627,1628,1631,1634,1636],{"class":59,"line":104},[57,1629,1630],{"class":111},"M.",[57,1632,1633],{"class":568},"value",[57,1635,380],{"class":107},[57,1637,1638],{"class":118}," 1\n",[57,1640,1641,1643,1645,1647,1650,1652,1654,1657,1659,1661],{"class":59,"line":135},[57,1642,652],{"class":107},[57,1644,864],{"class":568},[57,1646,867],{"class":111},[57,1648,1649],{"class":568},"use_b",[57,1651,1047],{"class":111},[57,1653,579],{"class":107},[57,1655,1656],{"class":111}," B.",[57,1658,1633],{"class":568},[57,1660,693],{"class":107},[57,1662,1663],{"class":63},"  -- uses B (which is loaded by now)\n",[57,1665,1666,1668],{"class":59,"line":157},[57,1667,579],{"class":107},[57,1669,1548],{"class":111},[57,1671,1672],{"class":59,"line":175},[57,1673,101],{"emptyLinePlaceholder":100},[57,1675,1676],{"class":59,"line":180},[57,1677,1678],{"class":63},"-- b.lua\n",[57,1680,1681,1683,1686,1688,1690,1692,1695,1697],{"class":59,"line":186},[57,1682,108],{"class":107},[57,1684,1685],{"class":111}," A ",[57,1687,115],{"class":107},[57,1689,119],{"class":118},[57,1691,122],{"class":111},[57,1693,1694],{"class":125},"\"a\"",[57,1696,1612],{"class":111},[57,1698,1699],{"class":63},"-- A is ALREADY loading (in package.loaded as a sentinel)\n",[57,1701,1702],{"class":59,"line":211},[57,1703,1704],{"class":63},"-- At this point, A is the chunk's return value — but a.lua hasn't returned yet!\n",[57,1706,1707],{"class":59,"line":223},[57,1708,1709],{"class":63},"-- So A might be nil (if a.lua hasn't reached `return M`)\n",[57,1711,1712,1714,1716,1718],{"class":59,"line":229},[57,1713,108],{"class":107},[57,1715,783],{"class":111},[57,1717,115],{"class":107},[57,1719,1150],{"class":111},[57,1721,1722,1724,1726,1728],{"class":59,"line":234},[57,1723,1630],{"class":111},[57,1725,1633],{"class":568},[57,1727,380],{"class":107},[57,1729,1730],{"class":118}," 2\n",[57,1732,1733,1735,1737,1739,1742],{"class":59,"line":240},[57,1734,652],{"class":107},[57,1736,864],{"class":568},[57,1738,867],{"class":111},[57,1740,1741],{"class":568},"use_a",[57,1743,555],{"class":111},[57,1745,1746],{"class":59,"line":258},[57,1747,1748],{"class":63},"  -- SAFE: defer the access to call time (A is fully loaded by now)\n",[57,1750,1751,1753,1756,1758],{"class":59,"line":279},[57,1752,560],{"class":107},[57,1754,1755],{"class":111}," A.",[57,1757,1633],{"class":568},[57,1759,1760],{"class":63},"  -- A is the complete module at call time\n",[57,1762,1763],{"class":59,"line":435},[57,1764,226],{"class":107},[57,1766,1767,1769],{"class":59,"line":441},[57,1768,579],{"class":107},[57,1770,1548],{"class":111},[760,1772,1774],{"id":1773},"anti-pattern-accessing-circular-dep-at-module-load-time","Anti-pattern: accessing circular dep at module load time",[45,1776,1777,1900,1904],{"language":47},[49,1778,1780],{"className":51,"code":1779,"language":47,"meta":53,"style":53},"-- BAD: b.lua accesses A at load time — A isn't ready\nlocal A = require(\"a\")\nlocal M = {}\nM.cached_a_value = A.value  -- ERROR: A is nil or incomplete at load time\nreturn M\n\n-- GOOD: defer to function call time\nlocal A = require(\"a\")\nlocal M = {}\nfunction M.get_a_value() return A.value end  -- A is complete by call time\nreturn M\n",[24,1781,1782,1787,1803,1813,1829,1835,1839,1844,1860,1870,1894],{"__ignoreMap":53},[57,1783,1784],{"class":59,"line":60},[57,1785,1786],{"class":63},"-- BAD: b.lua accesses A at load time — A isn't ready\n",[57,1788,1789,1791,1793,1795,1797,1799,1801],{"class":59,"line":67},[57,1790,108],{"class":107},[57,1792,1685],{"class":111},[57,1794,115],{"class":107},[57,1796,119],{"class":118},[57,1798,122],{"class":111},[57,1800,1694],{"class":125},[57,1802,423],{"class":111},[57,1804,1805,1807,1809,1811],{"class":59,"line":73},[57,1806,108],{"class":107},[57,1808,783],{"class":111},[57,1810,115],{"class":107},[57,1812,1150],{"class":111},[57,1814,1815,1817,1820,1822,1824,1826],{"class":59,"line":79},[57,1816,1630],{"class":111},[57,1818,1819],{"class":568},"cached_a_value",[57,1821,380],{"class":107},[57,1823,1755],{"class":111},[57,1825,1633],{"class":568},[57,1827,1828],{"class":63},"  -- ERROR: A is nil or incomplete at load time\n",[57,1830,1831,1833],{"class":59,"line":85},[57,1832,579],{"class":107},[57,1834,1548],{"class":111},[57,1836,1837],{"class":59,"line":91},[57,1838,101],{"emptyLinePlaceholder":100},[57,1840,1841],{"class":59,"line":97},[57,1842,1843],{"class":63},"-- GOOD: defer to function call time\n",[57,1845,1846,1848,1850,1852,1854,1856,1858],{"class":59,"line":104},[57,1847,108],{"class":107},[57,1849,1685],{"class":111},[57,1851,115],{"class":107},[57,1853,119],{"class":118},[57,1855,122],{"class":111},[57,1857,1694],{"class":125},[57,1859,423],{"class":111},[57,1861,1862,1864,1866,1868],{"class":59,"line":135},[57,1863,108],{"class":107},[57,1865,783],{"class":111},[57,1867,115],{"class":107},[57,1869,1150],{"class":111},[57,1871,1872,1874,1876,1878,1881,1883,1885,1887,1889,1891],{"class":59,"line":157},[57,1873,652],{"class":107},[57,1875,864],{"class":568},[57,1877,867],{"class":111},[57,1879,1880],{"class":568},"get_a_value",[57,1882,1047],{"class":111},[57,1884,579],{"class":107},[57,1886,1755],{"class":111},[57,1888,1633],{"class":568},[57,1890,693],{"class":107},[57,1892,1893],{"class":63},"  -- A is complete by call time\n",[57,1895,1896,1898],{"class":59,"line":175},[57,1897,579],{"class":107},[57,1899,1548],{"class":111},[760,1901,1903],{"id":1902},"pattern-lazy-require-defer-the-require-itself","Pattern: lazy require (defer the require itself)",[45,1905,1906],{"language":47},[49,1907,1909],{"className":51,"code":1908,"language":47,"meta":53,"style":53},"-- If even the require order is problematic, defer the require to call time\nlocal M = {}\nfunction M.use_a()\n  local A = require(\"a\")  -- require inside function — runs at call, not load\n  return A.value\nend\nreturn M\n",[24,1910,1911,1916,1926,1938,1957,1966,1970],{"__ignoreMap":53},[57,1912,1913],{"class":59,"line":60},[57,1914,1915],{"class":63},"-- If even the require order is problematic, defer the require to call time\n",[57,1917,1918,1920,1922,1924],{"class":59,"line":67},[57,1919,108],{"class":107},[57,1921,783],{"class":111},[57,1923,115],{"class":107},[57,1925,1150],{"class":111},[57,1927,1928,1930,1932,1934,1936],{"class":59,"line":73},[57,1929,652],{"class":107},[57,1931,864],{"class":568},[57,1933,867],{"class":111},[57,1935,1741],{"class":568},[57,1937,555],{"class":111},[57,1939,1940,1942,1944,1946,1948,1950,1952,1954],{"class":59,"line":79},[57,1941,660],{"class":107},[57,1943,1685],{"class":111},[57,1945,115],{"class":107},[57,1947,119],{"class":118},[57,1949,122],{"class":111},[57,1951,1694],{"class":125},[57,1953,621],{"class":111},[57,1955,1956],{"class":63},"-- require inside function — runs at call, not load\n",[57,1958,1959,1961,1963],{"class":59,"line":85},[57,1960,560],{"class":107},[57,1962,1755],{"class":111},[57,1964,1965],{"class":568},"value\n",[57,1967,1968],{"class":59,"line":91},[57,1969,226],{"class":107},[57,1971,1972,1974],{"class":59,"line":97},[57,1973,579],{"class":107},[57,1975,1548],{"class":111},[37,1977,1979],{"id":1978},"hot-reload","Hot-Reload",[45,1981,1982,2182,2189],{"language":47},[49,1983,1985],{"className":51,"code":1984,"language":47,"meta":53,"style":53},"-- Evict from cache + re-require: gets a fresh module, preserving state externally\nlocal function reload(name, state_transfer)\n  local old = package.loaded[name]\n  package.loaded[name] = nil  -- evict\n  local new = require(name)   -- re-execute (fresh module table)\n  if state_transfer then state_transfer(old, new) end\n  return new, old\nend\n\n-- Usage: reload game system, preserving player score\nlocal GameState = require(\"game.state\")\nGameState = reload(\"game.state\", function(old, new)\n  new.player_score = old.player_score  -- transfer state to new module\n  new.player_name = old.player_name\nend)\n\n-- WARNING: side effects in module body run AGAIN on reload\n-- If module registers callbacks at load time, you get double registration\n-- Solution: provide an init() function called explicitly, not at load time\n",[24,1986,1987,1992,2003,2018,2033,2050,2067,2074,2078,2082,2087,2105,2125,2143,2157,2163,2167,2172,2177],{"__ignoreMap":53},[57,1988,1989],{"class":59,"line":60},[57,1990,1991],{"class":63},"-- Evict from cache + re-require: gets a fresh module, preserving state externally\n",[57,1993,1994,1996,1998,2000],{"class":59,"line":67},[57,1995,108],{"class":107},[57,1997,552],{"class":107},[57,1999,1497],{"class":568},[57,2001,2002],{"class":111},"(name, state_transfer)\n",[57,2004,2005,2007,2010,2012,2015],{"class":59,"line":73},[57,2006,660],{"class":107},[57,2008,2009],{"class":111}," old ",[57,2011,115],{"class":107},[57,2013,2014],{"class":118}," package.loaded",[57,2016,2017],{"class":111},"[name]\n",[57,2019,2020,2023,2026,2028,2030],{"class":59,"line":79},[57,2021,2022],{"class":118},"  package.loaded",[57,2024,2025],{"class":111},"[name] ",[57,2027,115],{"class":107},[57,2029,690],{"class":118},[57,2031,2032],{"class":63},"  -- evict\n",[57,2034,2035,2037,2040,2042,2044,2047],{"class":59,"line":85},[57,2036,660],{"class":107},[57,2038,2039],{"class":111}," new ",[57,2041,115],{"class":107},[57,2043,119],{"class":118},[57,2045,2046],{"class":111},"(name)   ",[57,2048,2049],{"class":63},"-- re-execute (fresh module table)\n",[57,2051,2052,2054,2057,2059,2062,2065],{"class":59,"line":91},[57,2053,676],{"class":107},[57,2055,2056],{"class":111}," state_transfer ",[57,2058,684],{"class":107},[57,2060,2061],{"class":118}," state_transfer",[57,2063,2064],{"class":111},"(old, new) ",[57,2066,226],{"class":107},[57,2068,2069,2071],{"class":59,"line":97},[57,2070,560],{"class":107},[57,2072,2073],{"class":111}," new, old\n",[57,2075,2076],{"class":59,"line":104},[57,2077,226],{"class":107},[57,2079,2080],{"class":59,"line":135},[57,2081,101],{"emptyLinePlaceholder":100},[57,2083,2084],{"class":59,"line":157},[57,2085,2086],{"class":63},"-- Usage: reload game system, preserving player score\n",[57,2088,2089,2091,2094,2096,2098,2100,2103],{"class":59,"line":175},[57,2090,108],{"class":107},[57,2092,2093],{"class":111}," GameState ",[57,2095,115],{"class":107},[57,2097,119],{"class":118},[57,2099,122],{"class":111},[57,2101,2102],{"class":125},"\"game.state\"",[57,2104,423],{"class":111},[57,2106,2107,2110,2112,2114,2116,2118,2120,2122],{"class":59,"line":180},[57,2108,2109],{"class":111},"GameState ",[57,2111,115],{"class":107},[57,2113,1497],{"class":118},[57,2115,122],{"class":111},[57,2117,2102],{"class":125},[57,2119,418],{"class":111},[57,2121,652],{"class":107},[57,2123,2124],{"class":111},"(old, new)\n",[57,2126,2127,2130,2133,2135,2138,2140],{"class":59,"line":186},[57,2128,2129],{"class":111},"  new.",[57,2131,2132],{"class":568},"player_score",[57,2134,380],{"class":107},[57,2136,2137],{"class":111}," old.",[57,2139,2132],{"class":568},[57,2141,2142],{"class":63},"  -- transfer state to new module\n",[57,2144,2145,2147,2150,2152,2154],{"class":59,"line":211},[57,2146,2129],{"class":111},[57,2148,2149],{"class":568},"player_name",[57,2151,380],{"class":107},[57,2153,2137],{"class":111},[57,2155,2156],{"class":568},"player_name\n",[57,2158,2159,2161],{"class":59,"line":223},[57,2160,591],{"class":107},[57,2162,423],{"class":111},[57,2164,2165],{"class":59,"line":229},[57,2166,101],{"emptyLinePlaceholder":100},[57,2168,2169],{"class":59,"line":234},[57,2170,2171],{"class":63},"-- WARNING: side effects in module body run AGAIN on reload\n",[57,2173,2174],{"class":59,"line":240},[57,2175,2176],{"class":63},"-- If module registers callbacks at load time, you get double registration\n",[57,2178,2179],{"class":59,"line":258},[57,2180,2181],{"class":63},"-- Solution: provide an init() function called explicitly, not at load time\n",[37,2183,2185,2186],{"id":2184},"package-structure-initlua","Package Structure & ",[24,2187,2188],{},"init.lua",[45,2190,2191,2390,2397],{"language":47},[49,2192,2194],{"className":51,"code":2193,"language":47,"meta":53,"style":53},"-- Directory structure:\n-- mylib\u002F\n--   init.lua        → require(\"mylib\") finds this via ?\u002Finit.lua path\n--   utils.lua      → require(\"mylib.utils\")\n--   models\u002F\n--     user.lua     → require(\"mylib.models.user\")\n--     post.lua     → require(\"mylib.models.post\")\n\n-- mylib\u002Finit.lua: the package entry point, re-exports submodules\nreturn {\n  utils  = require(\"mylib.utils\"),\n  models = {\n    user = require(\"mylib.models.user\"),\n    post = require(\"mylib.models.post\"),\n  },\n}\n\n-- Usage:\nlocal mylib = require(\"mylib\")\nmylib.utils.trim(\"  hello  \")\nlocal user = mylib.models.user.new(\"Alice\")\n",[24,2195,2196,2201,2206,2211,2216,2221,2226,2231,2235,2240,2246,2262,2271,2287,2303,2308,2312,2316,2321,2339,2359],{"__ignoreMap":53},[57,2197,2198],{"class":59,"line":60},[57,2199,2200],{"class":63},"-- Directory structure:\n",[57,2202,2203],{"class":59,"line":67},[57,2204,2205],{"class":63},"-- mylib\u002F\n",[57,2207,2208],{"class":59,"line":73},[57,2209,2210],{"class":63},"--   init.lua        → require(\"mylib\") finds this via ?\u002Finit.lua path\n",[57,2212,2213],{"class":59,"line":79},[57,2214,2215],{"class":63},"--   utils.lua      → require(\"mylib.utils\")\n",[57,2217,2218],{"class":59,"line":85},[57,2219,2220],{"class":63},"--   models\u002F\n",[57,2222,2223],{"class":59,"line":91},[57,2224,2225],{"class":63},"--     user.lua     → require(\"mylib.models.user\")\n",[57,2227,2228],{"class":59,"line":97},[57,2229,2230],{"class":63},"--     post.lua     → require(\"mylib.models.post\")\n",[57,2232,2233],{"class":59,"line":104},[57,2234,101],{"emptyLinePlaceholder":100},[57,2236,2237],{"class":59,"line":135},[57,2238,2239],{"class":63},"-- mylib\u002Finit.lua: the package entry point, re-exports submodules\n",[57,2241,2242,2244],{"class":59,"line":157},[57,2243,579],{"class":107},[57,2245,563],{"class":111},[57,2247,2248,2251,2253,2255,2257,2259],{"class":59,"line":175},[57,2249,2250],{"class":111},"  utils  ",[57,2252,115],{"class":107},[57,2254,119],{"class":118},[57,2256,122],{"class":111},[57,2258,415],{"class":125},[57,2260,2261],{"class":111},"),\n",[57,2263,2264,2267,2269],{"class":59,"line":180},[57,2265,2266],{"class":111},"  models ",[57,2268,115],{"class":107},[57,2270,563],{"class":111},[57,2272,2273,2276,2278,2280,2282,2285],{"class":59,"line":186},[57,2274,2275],{"class":111},"    user ",[57,2277,115],{"class":107},[57,2279,119],{"class":118},[57,2281,122],{"class":111},[57,2283,2284],{"class":125},"\"mylib.models.user\"",[57,2286,2261],{"class":111},[57,2288,2289,2292,2294,2296,2298,2301],{"class":59,"line":211},[57,2290,2291],{"class":111},"    post ",[57,2293,115],{"class":107},[57,2295,119],{"class":118},[57,2297,122],{"class":111},[57,2299,2300],{"class":125},"\"mylib.models.post\"",[57,2302,2261],{"class":111},[57,2304,2305],{"class":59,"line":223},[57,2306,2307],{"class":111},"  },\n",[57,2309,2310],{"class":59,"line":229},[57,2311,1116],{"class":111},[57,2313,2314],{"class":59,"line":234},[57,2315,101],{"emptyLinePlaceholder":100},[57,2317,2318],{"class":59,"line":240},[57,2319,2320],{"class":63},"-- Usage:\n",[57,2322,2323,2325,2328,2330,2332,2334,2337],{"class":59,"line":258},[57,2324,108],{"class":107},[57,2326,2327],{"class":111}," mylib ",[57,2329,115],{"class":107},[57,2331,119],{"class":118},[57,2333,122],{"class":111},[57,2335,2336],{"class":125},"\"mylib\"",[57,2338,423],{"class":111},[57,2340,2341,2344,2347,2349,2352,2354,2357],{"class":59,"line":279},[57,2342,2343],{"class":111},"mylib.",[57,2345,2346],{"class":568},"utils",[57,2348,867],{"class":111},[57,2350,2351],{"class":118},"trim",[57,2353,122],{"class":111},[57,2355,2356],{"class":125},"\"  hello  \"",[57,2358,423],{"class":111},[57,2360,2361,2363,2366,2368,2371,2374,2376,2379,2381,2383,2385,2388],{"class":59,"line":435},[57,2362,108],{"class":107},[57,2364,2365],{"class":111}," user ",[57,2367,115],{"class":107},[57,2369,2370],{"class":111}," mylib.",[57,2372,2373],{"class":568},"models",[57,2375,867],{"class":111},[57,2377,2378],{"class":568},"user",[57,2380,867],{"class":111},[57,2382,1179],{"class":118},[57,2384,122],{"class":111},[57,2386,2387],{"class":125},"\"Alice\"",[57,2389,423],{"class":111},[37,2391,2393,2396],{"id":2392},"luarocks-package-manager",[24,2394,2395],{},"luarocks"," — Package Manager",[45,2398,2400,2548],{"language":2399},"bash",[49,2401,2404],{"className":2402,"code":2403,"language":2399,"meta":53,"style":53},"language-bash shiki shiki-themes github-light github-dark","# Install luarocks\nbrew install luarocks        # macOS\n\n# Install packages (into the system Lua tree)\nluarocks install lua-cjson   # C JSON encoder\u002Fdecoder\nluarocks install luasocket   # networking\nluarocks install lpeg        # parsing expression grammars\nluarocks install luaunit     # testing framework\nluarocks install luacheck    # static analyzer\n\n# Project-local installation (doesn't pollute system)\nluarocks install --local lua-cjson  # → ~\u002F.luarocks\u002F\n\n# Create a rockspec (package descriptor)\nluarocks new-mod  -- generates a template rockspec\n",[24,2405,2406,2411,2425,2429,2434,2446,2458,2470,2482,2494,2498,2503,2517,2521,2526],{"__ignoreMap":53},[57,2407,2408],{"class":59,"line":60},[57,2409,2410],{"class":63},"# Install luarocks\n",[57,2412,2413,2416,2419,2422],{"class":59,"line":67},[57,2414,2415],{"class":568},"brew",[57,2417,2418],{"class":125}," install",[57,2420,2421],{"class":125}," luarocks",[57,2423,2424],{"class":63},"        # macOS\n",[57,2426,2427],{"class":59,"line":73},[57,2428,101],{"emptyLinePlaceholder":100},[57,2430,2431],{"class":59,"line":79},[57,2432,2433],{"class":63},"# Install packages (into the system Lua tree)\n",[57,2435,2436,2438,2440,2443],{"class":59,"line":85},[57,2437,2395],{"class":568},[57,2439,2418],{"class":125},[57,2441,2442],{"class":125}," lua-cjson",[57,2444,2445],{"class":63},"   # C JSON encoder\u002Fdecoder\n",[57,2447,2448,2450,2452,2455],{"class":59,"line":91},[57,2449,2395],{"class":568},[57,2451,2418],{"class":125},[57,2453,2454],{"class":125}," luasocket",[57,2456,2457],{"class":63},"   # networking\n",[57,2459,2460,2462,2464,2467],{"class":59,"line":97},[57,2461,2395],{"class":568},[57,2463,2418],{"class":125},[57,2465,2466],{"class":125}," lpeg",[57,2468,2469],{"class":63},"        # parsing expression grammars\n",[57,2471,2472,2474,2476,2479],{"class":59,"line":104},[57,2473,2395],{"class":568},[57,2475,2418],{"class":125},[57,2477,2478],{"class":125}," luaunit",[57,2480,2481],{"class":63},"     # testing framework\n",[57,2483,2484,2486,2488,2491],{"class":59,"line":135},[57,2485,2395],{"class":568},[57,2487,2418],{"class":125},[57,2489,2490],{"class":125}," luacheck",[57,2492,2493],{"class":63},"    # static analyzer\n",[57,2495,2496],{"class":59,"line":157},[57,2497,101],{"emptyLinePlaceholder":100},[57,2499,2500],{"class":59,"line":175},[57,2501,2502],{"class":63},"# Project-local installation (doesn't pollute system)\n",[57,2504,2505,2507,2509,2512,2514],{"class":59,"line":180},[57,2506,2395],{"class":568},[57,2508,2418],{"class":125},[57,2510,2511],{"class":118}," --local",[57,2513,2442],{"class":125},[57,2515,2516],{"class":63},"  # → ~\u002F.luarocks\u002F\n",[57,2518,2519],{"class":59,"line":186},[57,2520,101],{"emptyLinePlaceholder":100},[57,2522,2523],{"class":59,"line":211},[57,2524,2525],{"class":63},"# Create a rockspec (package descriptor)\n",[57,2527,2528,2530,2533,2536,2539,2542,2545],{"class":59,"line":223},[57,2529,2395],{"class":568},[57,2531,2532],{"class":125}," new-mod",[57,2534,2535],{"class":118},"  --",[57,2537,2538],{"class":125}," generates",[57,2540,2541],{"class":125}," a",[57,2543,2544],{"class":125}," template",[57,2546,2547],{"class":125}," rockspec\n",[45,2549,2551],{"language":47,"filename":2550},"myproject-1.0-1.rockspec",[49,2552,2554],{"className":51,"code":2553,"language":47,"meta":53,"style":53},"-- rockspec: declarative package metadata for luarocks\npackage = \"myproject\"\nversion = \"1.0-1\"\nsource = { url = \"git+https:\u002F\u002Fgithub.com\u002Fme\u002Fmyproject.git\" }\ndescription = { summary = \"A Lua project\", license = \"MIT\" }\ndependencies = {\n  \"lua >= 5.3\",\n  \"lua-cjson >= 2.1\",\n  \"luasocket\",\n}\nbuild = {\n  type = \"builtin\",\n  modules = {\n    myproject = \"src\u002Fmyproject.lua\",\n    [\"myproject.utils\"] = \"src\u002Fmyproject\u002Futils.lua\",\n  },\n}\n",[24,2555,2556,2561,2571,2581,2598,2623,2632,2639,2646,2653,2657,2666,2678,2687,2699,2716,2720],{"__ignoreMap":53},[57,2557,2558],{"class":59,"line":60},[57,2559,2560],{"class":63},"-- rockspec: declarative package metadata for luarocks\n",[57,2562,2563,2566,2568],{"class":59,"line":67},[57,2564,2565],{"class":111},"package ",[57,2567,115],{"class":107},[57,2569,2570],{"class":125}," \"myproject\"\n",[57,2572,2573,2576,2578],{"class":59,"line":73},[57,2574,2575],{"class":111},"version ",[57,2577,115],{"class":107},[57,2579,2580],{"class":125}," \"1.0-1\"\n",[57,2582,2583,2586,2588,2591,2593,2596],{"class":59,"line":79},[57,2584,2585],{"class":111},"source ",[57,2587,115],{"class":107},[57,2589,2590],{"class":111}," { url ",[57,2592,115],{"class":107},[57,2594,2595],{"class":125}," \"git+https:\u002F\u002Fgithub.com\u002Fme\u002Fmyproject.git\" ",[57,2597,1116],{"class":111},[57,2599,2600,2603,2605,2608,2610,2613,2616,2618,2621],{"class":59,"line":85},[57,2601,2602],{"class":111},"description ",[57,2604,115],{"class":107},[57,2606,2607],{"class":111}," { summary ",[57,2609,115],{"class":107},[57,2611,2612],{"class":125}," \"A Lua project\"",[57,2614,2615],{"class":111},", license ",[57,2617,115],{"class":107},[57,2619,2620],{"class":125}," \"MIT\" ",[57,2622,1116],{"class":111},[57,2624,2625,2628,2630],{"class":59,"line":91},[57,2626,2627],{"class":111},"dependencies ",[57,2629,115],{"class":107},[57,2631,563],{"class":111},[57,2633,2634,2637],{"class":59,"line":97},[57,2635,2636],{"class":125},"  \"lua >= 5.3\"",[57,2638,594],{"class":111},[57,2640,2641,2644],{"class":59,"line":104},[57,2642,2643],{"class":125},"  \"lua-cjson >= 2.1\"",[57,2645,594],{"class":111},[57,2647,2648,2651],{"class":59,"line":135},[57,2649,2650],{"class":125},"  \"luasocket\"",[57,2652,594],{"class":111},[57,2654,2655],{"class":59,"line":157},[57,2656,1116],{"class":111},[57,2658,2659,2662,2664],{"class":59,"line":175},[57,2660,2661],{"class":111},"build ",[57,2663,115],{"class":107},[57,2665,563],{"class":111},[57,2667,2668,2671,2673,2676],{"class":59,"line":180},[57,2669,2670],{"class":111},"  type ",[57,2672,115],{"class":107},[57,2674,2675],{"class":125}," \"builtin\"",[57,2677,594],{"class":111},[57,2679,2680,2683,2685],{"class":59,"line":186},[57,2681,2682],{"class":111},"  modules ",[57,2684,115],{"class":107},[57,2686,563],{"class":111},[57,2688,2689,2692,2694,2697],{"class":59,"line":211},[57,2690,2691],{"class":111},"    myproject ",[57,2693,115],{"class":107},[57,2695,2696],{"class":125}," \"src\u002Fmyproject.lua\"",[57,2698,594],{"class":111},[57,2700,2701,2704,2707,2709,2711,2714],{"class":59,"line":223},[57,2702,2703],{"class":111},"    [",[57,2705,2706],{"class":125},"\"myproject.utils\"",[57,2708,250],{"class":111},[57,2710,115],{"class":107},[57,2712,2713],{"class":125}," \"src\u002Fmyproject\u002Futils.lua\"",[57,2715,594],{"class":111},[57,2717,2718],{"class":59,"line":229},[57,2719,2307],{"class":111},[57,2721,2722],{"class":59,"line":234},[57,2723,1116],{"class":111},[37,2725,2727],{"id":2726},"production-dependency-injection","Production: Dependency Injection",[45,2729,2730,3035,3039,3048,3144,3150,3241,3250,3277,3281,3290,3375,3395,3446,3465,3476,3480],{"language":47},[49,2731,2733],{"className":51,"code":2732,"language":47,"meta":53,"style":53},"-- Instead of require() at module load, accept dependencies as parameters\n-- This makes modules testable (pass mocks) and decoupled\n\n-- BAD: hard dependency, untestable\nlocal http = require(\"luasocket.http\")  -- can't mock for tests\nlocal M = {}\nfunction M.fetch_user(id)\n  return http.request(\"http:\u002F\u002Fapi\u002Fusers\u002F\" .. id)  -- hard to test\nend\nreturn M\n\n-- GOOD: dependency injection\nlocal M = {}\nfunction M.new(deps)\n  local http = deps.http or require(\"luasocket.http\")  -- inject or default\n  return {\n    fetch_user = function(id) return http.request(\"http:\u002F\u002Fapi\u002Fusers\u002F\" .. id) end,\n  }\nend\nreturn M\n\n-- Test: inject a mock http\nlocal mock_http = { request = function(url) return \"mock: \" .. url end }\nlocal client = require(\"myclient\").new({ http = mock_http })\nprint(client.fetch_user(42))  -- \"mock: http:\u002F\u002Fapi\u002Fusers\u002F42\"\n",[24,2734,2735,2740,2745,2749,2754,2774,2784,2798,2821,2825,2831,2835,2840,2850,2863,2891,2897,2928,2932,2936,2942,2946,2951,2985,3014],{"__ignoreMap":53},[57,2736,2737],{"class":59,"line":60},[57,2738,2739],{"class":63},"-- Instead of require() at module load, accept dependencies as parameters\n",[57,2741,2742],{"class":59,"line":67},[57,2743,2744],{"class":63},"-- This makes modules testable (pass mocks) and decoupled\n",[57,2746,2747],{"class":59,"line":73},[57,2748,101],{"emptyLinePlaceholder":100},[57,2750,2751],{"class":59,"line":79},[57,2752,2753],{"class":63},"-- BAD: hard dependency, untestable\n",[57,2755,2756,2758,2760,2762,2764,2766,2769,2771],{"class":59,"line":85},[57,2757,108],{"class":107},[57,2759,610],{"class":111},[57,2761,115],{"class":107},[57,2763,119],{"class":118},[57,2765,122],{"class":111},[57,2767,2768],{"class":125},"\"luasocket.http\"",[57,2770,621],{"class":111},[57,2772,2773],{"class":63},"-- can't mock for tests\n",[57,2775,2776,2778,2780,2782],{"class":59,"line":91},[57,2777,108],{"class":107},[57,2779,783],{"class":111},[57,2781,115],{"class":107},[57,2783,1150],{"class":111},[57,2785,2786,2788,2790,2792,2795],{"class":59,"line":97},[57,2787,652],{"class":107},[57,2789,864],{"class":568},[57,2791,867],{"class":111},[57,2793,2794],{"class":568},"fetch_user",[57,2796,2797],{"class":111},"(id)\n",[57,2799,2800,2802,2805,2808,2810,2813,2815,2818],{"class":59,"line":104},[57,2801,560],{"class":107},[57,2803,2804],{"class":111}," http.",[57,2806,2807],{"class":118},"request",[57,2809,122],{"class":111},[57,2811,2812],{"class":125},"\"http:\u002F\u002Fapi\u002Fusers\u002F\" ",[57,2814,585],{"class":107},[57,2816,2817],{"class":111}," id)  ",[57,2819,2820],{"class":63},"-- hard to test\n",[57,2822,2823],{"class":59,"line":135},[57,2824,226],{"class":107},[57,2826,2827,2829],{"class":59,"line":157},[57,2828,579],{"class":107},[57,2830,1548],{"class":111},[57,2832,2833],{"class":59,"line":175},[57,2834,101],{"emptyLinePlaceholder":100},[57,2836,2837],{"class":59,"line":180},[57,2838,2839],{"class":63},"-- GOOD: dependency injection\n",[57,2841,2842,2844,2846,2848],{"class":59,"line":186},[57,2843,108],{"class":107},[57,2845,783],{"class":111},[57,2847,115],{"class":107},[57,2849,1150],{"class":111},[57,2851,2852,2854,2856,2858,2860],{"class":59,"line":211},[57,2853,652],{"class":107},[57,2855,864],{"class":568},[57,2857,867],{"class":111},[57,2859,1179],{"class":568},[57,2861,2862],{"class":111},"(deps)\n",[57,2864,2865,2867,2869,2871,2874,2877,2880,2882,2884,2886,2888],{"class":59,"line":223},[57,2866,660],{"class":107},[57,2868,610],{"class":111},[57,2870,115],{"class":107},[57,2872,2873],{"class":111}," deps.",[57,2875,2876],{"class":568},"http",[57,2878,2879],{"class":107}," or",[57,2881,119],{"class":118},[57,2883,122],{"class":111},[57,2885,2768],{"class":125},[57,2887,621],{"class":111},[57,2889,2890],{"class":63},"-- inject or default\n",[57,2892,2893,2895],{"class":59,"line":229},[57,2894,560],{"class":107},[57,2896,563],{"class":111},[57,2898,2899,2902,2904,2906,2909,2911,2913,2915,2917,2919,2921,2924,2926],{"class":59,"line":234},[57,2900,2901],{"class":568},"    fetch_user",[57,2903,380],{"class":107},[57,2905,552],{"class":107},[57,2907,2908],{"class":111},"(id) ",[57,2910,579],{"class":107},[57,2912,2804],{"class":111},[57,2914,2807],{"class":118},[57,2916,122],{"class":111},[57,2918,2812],{"class":125},[57,2920,585],{"class":107},[57,2922,2923],{"class":111}," id) ",[57,2925,591],{"class":107},[57,2927,594],{"class":111},[57,2929,2930],{"class":59,"line":240},[57,2931,599],{"class":111},[57,2933,2934],{"class":59,"line":258},[57,2935,226],{"class":107},[57,2937,2938,2940],{"class":59,"line":279},[57,2939,579],{"class":107},[57,2941,1548],{"class":111},[57,2943,2944],{"class":59,"line":435},[57,2945,101],{"emptyLinePlaceholder":100},[57,2947,2948],{"class":59,"line":441},[57,2949,2950],{"class":63},"-- Test: inject a mock http\n",[57,2952,2953,2955,2958,2960,2963,2965,2967,2969,2971,2973,2976,2978,2980,2982],{"class":59,"line":474},[57,2954,108],{"class":107},[57,2956,2957],{"class":111}," mock_http ",[57,2959,115],{"class":107},[57,2961,2962],{"class":111}," { ",[57,2964,2807],{"class":568},[57,2966,380],{"class":107},[57,2968,552],{"class":107},[57,2970,576],{"class":111},[57,2972,579],{"class":107},[57,2974,2975],{"class":125}," \"mock: \" ",[57,2977,585],{"class":107},[57,2979,588],{"class":111},[57,2981,591],{"class":107},[57,2983,2984],{"class":111}," }\n",[57,2986,2987,2989,2992,2994,2996,2998,3001,3004,3006,3009,3011],{"class":59,"line":750},[57,2988,108],{"class":107},[57,2990,2991],{"class":111}," client ",[57,2993,115],{"class":107},[57,2995,119],{"class":118},[57,2997,122],{"class":111},[57,2999,3000],{"class":125},"\"myclient\"",[57,3002,3003],{"class":111},").",[57,3005,1179],{"class":118},[57,3007,3008],{"class":111},"({ http ",[57,3010,115],{"class":107},[57,3012,3013],{"class":111}," mock_http })\n",[57,3015,3017,3019,3022,3024,3026,3029,3032],{"class":59,"line":3016},25,[57,3018,160],{"class":118},[57,3020,3021],{"class":111},"(client.",[57,3023,2794],{"class":118},[57,3025,122],{"class":111},[57,3027,3028],{"class":118},"42",[57,3030,3031],{"class":111},"))  ",[57,3033,3034],{"class":63},"-- \"mock: http:\u002F\u002Fapi\u002Fusers\u002F42\"\n",[37,3036,3038],{"id":3037},"tips-tricks","💡 Tips & Tricks",[15,3040,3041,3047],{},[19,3042,3043,3044,3046],{},"Use ",[24,3045,540],{}," for testing",": Register mock modules before requiring the module under test.",[45,3049,3050],{"language":47},[49,3051,3053],{"className":51,"code":3052,"language":47,"meta":53,"style":53},"-- test.lua\npackage.preload[\"db\"] = function()\n  return {\n    query = function(sql) return {\"mock\", \"rows\"} end,\n  }\nend\nlocal app = require(\"app\")  -- app requires \"db\" → gets the mock\n",[24,3054,3055,3060,3077,3083,3115,3119,3123],{"__ignoreMap":53},[57,3056,3057],{"class":59,"line":60},[57,3058,3059],{"class":63},"-- test.lua\n",[57,3061,3062,3064,3066,3069,3071,3073,3075],{"class":59,"line":67},[57,3063,540],{"class":118},[57,3065,245],{"class":111},[57,3067,3068],{"class":125},"\"db\"",[57,3070,250],{"class":111},[57,3072,115],{"class":107},[57,3074,552],{"class":107},[57,3076,555],{"class":111},[57,3078,3079,3081],{"class":59,"line":73},[57,3080,560],{"class":107},[57,3082,563],{"class":111},[57,3084,3085,3088,3090,3092,3095,3097,3100,3103,3105,3108,3111,3113],{"class":59,"line":79},[57,3086,3087],{"class":568},"    query",[57,3089,380],{"class":107},[57,3091,552],{"class":107},[57,3093,3094],{"class":111},"(sql) ",[57,3096,579],{"class":107},[57,3098,3099],{"class":111}," {",[57,3101,3102],{"class":125},"\"mock\"",[57,3104,418],{"class":111},[57,3106,3107],{"class":125},"\"rows\"",[57,3109,3110],{"class":111},"} ",[57,3112,591],{"class":107},[57,3114,594],{"class":111},[57,3116,3117],{"class":59,"line":85},[57,3118,599],{"class":111},[57,3120,3121],{"class":59,"line":91},[57,3122,226],{"class":107},[57,3124,3125,3127,3130,3132,3134,3136,3139,3141],{"class":59,"line":97},[57,3126,108],{"class":107},[57,3128,3129],{"class":111}," app ",[57,3131,115],{"class":107},[57,3133,119],{"class":118},[57,3135,122],{"class":111},[57,3137,3138],{"class":125},"\"app\"",[57,3140,621],{"class":111},[57,3142,3143],{"class":63},"-- app requires \"db\" → gets the mock\n",[15,3145,3146,3149],{},[19,3147,3148],{},"Lazy-load expensive modules",": Defer require to first use for modules that may never be needed.",[45,3151,3152],{"language":47},[49,3153,3155],{"className":51,"code":3154,"language":47,"meta":53,"style":53},"local M = {}\nlocal json  -- not loaded yet\nfunction M.encode(data)\n  if not json then json = require(\"cjson\") end  -- load on first call\n  return json.encode(data)\nend\nreturn M\n",[24,3156,3157,3167,3177,3191,3220,3231,3235],{"__ignoreMap":53},[57,3158,3159,3161,3163,3165],{"class":59,"line":60},[57,3160,108],{"class":107},[57,3162,783],{"class":111},[57,3164,115],{"class":107},[57,3166,1150],{"class":111},[57,3168,3169,3171,3174],{"class":59,"line":67},[57,3170,108],{"class":107},[57,3172,3173],{"class":111}," json  ",[57,3175,3176],{"class":63},"-- not loaded yet\n",[57,3178,3179,3181,3183,3185,3188],{"class":59,"line":73},[57,3180,652],{"class":107},[57,3182,864],{"class":568},[57,3184,867],{"class":111},[57,3186,3187],{"class":568},"encode",[57,3189,3190],{"class":111},"(data)\n",[57,3192,3193,3195,3197,3200,3202,3204,3206,3208,3210,3213,3215,3217],{"class":59,"line":79},[57,3194,676],{"class":107},[57,3196,679],{"class":107},[57,3198,3199],{"class":111}," json ",[57,3201,684],{"class":107},[57,3203,3199],{"class":111},[57,3205,115],{"class":107},[57,3207,119],{"class":118},[57,3209,122],{"class":111},[57,3211,3212],{"class":125},"\"cjson\"",[57,3214,205],{"class":111},[57,3216,591],{"class":107},[57,3218,3219],{"class":63},"  -- load on first call\n",[57,3221,3222,3224,3227,3229],{"class":59,"line":85},[57,3223,560],{"class":107},[57,3225,3226],{"class":111}," json.",[57,3228,3187],{"class":118},[57,3230,3190],{"class":111},[57,3232,3233],{"class":59,"line":91},[57,3234,226],{"class":107},[57,3236,3237,3239],{"class":59,"line":97},[57,3238,579],{"class":107},[57,3240,1548],{"class":111},[15,3242,3243,3249],{},[19,3244,3245,3248],{},[24,3246,3247],{},"package.loaded[...]"," in a module to get its own name",": A module can discover its own name (5.2+).",[45,3251,3252],{"language":47},[49,3253,3255],{"className":51,"code":3254,"language":47,"meta":53,"style":53},"-- Inside a module chunk, `...` is the module name (for require'd modules)\nlocal module_name = ...  -- \"mylib.utils\" when require'd as that\n",[24,3256,3257,3262],{"__ignoreMap":53},[57,3258,3259],{"class":59,"line":60},[57,3260,3261],{"class":63},"-- Inside a module chunk, `...` is the module name (for require'd modules)\n",[57,3263,3264,3266,3269,3271,3274],{"class":59,"line":67},[57,3265,108],{"class":107},[57,3267,3268],{"class":111}," module_name ",[57,3270,115],{"class":107},[57,3272,3273],{"class":118}," ...",[57,3275,3276],{"class":63},"  -- \"mylib.utils\" when require'd as that\n",[37,3278,3280],{"id":3279},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[15,3282,3283,3289],{},[19,3284,3285,3288],{},[24,3286,3287],{},"require"," caches the return value, not the side effects",": If the module body registers callbacks (global side effects), evicting + re-requiring runs them again — can cause double-registration.",[45,3291,3292],{"language":47},[49,3293,3295],{"className":51,"code":3294,"language":47,"meta":53,"style":53},"-- hooks.lua\nhooks[\"on_tick\"] = function() print(\"tick\") end  -- side effect at load time\nreturn {}\n\n-- Evicting and re-requiring registers the hook AGAIN:\npackage.loaded[\"hooks\"] = nil\nrequire(\"hooks\")  -- hooks[\"on_tick\"] now registered twice → prints \"tick\" twice per tick\n",[24,3296,3297,3302,3332,3338,3342,3347,3362],{"__ignoreMap":53},[57,3298,3299],{"class":59,"line":60},[57,3300,3301],{"class":63},"-- hooks.lua\n",[57,3303,3304,3307,3310,3312,3314,3316,3318,3320,3322,3325,3327,3329],{"class":59,"line":67},[57,3305,3306],{"class":111},"hooks[",[57,3308,3309],{"class":125},"\"on_tick\"",[57,3311,250],{"class":111},[57,3313,115],{"class":107},[57,3315,552],{"class":107},[57,3317,1047],{"class":111},[57,3319,160],{"class":118},[57,3321,122],{"class":111},[57,3323,3324],{"class":125},"\"tick\"",[57,3326,205],{"class":111},[57,3328,591],{"class":107},[57,3330,3331],{"class":63},"  -- side effect at load time\n",[57,3333,3334,3336],{"class":59,"line":73},[57,3335,579],{"class":107},[57,3337,1150],{"class":111},[57,3339,3340],{"class":59,"line":79},[57,3341,101],{"emptyLinePlaceholder":100},[57,3343,3344],{"class":59,"line":85},[57,3345,3346],{"class":63},"-- Evicting and re-requiring registers the hook AGAIN:\n",[57,3348,3349,3351,3353,3356,3358,3360],{"class":59,"line":91},[57,3350,30],{"class":118},[57,3352,245],{"class":111},[57,3354,3355],{"class":125},"\"hooks\"",[57,3357,250],{"class":111},[57,3359,115],{"class":107},[57,3361,255],{"class":118},[57,3363,3364,3366,3368,3370,3372],{"class":59,"line":97},[57,3365,3287],{"class":118},[57,3367,122],{"class":111},[57,3369,3355],{"class":125},[57,3371,621],{"class":111},[57,3373,3374],{"class":63},"-- hooks[\"on_tick\"] now registered twice → prints \"tick\" twice per tick\n",[15,3376,3377,3384,3385,3387,3388,3391,3392,3394],{},[19,3378,3379,3380,3383],{},"Module returning ",[24,3381,3382],{},"nil"," or nothing",": ",[24,3386,3287],{}," caches ",[24,3389,3390],{},"true"," (not nil) when the module returns nothing. The module appears loaded but returns ",[24,3393,3390],{},", not the intended value.",[45,3396,3397],{"language":47},[49,3398,3400],{"className":51,"code":3399,"language":47,"meta":53,"style":53},"-- bad_module.lua\nlocal x = 1 + 1  -- no return statement\n-- require(\"bad_module\") → true (not nil!), subsequent requires return true\n\n-- Always return a table explicitly:\nreturn {}\n",[24,3401,3402,3407,3426,3431,3435,3440],{"__ignoreMap":53},[57,3403,3404],{"class":59,"line":60},[57,3405,3406],{"class":63},"-- bad_module.lua\n",[57,3408,3409,3411,3414,3416,3418,3421,3423],{"class":59,"line":67},[57,3410,108],{"class":107},[57,3412,3413],{"class":111}," x ",[57,3415,115],{"class":107},[57,3417,1026],{"class":118},[57,3419,3420],{"class":107}," +",[57,3422,1026],{"class":118},[57,3424,3425],{"class":63},"  -- no return statement\n",[57,3427,3428],{"class":59,"line":73},[57,3429,3430],{"class":63},"-- require(\"bad_module\") → true (not nil!), subsequent requires return true\n",[57,3432,3433],{"class":59,"line":79},[57,3434,101],{"emptyLinePlaceholder":100},[57,3436,3437],{"class":59,"line":85},[57,3438,3439],{"class":63},"-- Always return a table explicitly:\n",[57,3441,3442,3444],{"class":59,"line":91},[57,3443,579],{"class":107},[57,3445,1150],{"class":111},[15,3447,3448,3384,3453,3456,3457,3460,3461,3464],{},[19,3449,3450,3452],{},[24,3451,3287],{}," name must match the file path",[24,3454,3455],{},"require(\"utils\")"," looks for ",[24,3458,3459],{},"utils.lua",", NOT ",[24,3462,3463],{},"Utils.lua"," (case-sensitive on most systems).",[15,3466,3467,3472,3473,3475],{},[19,3468,3469,3471],{},[24,3470,377],{}," order matters",": First match wins. If two directories have ",[24,3474,3459],{},", the first in the path is loaded — can cause confusion when a local override is shadowed by a system module.",[37,3477,3479],{"id":3478},"spot-the-bug","🧠 Spot the Bug",[45,3481,3482,3613],{"language":47},[49,3483,3485],{"className":51,"code":3484,"language":47,"meta":53,"style":53},"-- a.lua\nlocal M = {}\nlocal helper = require(\"a.helper\")  -- requires a\u002Fhelper.lua\nfunction M.process(x) return helper.transform(x) end\nreturn M\n\n-- b.lua\nlocal A = require(\"a\")\nfunction M.use(x) return A.process(x) + 1 end\n\n-- What happens when b.lua is required first?\n",[24,3486,3487,3491,3501,3522,3547,3553,3557,3561,3577,3604,3608],{"__ignoreMap":53},[57,3488,3489],{"class":59,"line":60},[57,3490,1593],{"class":63},[57,3492,3493,3495,3497,3499],{"class":59,"line":67},[57,3494,108],{"class":107},[57,3496,783],{"class":111},[57,3498,115],{"class":107},[57,3500,1150],{"class":111},[57,3502,3503,3505,3508,3510,3512,3514,3517,3519],{"class":59,"line":73},[57,3504,108],{"class":107},[57,3506,3507],{"class":111}," helper ",[57,3509,115],{"class":107},[57,3511,119],{"class":118},[57,3513,122],{"class":111},[57,3515,3516],{"class":125},"\"a.helper\"",[57,3518,621],{"class":111},[57,3520,3521],{"class":63},"-- requires a\u002Fhelper.lua\n",[57,3523,3524,3526,3528,3530,3533,3535,3537,3540,3543,3545],{"class":59,"line":79},[57,3525,652],{"class":107},[57,3527,864],{"class":568},[57,3529,867],{"class":111},[57,3531,3532],{"class":568},"process",[57,3534,825],{"class":111},[57,3536,579],{"class":107},[57,3538,3539],{"class":111}," helper.",[57,3541,3542],{"class":118},"transform",[57,3544,825],{"class":111},[57,3546,226],{"class":107},[57,3548,3549,3551],{"class":59,"line":85},[57,3550,579],{"class":107},[57,3552,1548],{"class":111},[57,3554,3555],{"class":59,"line":91},[57,3556,101],{"emptyLinePlaceholder":100},[57,3558,3559],{"class":59,"line":97},[57,3560,1678],{"class":63},[57,3562,3563,3565,3567,3569,3571,3573,3575],{"class":59,"line":104},[57,3564,108],{"class":107},[57,3566,1685],{"class":111},[57,3568,115],{"class":107},[57,3570,119],{"class":118},[57,3572,122],{"class":111},[57,3574,1694],{"class":125},[57,3576,423],{"class":111},[57,3578,3579,3581,3583,3585,3588,3590,3592,3594,3596,3598,3600,3602],{"class":59,"line":135},[57,3580,652],{"class":107},[57,3582,864],{"class":568},[57,3584,867],{"class":111},[57,3586,3587],{"class":568},"use",[57,3589,825],{"class":111},[57,3591,579],{"class":107},[57,3593,1755],{"class":111},[57,3595,3532],{"class":118},[57,3597,825],{"class":111},[57,3599,897],{"class":107},[57,3601,1026],{"class":118},[57,3603,1071],{"class":107},[57,3605,3606],{"class":59,"line":157},[57,3607,101],{"emptyLinePlaceholder":100},[57,3609,3610],{"class":59,"line":175},[57,3611,3612],{"class":63},"-- What happens when b.lua is required first?\n",[3614,3615,3616,3620,3627,3697,3717,3725,3754],"details",{},[3617,3618,3619],"summary",{},"Answer",[15,3621,3622,3623,3626],{},"When ",[24,3624,3625],{},"b.lua"," is required first:",[3628,3629,3630,3639,3651,3663,3676,3688],"ol",{},[3631,3632,3633,3636,3637],"li",{},[24,3634,3635],{},"require(\"b\")"," starts executing ",[24,3638,3625],{},[3631,3640,3641,3643,3644,3647,3648],{},[24,3642,3625],{}," does ",[24,3645,3646],{},"require(\"a\")"," — starts executing ",[24,3649,3650],{},"a.lua",[3631,3652,3653,3643,3655,3658,3659,3662],{},[24,3654,3650],{},[24,3656,3657],{},"require(\"a.helper\")"," — loads ",[24,3660,3661],{},"a\u002Fhelper.lua"," (no circular issue here)",[3631,3664,3665,3667,3668,3671,3672,3675],{},[24,3666,3650],{}," completes, returns ",[24,3669,3670],{},"M"," (the ",[24,3673,3674],{},"a"," module table)",[3631,3677,3678,3679,418,3681,3684,3685,3687],{},"Back in ",[24,3680,3625],{},[24,3682,3683],{},"A"," is now the complete ",[24,3686,3674],{}," module",[3631,3689,3690,3692,3693,3696],{},[24,3691,3625],{}," defines ",[24,3694,3695],{},"M.use"," and returns",[15,3698,3699,3700,3702,3703,3705,3706,3709,3710,3712,3713,3716],{},"This works fine — no circular dependency issue because ",[24,3701,3650],{}," doesn't require ",[24,3704,3625],{},". The only issue would be if ",[24,3707,3708],{},"a.helper"," required ",[24,3711,3674],{}," or ",[24,3714,3715],{},"b"," (creating a cycle back to an incompletely-loaded module).",[15,3718,3719,3720,3722,3723,1219],{},"The bug would appear if ",[24,3721,3625],{}," was required by ",[24,3724,3708],{},[3726,3727,3728],"ul",{},[3631,3729,3730,3732,3733,3732,3735,3732,3737,3732,3739,3732,3741,3732,3743,3732,3745,3747,3748,3750,3751,3753],{},[24,3731,3635],{}," → ",[24,3734,3625],{},[24,3736,3646],{},[24,3738,3650],{},[24,3740,3657],{},[24,3742,3661],{},[24,3744,3635],{},[24,3746,3715],{}," is still loading (incomplete) → ",[24,3749,3715],{},"'s ",[24,3752,3670],{}," is incomplete or nil → error.",[15,3755,3756,3757,3759,3760,3762],{},"Fix: defer the ",[24,3758,3635],{}," inside ",[24,3761,3708],{}," to a function body, not at load time.",[3764,3765,3766],"style",{},"html pre.shiki code .sdCPZ, html code.shiki .sdCPZ{--shiki-default:#6A737D;--shiki-github-dark:#6A737D}html pre.shiki code .svdQ7, html code.shiki .svdQ7{--shiki-default:#D73A49;--shiki-github-dark:#F97583}html pre.shiki code .ssxIu, html code.shiki .ssxIu{--shiki-default:#24292E;--shiki-github-dark:#E1E4E8}html pre.shiki code .snvgF, html code.shiki .snvgF{--shiki-default:#005CC5;--shiki-github-dark:#79B8FF}html pre.shiki code .sJ6F3, html code.shiki .sJ6F3{--shiki-default:#032F62;--shiki-github-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html.github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html pre.shiki code .sIsaT, html code.shiki .sIsaT{--shiki-default:#6F42C1;--shiki-github-dark:#B392F0}",{"title":53,"searchDepth":67,"depth":67,"links":3768},[3769,3771,3778,3779,3780,3781,3782,3783],{"id":39,"depth":67,"text":3770},"require() Mechanics",{"id":294,"depth":67,"text":3772,"children":3773},"Search Path & package.searchpath",[3774,3775,3776],{"id":963,"depth":73,"text":964},{"id":1124,"depth":73,"text":1125},{"id":1364,"depth":73,"text":3777},"Pattern 4: Module with _ENV (export-by-assignment)",{"id":1561,"depth":67,"text":1562},{"id":1978,"depth":67,"text":1979},{"id":2726,"depth":67,"text":2727},{"id":3037,"depth":67,"text":3038},{"id":3279,"depth":67,"text":3280},{"id":3478,"depth":67,"text":3479},"[object Object]","md",{},"\u002Flua\u002F08-modules-and-packages",{"title":5,"description":3789},{"Deep-dive into Lua's module system":3790},"require mechanics, package.path\u002Fcpath, package.loaded cache, searchers and preload, module patterns (table, closure, class), circular dependency resolution via lazy require, hot-reload, and luarocks integration.","lua\u002F08-modules-and-packages","D9yhBkLIaWYsklQQPF-yxsFGPwZKOhESziLd0jtoIto",1789924650340]