mirror of
https://github.com/CommunityOx/ox_lib.git
synced 2026-08-17 06:56:03 +01:00
Some people accidentally, mistakenly, and without any malicious intent forget to attribute code from this resource to its source. This change will hopefully assist people, so that they do not make that mistake again.
191 lines
5.1 KiB
Lua
191 lines
5.1 KiB
Lua
--[[
|
|
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 (c) 2025 Linden <https://github.com/thelindat/fivem>
|
|
]]
|
|
|
|
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 = modName:match('^@(.-)/.+') --[[@as string?]]
|
|
|
|
if resource then
|
|
return resource, modName:sub(#resource + 3)
|
|
end
|
|
|
|
local idx = 4 -- call stack depth (kept slightly lower than expected depth "just in case")
|
|
|
|
while true do
|
|
local src = debug.getinfo(idx, 'S')?.source
|
|
|
|
if not src then
|
|
return cache.resource, modName
|
|
end
|
|
|
|
resource = src:match('^@@([^/]+)/.+')
|
|
|
|
if resource and not src:find('^@@ox_lib/imports/require') then
|
|
return resource, modName
|
|
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))
|
|
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 result, errMsg = package.searchers[i](modName)
|
|
|
|
if result then
|
|
if type(result) == 'function' then result = result() end
|
|
loaded[modName] = result or result == nil
|
|
|
|
return loaded[modName]
|
|
end
|
|
|
|
err[#err + 1] = errMsg
|
|
end
|
|
|
|
error(("%s"):format(table.concat(err, "\n\t")))
|
|
end
|
|
|
|
return lib.require
|