Repository navigation
Expand file tree
/
Copy pathrequire.lua
More file actions
203 lines (154 loc) · 5.35 KB
/
Copy pathrequire.lua
File metadata and controls
203 lines (154 loc) · 5.35 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
--[[
https://github.com/overextended/ox_lib
This file is licensed under LGPL-3.0 or higher <https://www.gnu.org/licenses/lgpl-3.0.en.html>
Copyright © 2025 Linden <https://github.com/thelindat>
]]
local lib = lib or ...
local loaded = {}
local _require = require
package = {
path = './?.lua;./?/init.lua',
preload = {},
loaded = setmetatable({}, {
__index = loaded,
__newindex = noop,
__metatable = false,
})
}
---@param modName string
---@return string
---@return string
local function getModuleInfo(modName)
local resource, path = modName:match('^@(.-)/(.+)$')
resource = resource or modName:match('^@(.+)$')
if resource then
return resource, path or 'init'
end
local idx = 4
while true do
local src = debug.getinfo(idx, 'S')?.source
if not src then
return cache.resource, modName
end
if src ~= 'require' then
resource = src:match('^@@([^/]+)/.+')
if resource then
return resource, modName
end
end
idx += 1
end
end
local tempData = {}
---@param name string
---@param path string
---@return string? filename
---@return string? errmsg
---@diagnostic disable-next-line: duplicate-set-field
function package.searchpath(name, path)
local resource, modName = getModuleInfo(name:gsub('%.', '/'))
local tried = {}
for template in path:gmatch('[^;]+') do
local fileName = template:gsub('^%./', ''):gsub('?', modName:gsub('%.', '/') or modName)
local file = LoadResourceFile(resource, fileName)
if file then
tempData[1] = file
tempData[2] = resource
return fileName
end
tried[#tried + 1] = ("no file '@%s/%s'"):format(resource, fileName)
end
return nil, table.concat(tried, "\n\t")
end
---Attempts to load a module at the given path relative to the resource root directory.\
---Returns a function to load the module chunk, or a string containing all tested paths.
---@param modName string
---@param env? table
local function loadModule(modName, env)
local fileName, err = package.searchpath(modName, package.path)
if fileName then
local file = tempData[1]
local resource = tempData[2]
table.wipe(tempData)
return assert(load(file, ('@@%s/%s'):format(resource, fileName), 't', env or _ENV)), fileName
end
return nil, err or 'unknown error'
end
---@alias PackageSearcher
---| fun(modName: string): function loader
---| fun(modName: string): nil, string errmsg
---@type PackageSearcher[]
package.searchers = {
function(modName)
local ok, result = pcall(_require, modName)
if ok then return result end
return ok, result
end,
function(modName)
if package.preload[modName] ~= nil then
return package.preload[modName]
end
return nil, ("no field package.preload['%s']"):format(modName)
end,
function(modName) return loadModule(modName) end,
}
---@param filePath string
---@param env? table
---@return unknown
---Loads and runs a Lua file at the given path. Unlike require, the chunk is not cached for future use.
function lib.load(filePath, env)
if type(filePath) ~= 'string' then
error(("file path must be a string (received '%s')"):format(filePath), 2)
end
local result, err = loadModule(filePath, env)
if result then return result() end
error(("file '%s' not found\n\t%s"):format(filePath, err))
end
---@param filePath string
---@return table
---Loads and decodes a json file at the given path.
function lib.loadJson(filePath)
if type(filePath) ~= 'string' then
error(("file path must be a string (received '%s')"):format(filePath), 2)
end
local resourceSrc, modPath = getModuleInfo(filePath:gsub('%.', '/'))
local resourceFile = LoadResourceFile(resourceSrc, ('%s.json'):format(modPath))
if resourceFile then
return json.decode(resourceFile)
end
error(("json file '%s' not found\n\tno file '@%s/%s.json'"):format(filePath, resourceSrc, modPath))
end
---Loads the given module, returns any value returned by the seacher (`true` when `nil`).\
---Passing `@resourceName.modName` loads a module from a remote resource.
---@param modName string
---@return unknown
function lib.require(modName)
if type(modName) ~= 'string' then
error(("module name must be a string (received '%s')"):format(modName), 3)
end
local module = loaded[modName]
if module == '__loading' then
error(("^1circular-dependency occurred when loading module '%s'^0"):format(modName), 2)
end
if module ~= nil then return module end
loaded[modName] = '__loading'
local err = {}
for i = 1, #package.searchers do
local loader, data = package.searchers[i](modName)
if loader then
if type(loader) == 'function' then loader = loader(modName, data) end
loaded[modName] = loader or loader == nil
return loaded[modName]
end
err[#err + 1] = data
end
error(("%s"):format(table.concat(err, "\n\t")))
end
_ENV.require = lib.require
table.insert(package.searchers, 1, function(modName)
if modName:find('^Ox%.') then
modName = modName:gsub('^Ox.', '@ox_lib.modules.')
return function() return require(modName) end
end
end)
return lib.require