// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details1#pragma once23#include "lua.h"45#include <stdbool.h>6#include <stddef.h>78////////////////////////////////////////////////////////////////////////////////9//10// Require-by-string assumes that the context in which it is embedded adheres to11// a particular structure.12//13// Each component in a require path either represents a module or a directory.14// Modules contain Luau code, whereas directories serve solely as organizational15// units. For the purposes of navigation, both modules and directories are16// functionally identical: modules and directories can both have children, which17// could themselves be modules or directories, and both types can have at most18// one parent, which could also be either a module or a directory.19//20// Without more context, it is impossible to tell which components in a given21// path "./foo/bar/baz" are modules and which are directories. To provide this22// context, the require-by-string runtime library must be opened with a23// luarequire_Configuration object, which defines the navigation behavior of the24// context in which Luau is embedded.25//26// Calls to to_parent and to_child signal a move up or down the context's27// hierarchy. The context is expected to maintain an internal state so that28// when is_module_present is called, require-by-string can determine whether it29// is currently pointing at a module or a directory.30//31// In a conventional filesystem context, "modules" map either to *.luau files or32// to directories on disk containing an init.luau file, whereas "directories"33// map to directories on disk not containing an init.luau file. In a more34// abstract context, a module and a directory could be represented by any35// nestable code unit and organizational unit, respectively.36//37// Require-by-string's runtime behavior can be additionally be configured in38// configuration files, such as .luaurc or .config.luau files in a filesystem39// context. The presence of a configuration file in the current context is40// signaled by the get_config_status function. Both modules and directories can41// contain configuration files; however, note that a given configuration file's42// scope is limited to the descendants of the module or directory in which it43// resides. In other words, when searching for a relevant configuration file for44// a given module, the search begins at the module's parent context and proceeds45// up the hierarchy from there, resolving to the first configuration file found.46//47////////////////////////////////////////////////////////////////////////////////4849typedef enum luarequire_NavigateResult50{51NAVIGATE_SUCCESS,52NAVIGATE_AMBIGUOUS,53NAVIGATE_NOT_FOUND54} luarequire_NavigateResult;5556// Functions returning WRITE_SUCCESS are expected to set their size_out argument57// to the number of bytes written to the buffer. If WRITE_BUFFER_TOO_SMALL is58// returned, size_out should be set to the required buffer size.59typedef enum luarequire_WriteResult60{61WRITE_SUCCESS,62WRITE_BUFFER_TOO_SMALL,63WRITE_FAILURE64} luarequire_WriteResult;6566// Represents whether a configuration file is present, and if so, its syntax.67typedef enum luarequire_ConfigStatus68{69CONFIG_ABSENT,70CONFIG_AMBIGUOUS, // Signals the presence of multiple configuration files.71CONFIG_PRESENT_JSON,72CONFIG_PRESENT_LUAU,73} luarequire_ConfigStatus;7475typedef struct luarequire_Configuration76{77// Returns whether requires are permitted from the given chunkname.78bool (*is_require_allowed)(lua_State* L, void* ctx, const char* requirer_chunkname);7980// Resets the internal state to point at the requirer module.81luarequire_NavigateResult (*reset)(lua_State* L, void* ctx, const char* requirer_chunkname);8283// Resets the internal state to point at an aliased module, given its exact84// path from a configuration file. This function is only called when an85// alias's path cannot be resolved relative to its configuration file.86luarequire_NavigateResult (*jump_to_alias)(lua_State* L, void* ctx, const char* path);8788// Provides an initial alias override opportunity prior to searching for89// configuration files. If NAVIGATE_SUCCESS is returned, the internal state90// must be updated to point at the aliased location. Can be left undefined.91luarequire_NavigateResult (*to_alias_override)(lua_State* L, void* ctx, const char* alias_unprefixed);9293// Provides a final opportunity to resolve an alias if it cannot be found in94// configuration files. If NAVIGATE_SUCCESS is returned, the internal state95// must be updated to point at the aliased location. Can be left undefined.96luarequire_NavigateResult (*to_alias_fallback)(lua_State* L, void* ctx, const char* alias_unprefixed);9798// Navigates through the context by making mutations to the internal state.99luarequire_NavigateResult (*to_parent)(lua_State* L, void* ctx);100luarequire_NavigateResult (*to_child)(lua_State* L, void* ctx, const char* name);101102// Returns whether the context is currently pointing at a module.103bool (*is_module_present)(lua_State* L, void* ctx);104105// Provides a chunkname for the current module. This will be accessible106// through the debug library. This function is only called if107// is_module_present returns true.108luarequire_WriteResult (*get_chunkname)(lua_State* L, void* ctx, char* buffer, size_t buffer_size, size_t* size_out);109110// Provides a loadname that identifies the current module and is passed to111// load. This function is only called if is_module_present returns true.112luarequire_WriteResult (*get_loadname)(lua_State* L, void* ctx, char* buffer, size_t buffer_size, size_t* size_out);113114// Provides a cache key representing the current module. This function is115// only called if is_module_present returns true.116luarequire_WriteResult (*get_cache_key)(lua_State* L, void* ctx, char* buffer, size_t buffer_size, size_t* size_out);117118// Returns whether a configuration file is present in the current context,119// and if so, its syntax. If not present, require-by-string will call120// to_parent until either a configuration file is present or121// NAVIGATE_FAILURE is returned (at root).122luarequire_ConfigStatus (*get_config_status)(lua_State* L, void* ctx);123124// Parses the configuration file in the current context for the given alias125// and returns its value or WRITE_FAILURE if not found. This function is126// only called if get_config_status returns true. If this function pointer127// is set, get_config must not be set. Opting in to this function pointer128// disables parsing configuration files internally and can be used for finer129// control over the configuration file parsing process.130luarequire_WriteResult (*get_alias)(lua_State* L, void* ctx, const char* alias, char* buffer, size_t buffer_size, size_t* size_out);131132// Provides the contents of the configuration file in the current context.133// This function is only called if get_config_status does not return134// CONFIG_ABSENT. If this function pointer is set, get_alias must not be135// set. Opting in to this function pointer enables parsing configuration136// files internally.137luarequire_WriteResult (*get_config)(lua_State* L, void* ctx, char* buffer, size_t buffer_size, size_t* size_out);138139// Returns the maximum number of milliseconds to allow for executing a given140// Luau-syntax configuration file. This function is only called if141// get_config_status returns CONFIG_PRESENT_LUAU and can be left undefined142// if support for Luau-syntax configuration files is not needed. A default143// value of 2000ms is used. Negative values are treated as infinite.144int (*get_luau_config_timeout)(lua_State* L, void* ctx);145146// Executes the module and places the result on the stack. Returns the147// number of results placed on the stack. Returning -1 directs the requiring148// thread to yield. In this case, this thread should be resumed with the149// module result pushed onto its stack.150int (*load)(lua_State* L, void* ctx, const char* path, const char* chunkname, const char* loadname);151} luarequire_Configuration;152153// Populates function pointers in the given luarequire_Configuration.154typedef void (*luarequire_Configuration_init)(luarequire_Configuration* config);155156// Initializes and pushes the require closure onto the stack without157// registration.158LUALIB_API int luarequire_pushrequire(lua_State* L, luarequire_Configuration_init config_init, void* ctx);159160// Initializes the require library and registers it globally.161LUALIB_API void luaopen_require(lua_State* L, luarequire_Configuration_init config_init, void* ctx);162163// Initializes and pushes a "proxyrequire" closure onto the stack. This function164// takes two parameters: the string path to resolve and the chunkname of an165// existing module. The path is resolved as if it were being required from the166// module that the chunkname represents.167LUALIB_API int luarequire_pushproxyrequire(lua_State* L, luarequire_Configuration_init config_init, void* ctx);168169// Registers an aliased require path to a result. After registration, the given170// result will always be immediately returned when the given path is required.171// Expects the path and table to be passed as arguments on the stack.172LUALIB_API int luarequire_registermodule(lua_State* L);173174// Clears the entry associated with the given cache key from the require cache.175// Expects the cache key to be passed as an argument on the stack.176LUALIB_API int luarequire_clearcacheentry(lua_State* L);177178// Clears all entries from the require cache.179LUALIB_API int luarequire_clearcache(lua_State* L);180181182