-
Notifications
You must be signed in to change notification settings - Fork 42
Feature: Lute Config and Module Resolvers #535
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 10 commits
fb1891b
15c79ce
9de6bf5
7c84839
3c9437b
a525d5c
91198b4
b331981
bc80966
6d73377
60e6b11
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,14 +1,18 @@ | ||
| add_library(Lute.Luau STATIC) | ||
|
|
||
| target_sources(Lute.Luau PRIVATE | ||
| include/lute/configresolver.h | ||
| include/lute/luau.h | ||
| include/lute/moduleresolver.h | ||
| include/lute/resolverequire.h | ||
|
|
||
| src/configresolver.cpp | ||
| src/luau.cpp | ||
| src/moduleresolver.cpp | ||
| src/resolverequire.cpp | ||
| ) | ||
|
|
||
| target_compile_features(Lute.Luau PUBLIC cxx_std_17) | ||
| target_include_directories(Lute.Luau PUBLIC "include" ${LIBUV_INCLUDE_DIR}) | ||
| target_link_libraries(Lute.Luau PRIVATE Lute.Require Lute.Runtime uv_a Luau.Analysis Luau.Ast Luau.Compiler Luau.Require Luau.VM) | ||
| target_link_libraries(Lute.Luau PRIVATE Lute.Require Lute.Runtime uv_a Luau.Analysis Luau.Ast Luau.Compiler Luau.CLI.lib Luau.Require Luau.VM) | ||
| target_compile_options(Lute.Luau PRIVATE ${LUTE_OPTIONS}) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| #pragma once | ||
|
|
||
| #include "Luau/LuauConfig.h" | ||
|
|
||
| namespace Luau | ||
| { | ||
|
|
||
| // Based on LuteConfigResolver in tc.cpp. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wait are we just duplicating this right now?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nah this one is more comprehensive than
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it's not exactly the same @Vighnesh-V did you say we wanted to use this one in configresolver instead of the implementation in tc.cpp?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would have guessed we were pulling these file/config resolvers out into their own files so that tc.cpp could use them.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yea I think the version in this PR is the |
||
| struct LuteConfigResolver : Luau::ConfigResolver | ||
| { | ||
| Luau::Config defaultConfig; | ||
| mutable std::unordered_map<std::string, Luau::Config> configCache; | ||
| mutable std::vector<std::pair<std::string, std::string>> configErrors; | ||
|
|
||
| LuteConfigResolver(Luau::Mode mode); | ||
|
|
||
| const Luau::Config& getConfig(const Luau::ModuleName& name) const override; | ||
|
|
||
| const Luau::Config& readConfigRec(const std::string& path) const; | ||
| }; | ||
|
|
||
| } // namespace Luau | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| #pragma once | ||
|
|
||
| #include "Luau/FileResolver.h" | ||
|
|
||
| namespace Luau | ||
| { | ||
|
|
||
| // Based on CliFileResolver in Analyze.cpp. | ||
| struct LuteModuleResolver : Luau::FileResolver | ||
| { | ||
| LuteModuleResolver() = default; | ||
|
|
||
| std::optional<Luau::SourceCode> readSource(const Luau::ModuleName& name) override; | ||
|
|
||
| // We are currently resolving modules and requires only, and will add support for Roblox globals / types in a subsequent PR. | ||
| std::optional<Luau::ModuleInfo> resolveModule(const Luau::ModuleInfo* context, Luau::AstExpr* node) override; | ||
| }; | ||
|
|
||
| } // namespace Luau |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,79 @@ | ||
| #include "lute/configresolver.h" | ||
|
|
||
| #include "Luau/ParseResult.h" | ||
| #include "Luau/FileUtils.h" | ||
| #include "Luau/LuauConfig.h" | ||
|
vrn-sn marked this conversation as resolved.
Outdated
|
||
|
|
||
| namespace Luau | ||
| { | ||
|
|
||
| LuteConfigResolver::LuteConfigResolver(Luau::Mode mode) | ||
| { | ||
| defaultConfig.mode = mode; | ||
| } | ||
|
|
||
| const Luau::Config& LuteConfigResolver::getConfig(const Luau::ModuleName& name) const | ||
| { | ||
| std::optional<std::string> path = getParentPath(name); | ||
| if (!path) | ||
| return defaultConfig; | ||
|
|
||
| return readConfigRec(*path); | ||
| } | ||
|
|
||
| const Luau::Config& LuteConfigResolver::readConfigRec(const std::string& path) const | ||
| { | ||
| auto it = configCache.find(path); | ||
| if (it != configCache.end()) | ||
| return it->second; | ||
|
|
||
| std::optional<std::string> parent = getParentPath(path); | ||
| Luau::Config result = parent ? readConfigRec(*parent) : defaultConfig; | ||
|
|
||
| std::optional<std::string> configPath = joinPaths(path, Luau::kConfigName); | ||
| if (!isFile(*configPath)) | ||
| configPath = std::nullopt; | ||
|
|
||
| std::optional<std::string> luauConfigPath = joinPaths(path, Luau::kLuauConfigName); | ||
| if (!isFile(*luauConfigPath)) | ||
| luauConfigPath = std::nullopt; | ||
|
|
||
| if (configPath && luauConfigPath) | ||
| { | ||
| std::string ambiguousError = Luau::format("Both %s and %s files exist", Luau::kConfigName, Luau::kLuauConfigName); | ||
| configErrors.emplace_back(*configPath, std::move(ambiguousError)); | ||
| } | ||
| else if (configPath) | ||
| { | ||
| if (std::optional<std::string> contents = readFile(*configPath)) | ||
| { | ||
| Luau::ConfigOptions::AliasOptions aliasOpts; | ||
| aliasOpts.configLocation = *configPath; | ||
| aliasOpts.overwriteAliases = true; | ||
|
|
||
| Luau::ConfigOptions opts; | ||
| opts.aliasOptions = std::move(aliasOpts); | ||
|
|
||
| std::optional<std::string> error = Luau::parseConfig(*contents, result, opts); | ||
| if (error) | ||
| configErrors.emplace_back(*configPath, *error); | ||
| } | ||
| } | ||
| else if (luauConfigPath) | ||
| { | ||
| if (std::optional<std::string> contents = readFile(*luauConfigPath)) | ||
| { | ||
| Luau::ConfigOptions::AliasOptions aliasOpts; | ||
| aliasOpts.configLocation = *luauConfigPath; | ||
| aliasOpts.overwriteAliases = true; | ||
|
|
||
| std::optional<std::string> error = Luau::extractLuauConfig(*contents, result, aliasOpts, Luau::InterruptCallbacks{}); | ||
| if (error) | ||
| configErrors.emplace_back(*luauConfigPath, *error); | ||
| } | ||
| } | ||
|
|
||
| return configCache[path] = result; | ||
| } | ||
|
|
||
| } // namespace Luau | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| #include "lute/moduleresolver.h" | ||
|
|
||
| #include "Luau/Ast.h" | ||
|
vrn-sn marked this conversation as resolved.
|
||
| #include "Luau/FileUtils.h" | ||
|
|
||
| #include "lute/resolverequire.h" | ||
|
|
||
| namespace Luau | ||
| { | ||
|
|
||
| std::optional<Luau::SourceCode> LuteModuleResolver::readSource(const Luau::ModuleName& name) | ||
| { | ||
| if (std::optional<std::string> source = readFile(name)) | ||
| return Luau::SourceCode{*source, Luau::SourceCode::Module}; | ||
| return std::nullopt; | ||
| } | ||
|
|
||
| // We are currently resolving modules and requires only, and will add support for Roblox globals / types in a subsequent PR. | ||
| std::optional<Luau::ModuleInfo> LuteModuleResolver::resolveModule(const Luau::ModuleInfo* context, Luau::AstExpr* node) | ||
| { | ||
| if (auto expr = node->as<Luau::AstExprConstantString>()) | ||
| { | ||
| std::string requirePath(expr->value.data, expr->value.size); | ||
|
|
||
| std::string error; | ||
| std::string requirerChunkname = "@" + context->name; | ||
| std::optional<std::string> absolutePath = resolveRequire(requirePath, std::move(requirerChunkname), &error); | ||
| if (!absolutePath) | ||
| { | ||
| printf("Failed to resolve require: %s\n", error.c_str()); | ||
| return std::nullopt; | ||
| } | ||
|
|
||
| return Luau::ModuleInfo{*absolutePath}; | ||
| } | ||
|
|
||
| return std::nullopt; | ||
| } | ||
|
|
||
| } // namespace Luau | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| // Tests for configuration resolver inheritance and ambiguity. | ||
| #include "doctest.h" | ||
| #include "luteprojectroot.h" | ||
|
|
||
| #include "lute/configresolver.h" | ||
|
|
||
| #include "Luau/FileUtils.h" | ||
| #include "Luau/LuauConfig.h" | ||
|
vrn-sn marked this conversation as resolved.
Outdated
|
||
|
|
||
| #include <sys/stat.h> | ||
| #include <sys/types.h> | ||
|
vrn-sn marked this conversation as resolved.
Outdated
|
||
|
|
||
| TEST_CASE("configresolver") | ||
| { | ||
| std::string root = getLuteProjectRootAbsolute(); | ||
| std::string baseDir = joinPaths(root, "tests/src/resolver"); | ||
| std::string file = joinPaths(baseDir, "mainmodule.luau"); | ||
|
|
||
| // There is a .luaurc in tests/src/resolver; verify resolver reads it without crashing | ||
| Luau::LuteConfigResolver configResolver(Luau::Mode::Nonstrict); | ||
| const Luau::Config& cfg = configResolver.getConfig(file); | ||
|
|
||
| // check that mode was set to strict per .luaurc | ||
| CHECK(cfg.mode == Luau::Mode::Strict); | ||
|
|
||
| // check the alias root was set | ||
| const Luau::Config::AliasInfo* aliasInfo = cfg.aliases.find("root"); | ||
| CHECK(aliasInfo != nullptr); | ||
| CHECK(aliasInfo->value == "./"); | ||
|
|
||
| // run readConfigRec on parent directory to verify inheritance | ||
| const Luau::Config& parentCfg = configResolver.readConfigRec(baseDir); | ||
| // mode should be the same as child since no override | ||
| CHECK(parentCfg.mode == Luau::Mode::Strict); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| // Simplified tests for module resolver functionality using public APIs. | ||
| #include "doctest.h" | ||
| #include "luteprojectroot.h" | ||
|
|
||
| #include "Luau/Ast.h" | ||
| #include "Luau/FileUtils.h" | ||
|
|
||
| #include "lute/moduleresolver.h" | ||
| #include "lute/resolverequire.h" | ||
|
|
||
| #include <string> | ||
|
|
||
| TEST_CASE("moduleresolver_read_source") | ||
| { | ||
| std::string root = getLuteProjectRootAbsolute(); | ||
| std::string file = joinPaths(root, "tests/src/resolver/mainmodule.luau"); | ||
| Luau::LuteModuleResolver resolver; | ||
| auto source = resolver.readSource(file); | ||
| REQUIRE(source); | ||
| CHECK(source->type == Luau::SourceCode::Module); | ||
| CHECK(source->source.find("require") != std::string::npos); | ||
| } | ||
|
|
||
| // TODO: add tests for resolveModule |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| { | ||
| "languageMode": "strict", | ||
| "aliases": { | ||
| "root": "./" | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| local types = require("@root/types") | ||
| local tableext = require("@std/tableext") | ||
|
|
||
| local testFilter: { [string]: number } = { | ||
| ["a"] = 1, | ||
| ["b"] = 2, | ||
| } | ||
|
|
||
| local a = {} | ||
| type tb = tableext.dictionary<number> | ||
|
|
||
| function a._a(a: number) | ||
| return function(s: string): types.test | ||
| return types(s, a) | ||
| end | ||
| end | ||
|
|
||
| a._b = tableext.map(testFilter, function(value) | ||
| return tostring(value) | ||
| end) | ||
|
|
||
| a._metadata = { | ||
| key = -1, | ||
| id = "nice", | ||
| } | ||
|
|
||
| return a |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| export type test = { [string]: number } | ||
|
|
||
| return function(s: string, n: number): test | ||
| return { | ||
| [s] = n, | ||
| } | ||
| end |
Uh oh!
There was an error while loading. Please reload this page.