From 258d1a99ff4a2d6a6d5ab1181eb308b286636d07 Mon Sep 17 00:00:00 2001 From: Erica Marigold Date: Mon, 2 Dec 2024 15:16:22 +0000 Subject: [PATCH 01/39] chore: setup as pesde managed package --- pesde.lock | 3 +++ pesde.toml | 12 ++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 pesde.lock create mode 100644 pesde.toml diff --git a/pesde.lock b/pesde.lock new file mode 100644 index 0000000..6e78c33 --- /dev/null +++ b/pesde.lock @@ -0,0 +1,3 @@ +name = "pesde/scripts" +version = "0.1.0" +target = "lune" diff --git a/pesde.toml b/pesde.toml new file mode 100644 index 0000000..ea575e1 --- /dev/null +++ b/pesde.toml @@ -0,0 +1,12 @@ +name = "pesde/scripts" +version = "0.1.0" +description = "Scripts and other utilities for use with pesde" +authors = ["dai (https://www.daimond113.com/)", "Erica Marigold "] +repository = "https://github.com/pesde-pkg/scripts" +license = "MIT" + +[target] +environment = "lune" + +[indices] +default = "https://github.com/daimond113/pesde-index" From 6f4a186af4a175ec332b03fce71bee9c5622fa6a Mon Sep 17 00:00:00 2001 From: Erica Marigold Date: Mon, 2 Dec 2024 15:16:46 +0000 Subject: [PATCH 02/39] chore(vscode): include lune require config snippet --- .vscode/settings.json | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..17eb63b --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "luau-lsp.require.mode": "relativeToFile", + "luau-lsp.require.directoryAliases": { + "@lune/": "~/.lune/.typedefs/0.8.9/" + } +} \ No newline at end of file From 84720a2885fc73d103cc4e144e50bc83ee8be483 Mon Sep 17 00:00:00 2001 From: Erica Marigold Date: Mon, 2 Dec 2024 16:54:17 +0000 Subject: [PATCH 03/39] refactor: restructure repo and setup as library Restructures the project to be a library with generator exports, and includes lune scripts as examples on how to use exported generators by the library. Also specifies `includes` and pins a pesde version in the package manifest. --- .lune/roblox_sync_config_generator.luau | 19 ++++ .lune/sourcemap_generator.luau | 19 ++++ lune/rojo/roblox_sync_config_generator.luau | 44 --------- lune/rojo/sourcemap_generator.luau | 19 ---- pesde.toml | 3 + src/generators/rojo/sourcemap.luau | 43 ++++++++ src/generators/rojo/sync_config.luau | 104 ++++++++++++++++++++ src/init.luau | 8 ++ 8 files changed, 196 insertions(+), 63 deletions(-) create mode 100644 .lune/roblox_sync_config_generator.luau create mode 100644 .lune/sourcemap_generator.luau delete mode 100644 lune/rojo/roblox_sync_config_generator.luau delete mode 100644 lune/rojo/sourcemap_generator.luau create mode 100644 src/generators/rojo/sourcemap.luau create mode 100644 src/generators/rojo/sync_config.luau create mode 100644 src/init.luau diff --git a/.lune/roblox_sync_config_generator.luau b/.lune/roblox_sync_config_generator.luau new file mode 100644 index 0000000..a233ae0 --- /dev/null +++ b/.lune/roblox_sync_config_generator.luau @@ -0,0 +1,19 @@ +local function enter(fn: (args: { string }) -> number?): never + local process = require("@lune/process") + local stdio = require("@lune/stdio") + + local startTime = os.clock() + local exitCode = fn(table.clone(process.args)) + + stdio.write( + `done in {stdio.style("dim")}{string.format("%.2fs", os.clock() - startTime)}{stdio.style("reset")}!\n` + ) + + return process.exit(exitCode) +end + +return enter(function(args: { string }): number? + local ok, _ = require("../src").generators.rojo.syncConfig(table.remove(args, 1), args, true) + + return tonumber(ok) +end) diff --git a/.lune/sourcemap_generator.luau b/.lune/sourcemap_generator.luau new file mode 100644 index 0000000..2037a13 --- /dev/null +++ b/.lune/sourcemap_generator.luau @@ -0,0 +1,19 @@ +local function enter(fn: (args: { string }) -> number?): never + local process = require("@lune/process") + local stdio = require("@lune/stdio") + + local startTime = os.clock() + local exitCode = fn(table.clone(process.args)) + + stdio.ewrite( + `\ndone in {stdio.style("dim")}{string.format("%.2fs", os.clock() - startTime)}{stdio.style("reset")}!\n` + ) + + return process.exit(exitCode) +end + +return enter(function(args: { string }): number? + return tonumber(require("../src").generators.rojo.sourcemap( + args[1] + )) +end) \ No newline at end of file diff --git a/lune/rojo/roblox_sync_config_generator.luau b/lune/rojo/roblox_sync_config_generator.luau deleted file mode 100644 index 3631255..0000000 --- a/lune/rojo/roblox_sync_config_generator.luau +++ /dev/null @@ -1,44 +0,0 @@ -local fs = require("@lune/fs") -local process = require("@lune/process") -local serde = require("@lune/serde") - -local package_directory = process.args[1] - -if fs.isFile(package_directory .. "/default.project.json") then - return -end - -local output = { - tree = {}, -} - -for i, file in process.args do - if i == 1 then - continue - end - - local name = string.gsub(file, ".luau?$", "") - - if name == "init" then - output.tree["$path"] = file - continue - end - - output.tree[name] = { - ["$path"] = file, - } -end - -if not output.tree["$path"] then - output.tree["$className"] = "Folder" -end - -if not output.tree["roblox_packages"] then - output.tree["roblox_packages"] = { - ["$path"] = { - optional = "roblox_packages", - }, - } -end - -fs.writeFile(package_directory .. "/default.project.json", serde.encode("json", output, true)) diff --git a/lune/rojo/sourcemap_generator.luau b/lune/rojo/sourcemap_generator.luau deleted file mode 100644 index 4cf09cd..0000000 --- a/lune/rojo/sourcemap_generator.luau +++ /dev/null @@ -1,19 +0,0 @@ -local fs = require("@lune/fs") -local process = require("@lune/process") -local serde = require("@lune/serde") -local stdio = require("@lune/stdio") - -local package_directory = process.args[1] - -if fs.isFile(package_directory .. "/default.project.json") then - process.spawn("rojo", { "sourcemap", package_directory }, { cwd = process.cwd, stdio = "forward" }) -elseif fs.isFile(package_directory .. "/init.luau") then - local sourcemap = { filePaths = { "init.luau" } } - stdio.write(serde.encode("json", sourcemap, false)) -elseif fs.isFile(package_directory .. "/init.lua") then - local sourcemap = { filePaths = { "init.lua" } } - stdio.write(serde.encode("json", sourcemap, false)) -else - -- use stderr to avoid this being parsed as the output of the sourcemap command - stdio.ewrite("no default.project.json found in " .. package_directory) -end diff --git a/pesde.toml b/pesde.toml index ea575e1..d8d6917 100644 --- a/pesde.toml +++ b/pesde.toml @@ -1,12 +1,15 @@ name = "pesde/scripts" version = "0.1.0" +pesde_version = "0.5.0-rc.14" description = "Scripts and other utilities for use with pesde" authors = ["dai (https://www.daimond113.com/)", "Erica Marigold "] repository = "https://github.com/pesde-pkg/scripts" license = "MIT" +includes = ["src/**/*.luau"] [target] environment = "lune" +lib = "src/init.luau" [indices] default = "https://github.com/daimond113/pesde-index" diff --git a/src/generators/rojo/sourcemap.luau b/src/generators/rojo/sourcemap.luau new file mode 100644 index 0000000..d451b24 --- /dev/null +++ b/src/generators/rojo/sourcemap.luau @@ -0,0 +1,43 @@ +local fs = require("@lune/fs") +local process = require("@lune/process") +local serde = require("@lune/serde") +local stdio = require("@lune/stdio") + +local PLATFORM_SEP = if process.os == "windows" then "\\" else "/" + +--- Writes a Rojo sourcemap for the project in the provided directory or the current +--- working directory to standard output. + +--- ## Errors +--- * The current process lacks permissions to a file +--- * Any I/O error occurs +return function(packageDirectory: string?): boolean + packageDirectory = packageDirectory or process.cwd + + -- A mapping of things to do depending on the file present + local PATH_ACTION_MAP: { [string]: (dir: string) -> number? } = { + ["default.project.json"] = function(dir) + return process.spawn("rojo", { "sourcemap", dir }, { + cwd = process.cwd, + env = process.env, + stdio = "forward", + }).code + end, + + ["init.lua"] = stdio.write(serde.encode("json", { filePaths = { "init.lua" } }, false)), + ["init.luau"] = stdio.write(serde.encode("json", { filePaths = { "init.luau" } }, false)), + } + + -- We go through the action mappings in order of priority and check for the + -- file predicates, if present, we execute the action and report our status + for path, action in PATH_ACTION_MAP do + if fs.isFile(`{packageDirectory}{PLATFORM_SEP}{path}`) then + local status = action() + return if status ~= nil then status == 0 else true + end + end + + -- If we reached so far, that must mean none of the file predicates matched, + -- so we return a `false` signifying an error + return false +end diff --git a/src/generators/rojo/sync_config.luau b/src/generators/rojo/sync_config.luau new file mode 100644 index 0000000..5318724 --- /dev/null +++ b/src/generators/rojo/sync_config.luau @@ -0,0 +1,104 @@ +local fs = require("@lune/fs") +local process = require("@lune/process") +local serde = require("@lune/serde") + +export type TreeProperties = { + Name: never?, + Parent: never?, +} + +export type TreeBase = { + ["$className"]: string?, + ["$ignoreUnknownInstances"]: boolean?, + ["$path"]: string | { optional: string }?, + ["$properties"]: TreeProperties?, +} + +export type TreeNormal = TreeBase & { + [string]: TreeNormal, +} & ({ ["$className"]: string } | { ["$path"]: string | { optional: string } }) + +export type TreeService = TreeBase & { + [string]: TreeNormal, +} + +export type DataModelTree = TreeBase & { + StarterPlayer: (TreeBase & { + StarterPlayerScripts: TreeService?, + StarterCharacterScripts: TreeService?, + [string]: TreeNormal, + })?, + [string]: TreeService, +} + +export type Tree = (DataModelTree & { + ["$className"]: "DataModel", +}) | TreeNormal + +export type SyncConfig = { + name: string, + servePort: number?, + servePlaceIds: { number }?, + placeId: number?, + gameId: number?, + serveAddress: string?, + globIgnorePaths: { string }?, + tree: Tree, +} + +local PLATFORM_SEP = if process.os == "windows" then "\\" else "/" + +--- Generates a Rojo sync configuration file (`default.project.json`) from a list of +--- input files to be included. + +--- ## Errors +--- * The current process lacks permissions to a file +--- * Any I/O error occurs +return function(packageDirectory: string?, files: { string }, writeToFile: boolean?): (boolean, string?) + packageDirectory = packageDirectory or process.cwd + local syncConfigPath = `{packageDirectory}{PLATFORM_SEP}default.project.json` + if fs.isFile(syncConfigPath) then + return true, nil + end + + local syncConfigTree = {} :: Tree + + for _, file in files do + -- Remove the `.lua` or `.luau` file extension from the file name + local name = string.gsub(file, ".luau?$", "") + + if name == "init" then + syncConfigTree["$path"] = name + continue + end + + syncConfigTree[name] = { + ["$path"] = file, + } + end + + -- If there isn't a top level path, we mark the entire thing as a Folder + if not syncConfigTree["$path"] then + syncConfigTree["$className"] = "Folder" + end + + -- If the config tree does not include pesde's downloaded roblox dependencies + -- directory, we add it as an optional one for the future, once dependencies + -- are installed + if not syncConfigTree["roblox_packages"] then + syncConfigTree["roblox_packages"] = { + ["$path"] = { + optional = "roblox_packages", + }, + } + end + + -- Finally, we serialize the config to a JSON string and optionally write it + -- to the sync config path + local serializedConfig = serde.encode("json", { tree = syncConfigTree }, true) + if writeToFile then + fs.writeFile(syncConfigPath, serializedConfig) + end + + return true, serializedConfig +end diff --git a/src/init.luau b/src/init.luau new file mode 100644 index 0000000..a9ff34a --- /dev/null +++ b/src/init.luau @@ -0,0 +1,8 @@ +return { + generators = { + rojo = { + sourcemap = require("./generators/rojo/sourcemap"), + syncConfig = require("./generators/rojo/sync_config"), + }, + }, +} From e019e5a8edee682c7dd9496d5d19e2b20d7b3c1d Mon Sep 17 00:00:00 2001 From: Erica Marigold Date: Mon, 2 Dec 2024 16:57:16 +0000 Subject: [PATCH 04/39] chore: include stylua config --- stylua.toml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/stylua.toml b/stylua.toml index 056139b..2ab5776 100644 --- a/stylua.toml +++ b/stylua.toml @@ -1,2 +1,10 @@ +line_endings = "Unix" +quote_style = "AutoPreferDouble" +indent_type = "Spaces" +call_parentheses = "Always" + +indent_width = 4 +column_width = 80 + [sort_requires] -enabled = true \ No newline at end of file +enabled = true From 51994dfd1337465e7bf6512899dba9af0b0bf938 Mon Sep 17 00:00:00 2001 From: Erica Marigold Date: Mon, 2 Dec 2024 16:57:31 +0000 Subject: [PATCH 05/39] style: apply stylua formatter --- src/generators/rojo/sourcemap.luau | 54 ++++++----- src/generators/rojo/sync_config.luau | 136 ++++++++++++++------------- src/init.luau | 16 ++-- 3 files changed, 109 insertions(+), 97 deletions(-) diff --git a/src/generators/rojo/sourcemap.luau b/src/generators/rojo/sourcemap.luau index d451b24..4d716b1 100644 --- a/src/generators/rojo/sourcemap.luau +++ b/src/generators/rojo/sourcemap.luau @@ -12,32 +12,36 @@ local PLATFORM_SEP = if process.os == "windows" then "\\" else "/" --- * The current process lacks permissions to a file --- * Any I/O error occurs return function(packageDirectory: string?): boolean - packageDirectory = packageDirectory or process.cwd - - -- A mapping of things to do depending on the file present - local PATH_ACTION_MAP: { [string]: (dir: string) -> number? } = { - ["default.project.json"] = function(dir) - return process.spawn("rojo", { "sourcemap", dir }, { - cwd = process.cwd, - env = process.env, - stdio = "forward", - }).code - end, + packageDirectory = packageDirectory or process.cwd - ["init.lua"] = stdio.write(serde.encode("json", { filePaths = { "init.lua" } }, false)), - ["init.luau"] = stdio.write(serde.encode("json", { filePaths = { "init.luau" } }, false)), - } + -- A mapping of things to do depending on the file present + local PATH_ACTION_MAP: { [string]: (dir: string) -> number? } = { + ["default.project.json"] = function(dir) + return process.spawn("rojo", { "sourcemap", dir }, { + cwd = process.cwd, + env = process.env, + stdio = "forward", + }).code + end, - -- We go through the action mappings in order of priority and check for the - -- file predicates, if present, we execute the action and report our status - for path, action in PATH_ACTION_MAP do - if fs.isFile(`{packageDirectory}{PLATFORM_SEP}{path}`) then - local status = action() - return if status ~= nil then status == 0 else true - end - end + ["init.lua"] = stdio.write( + serde.encode("json", { filePaths = { "init.lua" } }, false) + ), + ["init.luau"] = stdio.write( + serde.encode("json", { filePaths = { "init.luau" } }, false) + ), + } - -- If we reached so far, that must mean none of the file predicates matched, - -- so we return a `false` signifying an error - return false + -- We go through the action mappings in order of priority and check for the + -- file predicates, if present, we execute the action and report our status + for path, action in PATH_ACTION_MAP do + if fs.isFile(`{packageDirectory}{PLATFORM_SEP}{path}`) then + local status = action() + return if status ~= nil then status == 0 else true + end + end + + -- If we reached so far, that must mean none of the file predicates matched, + -- so we return a `false` signifying an error + return false end diff --git a/src/generators/rojo/sync_config.luau b/src/generators/rojo/sync_config.luau index 5318724..9ece19e 100644 --- a/src/generators/rojo/sync_config.luau +++ b/src/generators/rojo/sync_config.luau @@ -3,102 +3,110 @@ local process = require("@lune/process") local serde = require("@lune/serde") export type TreeProperties = { - Name: never?, - Parent: never?, + Name: never?, + Parent: never?, } export type TreeBase = { - ["$className"]: string?, - ["$ignoreUnknownInstances"]: boolean?, - ["$path"]: string | { optional: string }?, - ["$properties"]: TreeProperties?, + ["$className"]: string?, + ["$ignoreUnknownInstances"]: boolean?, + ["$path"]: string | { optional: string }?, + ["$properties"]: TreeProperties?, } export type TreeNormal = TreeBase & { - [string]: TreeNormal, -} & ({ ["$className"]: string } | { ["$path"]: string | { optional: string } }) + [string]: TreeNormal, +} & ({ ["$className"]: string } | { + ["$path"]: string | { optional: string }, +}) export type TreeService = TreeBase & { - [string]: TreeNormal, + [string]: TreeNormal, } export type DataModelTree = TreeBase & { - StarterPlayer: (TreeBase & { - StarterPlayerScripts: TreeService?, - StarterCharacterScripts: TreeService?, - [string]: TreeNormal, - })?, - [string]: TreeService, + StarterPlayer: (TreeBase & { + StarterPlayerScripts: TreeService?, + StarterCharacterScripts: TreeService?, + [string]: TreeNormal, + })?, + [string]: TreeService, } export type Tree = (DataModelTree & { - ["$className"]: "DataModel", + ["$className"]: "DataModel", }) | TreeNormal export type SyncConfig = { - name: string, - servePort: number?, - servePlaceIds: { number }?, - placeId: number?, - gameId: number?, - serveAddress: string?, - globIgnorePaths: { string }?, - tree: Tree, + name: string, + servePort: number?, + servePlaceIds: { number }?, + placeId: number?, + gameId: number?, + serveAddress: string?, + globIgnorePaths: { string }?, + tree: Tree, } local PLATFORM_SEP = if process.os == "windows" then "\\" else "/" ---- Generates a Rojo sync configuration file (`default.project.json`) from a list of +--- Generates a Rojo sync configuration file (`default.project.json`) from a list of --- input files to be included. --- ## Errors --- * The current process lacks permissions to a file --- * Any I/O error occurs -return function(packageDirectory: string?, files: { string }, writeToFile: boolean?): (boolean, string?) - packageDirectory = packageDirectory or process.cwd - local syncConfigPath = `{packageDirectory}{PLATFORM_SEP}default.project.json` - if fs.isFile(syncConfigPath) then - return true, nil - end +return function( + packageDirectory: string?, + files: { string }, + writeToFile: boolean? +): (boolean, string?) + packageDirectory = packageDirectory or process.cwd + local syncConfigPath = + `{packageDirectory}{PLATFORM_SEP}default.project.json` + if fs.isFile(syncConfigPath) then + return true, nil + end - local syncConfigTree = {} :: Tree + local syncConfigTree = {} :: Tree - for _, file in files do - -- Remove the `.lua` or `.luau` file extension from the file name - local name = string.gsub(file, ".luau?$", "") + for _, file in files do + -- Remove the `.lua` or `.luau` file extension from the file name + local name = string.gsub(file, ".luau?$", "") - if name == "init" then - syncConfigTree["$path"] = name - continue - end + if name == "init" then + syncConfigTree["$path"] = name + continue + end - syncConfigTree[name] = { - ["$path"] = file, - } - end + syncConfigTree[name] = { + ["$path"] = file, + } + end - -- If there isn't a top level path, we mark the entire thing as a Folder - if not syncConfigTree["$path"] then - syncConfigTree["$className"] = "Folder" - end + -- If there isn't a top level path, we mark the entire thing as a Folder + if not syncConfigTree["$path"] then + syncConfigTree["$className"] = "Folder" + end - -- If the config tree does not include pesde's downloaded roblox dependencies - -- directory, we add it as an optional one for the future, once dependencies - -- are installed - if not syncConfigTree["roblox_packages"] then - syncConfigTree["roblox_packages"] = { - ["$path"] = { - optional = "roblox_packages", - }, - } - end + -- If the config tree does not include pesde's downloaded roblox dependencies + -- directory, we add it as an optional one for the future, once dependencies + -- are installed + if not syncConfigTree["roblox_packages"] then + syncConfigTree["roblox_packages"] = { + ["$path"] = { + optional = "roblox_packages", + }, + } + end - -- Finally, we serialize the config to a JSON string and optionally write it - -- to the sync config path - local serializedConfig = serde.encode("json", { tree = syncConfigTree }, true) - if writeToFile then - fs.writeFile(syncConfigPath, serializedConfig) - end + -- Finally, we serialize the config to a JSON string and optionally write it + -- to the sync config path + local serializedConfig = + serde.encode("json", { tree = syncConfigTree }, true) + if writeToFile then + fs.writeFile(syncConfigPath, serializedConfig) + end - return true, serializedConfig + return true, serializedConfig end diff --git a/src/init.luau b/src/init.luau index a9ff34a..cf48aa9 100644 --- a/src/init.luau +++ b/src/init.luau @@ -1,8 +1,8 @@ -return { - generators = { - rojo = { - sourcemap = require("./generators/rojo/sourcemap"), - syncConfig = require("./generators/rojo/sync_config"), - }, - }, -} +return { + generators = { + rojo = { + sourcemap = require("./generators/rojo/sourcemap"), + syncConfig = require("./generators/rojo/sync_config"), + }, + }, +} From a6a52cac3b3b4ceac1e109fad6cf28706f8a5f19 Mon Sep 17 00:00:00 2001 From: Erica Marigold Date: Mon, 2 Dec 2024 17:03:11 +0000 Subject: [PATCH 06/39] chore(pkg): include MIT license and README in publish --- LICENSE.md | 7 +++++++ pesde.toml | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 LICENSE.md diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..7924a3f --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,7 @@ +Copyright © 2024 pesde-pkg + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/pesde.toml b/pesde.toml index d8d6917..ea6b026 100644 --- a/pesde.toml +++ b/pesde.toml @@ -5,7 +5,7 @@ description = "Scripts and other utilities for use with pesde" authors = ["dai (https://www.daimond113.com/)", "Erica Marigold "] repository = "https://github.com/pesde-pkg/scripts" license = "MIT" -includes = ["src/**/*.luau"] +includes = ["src/**/*.luau", "README.md", "LICENSE.md"] [target] environment = "lune" From 3a160c3e22dcb90ecb0394c3003c117bdf0db280 Mon Sep 17 00:00:00 2001 From: Erica Marigold Date: Tue, 3 Dec 2024 10:22:48 +0000 Subject: [PATCH 07/39] fix(rojo): move action map to top level and lazily evaluate --- src/generators/rojo/sourcemap.luau | 42 ++++++++++++++++-------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/src/generators/rojo/sourcemap.luau b/src/generators/rojo/sourcemap.luau index 4d716b1..612e518 100644 --- a/src/generators/rojo/sourcemap.luau +++ b/src/generators/rojo/sourcemap.luau @@ -5,6 +5,28 @@ local stdio = require("@lune/stdio") local PLATFORM_SEP = if process.os == "windows" then "\\" else "/" +-- A mapping of things to do depending on the file present +local PATH_ACTION_MAP: { [string]: (dir: string) -> number? } = { + ["default.project.json"] = function(dir) + return process.spawn("rojo", { "sourcemap", dir }, { + cwd = process.cwd, + env = process.env, + stdio = "forward", + }).code + end, + + ["init.lua"] = function() + return stdio.write( + serde.encode("json", { filePaths = { "init.lua" } }, false) + ) + end, + ["init.luau"] = function() + return stdio.write( + serde.encode("json", { filePaths = { "init.luau" } }, false) + ) + end, +} + --- Writes a Rojo sourcemap for the project in the provided directory or the current --- working directory to standard output. @@ -12,25 +34,7 @@ local PLATFORM_SEP = if process.os == "windows" then "\\" else "/" --- * The current process lacks permissions to a file --- * Any I/O error occurs return function(packageDirectory: string?): boolean - packageDirectory = packageDirectory or process.cwd - - -- A mapping of things to do depending on the file present - local PATH_ACTION_MAP: { [string]: (dir: string) -> number? } = { - ["default.project.json"] = function(dir) - return process.spawn("rojo", { "sourcemap", dir }, { - cwd = process.cwd, - env = process.env, - stdio = "forward", - }).code - end, - - ["init.lua"] = stdio.write( - serde.encode("json", { filePaths = { "init.lua" } }, false) - ), - ["init.luau"] = stdio.write( - serde.encode("json", { filePaths = { "init.luau" } }, false) - ), - } + packageDirectory = packageDirectory or process.cwd -- We go through the action mappings in order of priority and check for the -- file predicates, if present, we execute the action and report our status From 5f068ba1b86cd3c730b944726cc249608d395548 Mon Sep 17 00:00:00 2001 From: Erica Marigold Date: Tue, 3 Dec 2024 10:50:07 +0000 Subject: [PATCH 08/39] fix(rojo): pass directory to action from map --- src/generators/rojo/sourcemap.luau | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/generators/rojo/sourcemap.luau b/src/generators/rojo/sourcemap.luau index 612e518..72651f9 100644 --- a/src/generators/rojo/sourcemap.luau +++ b/src/generators/rojo/sourcemap.luau @@ -40,7 +40,7 @@ return function(packageDirectory: string?): boolean -- file predicates, if present, we execute the action and report our status for path, action in PATH_ACTION_MAP do if fs.isFile(`{packageDirectory}{PLATFORM_SEP}{path}`) then - local status = action() + local status = action(packageDirectory) return if status ~= nil then status == 0 else true end end From 83ab6bf97f0599ca249ce2c750fa018502c2a698 Mon Sep 17 00:00:00 2001 From: Erica Marigold Date: Tue, 3 Dec 2024 14:58:15 +0000 Subject: [PATCH 09/39] feat: test setup using frktest & more * Sets up a test runner system and includes unit tests for existing generators using Rojo's test files. * Configure Luau analysis and disables unnecessary lints. * Make sync config generator accept a third options argument with a `force` option to generate configs even when there is a `default.project.json` present in the `projectDir`. --- .gitignore | 2 + .gitmodules | 3 + .luaurc | 7 ++ .lune/roblox_sync_config_generator.luau | 18 +++-- .lune/tests/init.luau | 74 +++++++++++++++++++ .lune/tests/reporter.luau | 45 ++++++++++++ pesde.toml | 5 +- src/generators/rojo/sourcemap.luau | 11 ++- src/generators/rojo/sourcemap.spec.luau | 87 +++++++++++++++++++++++ src/generators/rojo/sync_config.luau | 19 +++-- src/generators/rojo/sync_config.spec.luau | 33 +++++++++ stylua.toml | 2 +- test-files/rojo | 1 + 13 files changed, 277 insertions(+), 30 deletions(-) create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 .luaurc create mode 100644 .lune/tests/init.luau create mode 100644 .lune/tests/reporter.luau create mode 100644 src/generators/rojo/sourcemap.spec.luau create mode 100644 src/generators/rojo/sync_config.spec.luau create mode 160000 test-files/rojo diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a6f599a --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*_packages/ +pesde.lock \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..d382b7a --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "test-files/rojo"] + path = test-files/rojo + url = https://github.com/rojo-rbx/rojo.git diff --git a/.luaurc b/.luaurc new file mode 100644 index 0000000..2cc120f --- /dev/null +++ b/.luaurc @@ -0,0 +1,7 @@ +{ + "languageMode": "nonstrict", + "lint": { + "*": true, + "TableOperations": false + } +} \ No newline at end of file diff --git a/.lune/roblox_sync_config_generator.luau b/.lune/roblox_sync_config_generator.luau index a233ae0..2f06e3e 100644 --- a/.lune/roblox_sync_config_generator.luau +++ b/.lune/roblox_sync_config_generator.luau @@ -1,19 +1,17 @@ local function enter(fn: (args: { string }) -> number?): never - local process = require("@lune/process") - local stdio = require("@lune/stdio") + local process = require("@lune/process") + local stdio = require("@lune/stdio") - local startTime = os.clock() - local exitCode = fn(table.clone(process.args)) + local startTime = os.clock() + local exitCode = fn(table.clone(process.args)) - stdio.write( - `done in {stdio.style("dim")}{string.format("%.2fs", os.clock() - startTime)}{stdio.style("reset")}!\n` - ) + stdio.write(`done in {stdio.style("dim")}{string.format("%.2fs", os.clock() - startTime)}{stdio.style("reset")}!\n`) - return process.exit(exitCode) + return process.exit(exitCode) end return enter(function(args: { string }): number? - local ok, _ = require("../src").generators.rojo.syncConfig(table.remove(args, 1), args, true) + local ok, _ = require("../src").generators.rojo.syncConfig(table.remove(args, 1), args, { writeToFile = true }) - return tonumber(ok) + return tonumber(ok) end) diff --git a/.lune/tests/init.luau b/.lune/tests/init.luau new file mode 100644 index 0000000..89cbbb1 --- /dev/null +++ b/.lune/tests/init.luau @@ -0,0 +1,74 @@ +--> Run tests using frktest runner + +local fs = require("@lune/fs") +local process = require("@lune/process") + +local frktest = require("../../lune_packages/frktest") +local reporter = require("./reporter") + +-- HACK: Cast require to allow for dynamic paths in strict mode +-- A more proper solution would be to use luau.load instead, but +-- frktest requires its global state to be modified by test suites +local require = require :: ( + path: string +) -> ( + test: typeof(setmetatable( + {} :: { + case: (name: string, fn: () -> nil) -> (), + suite: (name: string, fn: () -> ()) -> (), + }, + { __index = frktest.test } + )) +) -> () + +local function discoverTests(dir: string) + local tests = {} + for _, file in fs.readDir(dir) do + local fullPath = `{dir}/{file}` + + -- Look for files ending in `.spec.luau` as tests + if fs.isFile(fullPath) and string.sub(file, -10) == ".spec.luau" then + table.insert(tests, fullPath) + end + + -- Recurse for directories + if fs.isDir(fullPath) then + local moreTests = discoverTests(fullPath) + + -- Why are the indices starting at 0???? What???? + table.move(moreTests, 0, #moreTests, #tests, tests) + end + end + + return tests +end + +local allowedTests = process.args +for _, test in discoverTests("src") do + -- If we are given any arguments, we only run those tests, otherwise, + -- we run all the tests + + -- So, to include only a certain set of test files, you can provide either + -- the full path to the test file (with or without the extension) or the test + -- file name + local withoutExt = string.sub(test, 1, -6) + local isAllowed = #process.args == 0 + or table.find(allowedTests, `tests/{test}`) + or table.find(allowedTests, withoutExt) + or table.find(allowedTests, `tests/{withoutExt}`) + + local constructors = { + case = frktest.test.case, + suite = frktest.test.suite, + } + + if not isAllowed then + constructors.case = frktest.test.skip.case + constructors.suite = frktest.test.skip.suite + end + + require(`../../{test}`)(setmetatable(constructors, { __index = frktest.test })) +end + +reporter.init() +process.exit(tonumber(frktest.run())) \ No newline at end of file diff --git a/.lune/tests/reporter.luau b/.lune/tests/reporter.luau new file mode 100644 index 0000000..425d9c9 --- /dev/null +++ b/.lune/tests/reporter.luau @@ -0,0 +1,45 @@ +local stdio = require("@lune/stdio") + +local frktest = require("../../lune_packages/frktest") +local Reporter = frktest._reporters.lune_console_reporter + +local STYLE = table.freeze({ + suite = function(name: string) + return `{stdio.style("bold")}{stdio.color("purple")}SUITE{stdio.style("reset")} {name}` + end, + + report = function(name: string, state: "run" | "success" | "error" | "skip") + local state_color: stdio.Color = if state == "run" + then "white" + elseif state == "success" then "green" + elseif state == "error" then "red" + elseif state == "skip" then "yellow" + else error("Invalid test state") + return ` {stdio.style("bold")}{stdio.color(state_color)}{if state == "skip" then "SKIP" else "TEST"}{stdio.style( + "reset" + )} {name}` + end, +}) + +local ReporterExt = {} +function ReporterExt.init() + frktest.test.on_suite_enter(function(suite) + print(STYLE.suite(suite.name)) + end) + + frktest.test.on_suite_leave(function() + stdio.write("\n") + end) + + frktest.test.on_test_leave(function(test) + print(STYLE.report(test.name, if test.failed then "error" else "success")) + end) + + frktest.test.on_test_skipped(function(test) + print(STYLE.report(test.name, "skip")) + end) + + Reporter.init() +end + +return setmetatable(ReporterExt, { __index = Reporter }) \ No newline at end of file diff --git a/pesde.toml b/pesde.toml index ea6b026..dca8983 100644 --- a/pesde.toml +++ b/pesde.toml @@ -5,7 +5,7 @@ description = "Scripts and other utilities for use with pesde" authors = ["dai (https://www.daimond113.com/)", "Erica Marigold "] repository = "https://github.com/pesde-pkg/scripts" license = "MIT" -includes = ["src/**/*.luau", "README.md", "LICENSE.md"] +includes = ["src/**/*.luau", "pesde.toml", "README.md", "LICENSE.md"] [target] environment = "lune" @@ -13,3 +13,6 @@ lib = "src/init.luau" [indices] default = "https://github.com/daimond113/pesde-index" + +[dev_dependencies] +frktest = { name = "itsfrank/frktest", version = "^0.0.2" } diff --git a/src/generators/rojo/sourcemap.luau b/src/generators/rojo/sourcemap.luau index 72651f9..4402d9f 100644 --- a/src/generators/rojo/sourcemap.luau +++ b/src/generators/rojo/sourcemap.luau @@ -16,14 +16,10 @@ local PATH_ACTION_MAP: { [string]: (dir: string) -> number? } = { end, ["init.lua"] = function() - return stdio.write( - serde.encode("json", { filePaths = { "init.lua" } }, false) - ) + return stdio.write(serde.encode("json", { filePaths = { "init.lua" } }, false)) end, ["init.luau"] = function() - return stdio.write( - serde.encode("json", { filePaths = { "init.luau" } }, false) - ) + return stdio.write(serde.encode("json", { filePaths = { "init.luau" } }, false)) end, } @@ -32,9 +28,10 @@ local PATH_ACTION_MAP: { [string]: (dir: string) -> number? } = { --- ## Errors --- * The current process lacks permissions to a file +--- * Failure to spawn `rojo` command --- * Any I/O error occurs return function(packageDirectory: string?): boolean - packageDirectory = packageDirectory or process.cwd + packageDirectory = packageDirectory or process.cwd -- We go through the action mappings in order of priority and check for the -- file predicates, if present, we execute the action and report our status diff --git a/src/generators/rojo/sourcemap.spec.luau b/src/generators/rojo/sourcemap.spec.luau new file mode 100644 index 0000000..bc2bb5f --- /dev/null +++ b/src/generators/rojo/sourcemap.spec.luau @@ -0,0 +1,87 @@ +local fs = require("@lune/fs") +local luau = require("@lune/luau") +local process = require("@lune/process") +local regex = require("@lune/regex") +local serde = require("@lune/serde") + +local frktest = require("../../../lune_packages/frktest") +local check = frktest.assert.check + +local TEST_PROJECTS_DIR = "./test-files/rojo/test-projects" +local TEST_PROJECT_EXCLUDES = { + "json_model", + "bad_json_model", + "plugins", + "legacy-0.5.x-unknown-names", +} + +local BUILTIN_PATCHES: { + [string]: { [string]: (...any) -> ...any? }, +} = { + ["@lune/process"] = { + spawn = function(program: string, params: { string }, options: process.SpawnOptions): process.SpawnResult + local patchedOptions: process.SpawnOptions = options or {} + patchedOptions.stdio = "default" + + local result = process.spawn(program, params, patchedOptions) + + -- First we make sure the command exited properly + assert(result.ok, `Expected \`rojo\` command to not error, got:\n{string.rep(" ", 6)}{result.stderr}`) + + -- We also make sure that the output JSON was valid + serde.decode("json", result.stdout) + + return result + end, + }, + + ["@lune/stdio"] = { + write = function(msg: string) + -- Only make sure output JSON is valid + serde.decode("json", msg) + end, + }, +} + +local function requireWithPatches(path: string) + for builtin, patch in BUILTIN_PATCHES do + if path == builtin then + return setmetatable(patch, { __index = require(builtin) }) + end + end + + return require(path) +end + +return function(test: typeof(frktest.test)) + test.suite("Generates Rojo sourcemaps for test projects successfully", function() + for _, file in fs.readDir(TEST_PROJECTS_DIR) do + if table.find(TEST_PROJECT_EXCLUDES, file) then + -- It does not make sense to test sourcemap generation for some of the test projects + continue + end + + local testProject = `{TEST_PROJECTS_DIR}/{file}` + test.case(file, function() + -- If a file starts with `bad_` but not `bad_meta_`, we should expect a failure + -- Also, sorry about this shitty regex, regex-rs does not support look-around :( + local isBadMeta = regex.new("^bad_[^m]|^bad_m[^e]|^bad_me[^t]|^bad_met[^a]") + local expect = if isBadMeta:isMatch(file) then check.should_error else check.should_not_error + + expect(function() + -- We override the require which applies some patches to some builtins, mainly preventing + -- command stdout forwarding and `stdio.write` outputs to stdout in order to not fill + -- test ouput with large amounts of JSON data + local sourcemap: (string) -> boolean = + luau.load(fs.readFile("./src/generators/rojo/sourcemap.luau"), { + environment = { + require = requireWithPatches, + }, + })() + + return check.is_true(sourcemap(testProject)) + end) + end) + end + end) +end diff --git a/src/generators/rojo/sync_config.luau b/src/generators/rojo/sync_config.luau index 9ece19e..cc6f95a 100644 --- a/src/generators/rojo/sync_config.luau +++ b/src/generators/rojo/sync_config.luau @@ -56,15 +56,13 @@ local PLATFORM_SEP = if process.os == "windows" then "\\" else "/" --- ## Errors --- * The current process lacks permissions to a file --- * Any I/O error occurs -return function( - packageDirectory: string?, - files: { string }, - writeToFile: boolean? -): (boolean, string?) +return function(packageDirectory: string?, files: { string }, options: { + writeToFile: boolean?, + force: boolean?, +}): (boolean, string?) packageDirectory = packageDirectory or process.cwd - local syncConfigPath = - `{packageDirectory}{PLATFORM_SEP}default.project.json` - if fs.isFile(syncConfigPath) then + local syncConfigPath = `{packageDirectory}{PLATFORM_SEP}default.project.json` + if fs.isFile(syncConfigPath) and not options.force then return true, nil end @@ -102,9 +100,8 @@ return function( -- Finally, we serialize the config to a JSON string and optionally write it -- to the sync config path - local serializedConfig = - serde.encode("json", { tree = syncConfigTree }, true) - if writeToFile then + local serializedConfig = serde.encode("json", { tree = syncConfigTree }, true) + if options.writeToFile then fs.writeFile(syncConfigPath, serializedConfig) end diff --git a/src/generators/rojo/sync_config.spec.luau b/src/generators/rojo/sync_config.spec.luau new file mode 100644 index 0000000..6069bb3 --- /dev/null +++ b/src/generators/rojo/sync_config.spec.luau @@ -0,0 +1,33 @@ +local fs = require("@lune/fs") +local serde = require("@lune/serde") + +local frktest = require("../../../lune_packages/frktest") +local check = frktest.assert.check + +local syncConfig = require("./sync_config") + +local TEST_PROJECTS_DIRS = { + "./test-files/rojo/test-projects", + "./test-files/rojo/rojo-test/serve-tests", +} + +return function(test: typeof(frktest.test)) + test.suite("Generates Rojo valid sync configs", function() + for _, dir in TEST_PROJECTS_DIRS do + for _, file in fs.readDir(dir) do + local fullPath = `{dir}/{file}` + test.case(`{file}`, function() + local ok, config = syncConfig(fullPath, fs.readDir(fullPath), { writeToFile = false, force = true }) + check.is_true(ok) + + -- Make sure that the generated config and the real configs are similar + local generatedConfig, realConfig = + serde.decode("json", config), + serde.decode("json", fs.readFile(`{fullPath}/default.project.json`)) + + check.table.contains(realConfig, generatedConfig) + end) + end + end + end) +end diff --git a/stylua.toml b/stylua.toml index 2ab5776..8fa16f6 100644 --- a/stylua.toml +++ b/stylua.toml @@ -4,7 +4,7 @@ indent_type = "Spaces" call_parentheses = "Always" indent_width = 4 -column_width = 80 +# column_width = 80 [sort_requires] enabled = true diff --git a/test-files/rojo b/test-files/rojo new file mode 160000 index 0000000..b7d3394 --- /dev/null +++ b/test-files/rojo @@ -0,0 +1 @@ +Subproject commit b7d3394464e0ffb350b9c8481399cc5845c10f07 From c1a4bf5bd7309547813e88814019e7ffdbbc7257 Mon Sep 17 00:00:00 2001 From: Erica Marigold Date: Tue, 3 Dec 2024 15:01:34 +0000 Subject: [PATCH 10/39] chore: update lockfile cache --- pesde.lock | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/pesde.lock b/pesde.lock index 6e78c33..29244a6 100644 --- a/pesde.lock +++ b/pesde.lock @@ -1,3 +1,21 @@ name = "pesde/scripts" version = "0.1.0" target = "lune" + +[graph."itsfrank/frktest"."0.0.2 lune"] +direct = ["frktest", { name = "itsfrank/frktest", version = "^0.0.2" }, "dev"] +resolved_ty = "dev" + +[graph."itsfrank/frktest"."0.0.2 lune".target] +environment = "lune" +lib = "src/_pesde_init.luau" + +[graph."itsfrank/frktest"."0.0.2 lune".pkg_ref] +ref_ty = "pesde" +name = "itsfrank/frktest" +version = "0.0.2" +index_url = "https://github.com/daimond113/pesde-index" + +[graph."itsfrank/frktest"."0.0.2 lune".pkg_ref.target] +environment = "lune" +lib = "src/_pesde_init.luau" From add460c3ccd52ebba79d739f7748e7d8459e192c Mon Sep 17 00:00:00 2001 From: Erica Marigold Date: Tue, 3 Dec 2024 15:05:47 +0000 Subject: [PATCH 11/39] style: apply stylua formatter --- src/generators/rojo/sync_config.luau | 12 +++-- src/generators/rojo/sync_config.spec.luau | 66 +++++++++++------------ 2 files changed, 41 insertions(+), 37 deletions(-) diff --git a/src/generators/rojo/sync_config.luau b/src/generators/rojo/sync_config.luau index cc6f95a..7b06134 100644 --- a/src/generators/rojo/sync_config.luau +++ b/src/generators/rojo/sync_config.luau @@ -56,10 +56,14 @@ local PLATFORM_SEP = if process.os == "windows" then "\\" else "/" --- ## Errors --- * The current process lacks permissions to a file --- * Any I/O error occurs -return function(packageDirectory: string?, files: { string }, options: { - writeToFile: boolean?, - force: boolean?, -}): (boolean, string?) +return function( + packageDirectory: string?, + files: { string }, + options: { + writeToFile: boolean?, + force: boolean?, + } +): (boolean, string?) packageDirectory = packageDirectory or process.cwd local syncConfigPath = `{packageDirectory}{PLATFORM_SEP}default.project.json` if fs.isFile(syncConfigPath) and not options.force then diff --git a/src/generators/rojo/sync_config.spec.luau b/src/generators/rojo/sync_config.spec.luau index 6069bb3..02a5844 100644 --- a/src/generators/rojo/sync_config.spec.luau +++ b/src/generators/rojo/sync_config.spec.luau @@ -1,33 +1,33 @@ -local fs = require("@lune/fs") -local serde = require("@lune/serde") - -local frktest = require("../../../lune_packages/frktest") -local check = frktest.assert.check - -local syncConfig = require("./sync_config") - -local TEST_PROJECTS_DIRS = { - "./test-files/rojo/test-projects", - "./test-files/rojo/rojo-test/serve-tests", -} - -return function(test: typeof(frktest.test)) - test.suite("Generates Rojo valid sync configs", function() - for _, dir in TEST_PROJECTS_DIRS do - for _, file in fs.readDir(dir) do - local fullPath = `{dir}/{file}` - test.case(`{file}`, function() - local ok, config = syncConfig(fullPath, fs.readDir(fullPath), { writeToFile = false, force = true }) - check.is_true(ok) - - -- Make sure that the generated config and the real configs are similar - local generatedConfig, realConfig = - serde.decode("json", config), - serde.decode("json", fs.readFile(`{fullPath}/default.project.json`)) - - check.table.contains(realConfig, generatedConfig) - end) - end - end - end) -end +local fs = require("@lune/fs") +local serde = require("@lune/serde") + +local frktest = require("../../../lune_packages/frktest") +local check = frktest.assert.check + +local syncConfig = require("./sync_config") + +local TEST_PROJECTS_DIRS = { + "./test-files/rojo/test-projects", + "./test-files/rojo/rojo-test/serve-tests", +} + +return function(test: typeof(frktest.test)) + test.suite("Generates Rojo valid sync configs", function() + for _, dir in TEST_PROJECTS_DIRS do + for _, file in fs.readDir(dir) do + local fullPath = `{dir}/{file}` + test.case(`{file}`, function() + local ok, config = syncConfig(fullPath, fs.readDir(fullPath), { writeToFile = false, force = true }) + check.is_true(ok) + + -- Make sure that the generated config and the real configs are similar + local generatedConfig, realConfig = + serde.decode("json", config), + serde.decode("json", fs.readFile(`{fullPath}/default.project.json`)) + + check.table.contains(realConfig, generatedConfig) + end) + end + end + end) +end From 392a8f2c9aed93d3cd61d3de018eba6dd0f0c993 Mon Sep 17 00:00:00 2001 From: Erica Marigold Date: Tue, 3 Dec 2024 15:35:24 +0000 Subject: [PATCH 12/39] chore(lune): add time consume per test statistic --- .lune/tests/channel.luau | 48 +++++++++++++++++++++++++ .lune/tests/reporter.luau | 75 ++++++++++++++++++++++++--------------- 2 files changed, 94 insertions(+), 29 deletions(-) create mode 100644 .lune/tests/channel.luau diff --git a/.lune/tests/channel.luau b/.lune/tests/channel.luau new file mode 100644 index 0000000..b34543d --- /dev/null +++ b/.lune/tests/channel.luau @@ -0,0 +1,48 @@ +--- An MPSC synchronization primitive powered by Lua upvalues which retains only +--- one value at a time. + +--- ## Usage +--- ```luau +--- local send, recv = watch((nil :: any) :: string) +--- task.delay(5, send, "hello, world!") +--- task.spawn(function() +--- local value = recv() +--- print("recevied value:", value) +--- end) +--- ``` +type Watch = { + value: T?, + receivers: { thread }, +} + +--- Crates a new `Watch` channel, returning its send and receive handles. +local function chan(_phantom: T): ((T) -> (), () -> T?) + local watch: Watch = { + value = nil, + receivers = {}, + } + + local function send(value: T) + watch.value = value + + for _, receiver in watch.receivers do + coroutine.resume(receiver, value) + end + end + + local function recv(): T + local value = watch.value + watch.value = nil + + if value == nil then + table.insert(watch.receivers, coroutine.running()) + return coroutine.yield() + end + + return value :: T + end + + return send, recv +end + +return chan \ No newline at end of file diff --git a/.lune/tests/reporter.luau b/.lune/tests/reporter.luau index 425d9c9..fb81dfa 100644 --- a/.lune/tests/reporter.luau +++ b/.lune/tests/reporter.luau @@ -3,43 +3,60 @@ local stdio = require("@lune/stdio") local frktest = require("../../lune_packages/frktest") local Reporter = frktest._reporters.lune_console_reporter -local STYLE = table.freeze({ - suite = function(name: string) - return `{stdio.style("bold")}{stdio.color("purple")}SUITE{stdio.style("reset")} {name}` - end, +local watch = require("./channel") - report = function(name: string, state: "run" | "success" | "error" | "skip") - local state_color: stdio.Color = if state == "run" - then "white" - elseif state == "success" then "green" - elseif state == "error" then "red" - elseif state == "skip" then "yellow" - else error("Invalid test state") - return ` {stdio.style("bold")}{stdio.color(state_color)}{if state == "skip" then "SKIP" else "TEST"}{stdio.style( - "reset" - )} {name}` - end, +local STYLE = table.freeze({ + suite = function(name: string) + return `{stdio.style("bold")}{stdio.color("purple")}SUITE{stdio.style("reset")} {name}` + end, + + report = function(name: string, state: "run" | "success" | "error" | "skip", elapsed: number) + local state_color: stdio.Color = if state == "run" + then "white" + elseif state == "success" then "green" + elseif state == "error" then "red" + elseif state == "skip" then "yellow" + else error("Invalid test state") + return ` {stdio.style("bold")}{stdio.color(state_color)}{if state == "skip" then "SKIP" else "TEST"}{stdio.style( + "reset" + )} {name} [{stdio.style("dim")}{string.format("%.2fms", elapsed)}{stdio.style("reset")}]` + end, }) local ReporterExt = {} function ReporterExt.init() - frktest.test.on_suite_enter(function(suite) - print(STYLE.suite(suite.name)) - end) + frktest.test.on_suite_enter(function(suite) + print(STYLE.suite(suite.name)) + end) - frktest.test.on_suite_leave(function() - stdio.write("\n") - end) + frktest.test.on_suite_leave(function() + stdio.write("\n") + end) - frktest.test.on_test_leave(function(test) - print(STYLE.report(test.name, if test.failed then "error" else "success")) - end) + local send_ts, recv_ts = watch((nil :: any) :: number) + + frktest.test.on_test_enter(function() + -- Send over some high precision timestamp when the test starts + return send_ts(os.clock()) + end) - frktest.test.on_test_skipped(function(test) - print(STYLE.report(test.name, "skip")) - end) + frktest.test.on_test_leave(function(test) + print( + STYLE.report( + test.name, + if test.failed then "error" else "success", - Reporter.init() + -- Await receival of the timestamp and convert the difference to ms + (os.clock() - recv_ts()) * 1000 + ) + ) + end) + + frktest.test.on_test_skipped(function(test) + print(STYLE.report(test.name, "skip")) + end) + + Reporter.init() end -return setmetatable(ReporterExt, { __index = Reporter }) \ No newline at end of file +return setmetatable(ReporterExt, { __index = Reporter }) From 9fc9e44522d3a56704d8154fbc354d8cb9046bdb Mon Sep 17 00:00:00 2001 From: Erica Marigold Date: Wed, 4 Dec 2024 05:30:44 +0000 Subject: [PATCH 13/39] chore(lune): fix typo in `Watch` channel implementation doc example --- .lune/tests/channel.luau | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.lune/tests/channel.luau b/.lune/tests/channel.luau index b34543d..03cf12c 100644 --- a/.lune/tests/channel.luau +++ b/.lune/tests/channel.luau @@ -7,7 +7,7 @@ --- task.delay(5, send, "hello, world!") --- task.spawn(function() --- local value = recv() ---- print("recevied value:", value) +--- print("received value:", value) --- end) --- ``` type Watch = { From ca5f2d53c86a2d530c5cbdec3725ad0507330d66 Mon Sep 17 00:00:00 2001 From: Erica Marigold Date: Wed, 4 Dec 2024 05:49:01 +0000 Subject: [PATCH 14/39] chore(lune): restructure util libraries and include dev scripts * Moved all util libs like `channel` into a `lib/` directory within `.lune`. Also includes a builder-pattern exec lib. * Added dev scripts for fmt (stylua) and typecheck (luau-lsp). --- .lune/fmt.luau | 7 ++ .lune/{tests => lib}/channel.luau | 0 .lune/lib/exec.luau | 103 ++++++++++++++++++++++++ .lune/roblox_sync_config_generator.luau | 2 + .lune/sourcemap_generator.luau | 2 + .lune/tests/reporter.luau | 2 +- .lune/typecheck.luau | 16 ++++ 7 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 .lune/fmt.luau rename .lune/{tests => lib}/channel.luau (100%) create mode 100644 .lune/lib/exec.luau create mode 100644 .lune/typecheck.luau diff --git a/.lune/fmt.luau b/.lune/fmt.luau new file mode 100644 index 0000000..aad2818 --- /dev/null +++ b/.lune/fmt.luau @@ -0,0 +1,7 @@ +--> Run stylua to check for formatting errors + +local process = require("@lune/process") + +local CommandBuilder = require("./lib/exec") + +process.exit(CommandBuilder.new("stylua"):withArg("."):withArgs(process.args):withStdioStrategy("forward"):exec().code) diff --git a/.lune/tests/channel.luau b/.lune/lib/channel.luau similarity index 100% rename from .lune/tests/channel.luau rename to .lune/lib/channel.luau diff --git a/.lune/lib/exec.luau b/.lune/lib/exec.luau new file mode 100644 index 0000000..8ce24da --- /dev/null +++ b/.lune/lib/exec.luau @@ -0,0 +1,103 @@ +--> lib: Builder pattern class to spawn child processes + +local process = require("@lune/process") +local stdio = require("@lune/stdio") + +local CommandBuilder = {} + +export type CommandBuilder = typeof(setmetatable({} :: CommandBuilderFields, { __index = CommandBuilder })) +type CommandBuilderFields = { + program: string, + args: { string }, + stdioStrategy: IoStrategyMapping?, +} +export type StdioStrategy = "pipe" | "forward" | "none" +export type IoStrategyMapping = { + stdout: StdioStrategy?, + stderr: StdioStrategy?, +} + +local DEFAULT_STDIO_STRATEGY: IoStrategyMapping = { + stdout = "pipe", + stderr = "pipe", +} +function CommandBuilder.new(program: string) + return setmetatable( + { + program = program, + args = {}, + stdioStrategy = nil, + } :: CommandBuilderFields, + { + __index = CommandBuilder, + } + ) +end + +function CommandBuilder.withArg(self: CommandBuilder, arg: string): CommandBuilder + table.insert(self.args, arg) + return self +end + +function CommandBuilder.withArgs(self: CommandBuilder, args: { string }): CommandBuilder + for _, arg in args do + self:withArg(arg) + end + + return self +end + +function CommandBuilder.withStdioStrategy( + self: CommandBuilder, + strategy: StdioStrategy | IoStrategyMapping +): CommandBuilder + self.stdioStrategy = if typeof(strategy) == "string" + then { + stdout = strategy, + stderr = strategy, + } + else strategy + return self +end + +local function intoSpawnOptionsStdioKind(strategy: StdioStrategy): process.SpawnOptionsStdioKind + if strategy == "pipe" then + return "default" + end + + if strategy == "forward" then + return "forward" + end + + if strategy == "none" then + return "none" + end + + error(`Non-strategy provided: {strategy}`) +end + +function CommandBuilder.exec(self: CommandBuilder): process.SpawnResult + print("$", stdio.style("dim") .. self.program, table.concat(self.args, " ") .. stdio.style("reset")) + + local function translateIoStrategyMappings(mappings: IoStrategyMapping) + local translatedMappings: process.SpawnOptionsStdio = {} + for field, value in mappings do + translatedMappings[field] = intoSpawnOptionsStdioKind(value) + end + + return translatedMappings + end + + local child = process.spawn(self.program, self.args, { + shell = true, + stdio = translateIoStrategyMappings(self.stdioStrategy or DEFAULT_STDIO_STRATEGY), + }) + + if not child.ok then + print(`\n{stdio.color("red")}[luau-lsp]{stdio.color("reset")} Exited with code`, child.code) + end + + return child +end + +return CommandBuilder diff --git a/.lune/roblox_sync_config_generator.luau b/.lune/roblox_sync_config_generator.luau index 2f06e3e..13d1fca 100644 --- a/.lune/roblox_sync_config_generator.luau +++ b/.lune/roblox_sync_config_generator.luau @@ -1,3 +1,5 @@ +--> Generates a Rojo sync config from a list of input files + local function enter(fn: (args: { string }) -> number?): never local process = require("@lune/process") local stdio = require("@lune/stdio") diff --git a/.lune/sourcemap_generator.luau b/.lune/sourcemap_generator.luau index 2037a13..e98a3ef 100644 --- a/.lune/sourcemap_generator.luau +++ b/.lune/sourcemap_generator.luau @@ -1,3 +1,5 @@ +--> Generates a Rojo sourcemap for a provided project directory + local function enter(fn: (args: { string }) -> number?): never local process = require("@lune/process") local stdio = require("@lune/stdio") diff --git a/.lune/tests/reporter.luau b/.lune/tests/reporter.luau index fb81dfa..726874f 100644 --- a/.lune/tests/reporter.luau +++ b/.lune/tests/reporter.luau @@ -3,7 +3,7 @@ local stdio = require("@lune/stdio") local frktest = require("../../lune_packages/frktest") local Reporter = frktest._reporters.lune_console_reporter -local watch = require("./channel") +local watch = require("../lib/channel") local STYLE = table.freeze({ suite = function(name: string) diff --git a/.lune/typecheck.luau b/.lune/typecheck.luau new file mode 100644 index 0000000..2818b79 --- /dev/null +++ b/.lune/typecheck.luau @@ -0,0 +1,16 @@ +--> Run luau-lsp analysis to check for type errors + +local process = require("@lune/process") + +local CommandBuilder = require("./lib/exec") + +process.exit( + CommandBuilder.new("~/.rokit/bin/luau-lsp") + :withArg("analyze") + :withArgs({ "--settings", ".vscode/settings.json" }) + :withArgs({ "--ignore", "'**/.pesde/**'" }) + :withArgs({ "--ignore", "'./test-files/**'" }) + :withArg(".") + :withStdioStrategy("forward") + :exec().code +) \ No newline at end of file From b7ccc0570241371c62700f925e4186a773aaf6aa Mon Sep 17 00:00:00 2001 From: Erica Marigold Date: Wed, 4 Dec 2024 06:05:54 +0000 Subject: [PATCH 15/39] chore(lune): remove unneeded status from frktest reporter --- .lune/tests/reporter.luau | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.lune/tests/reporter.luau b/.lune/tests/reporter.luau index 726874f..a209997 100644 --- a/.lune/tests/reporter.luau +++ b/.lune/tests/reporter.luau @@ -10,10 +10,8 @@ local STYLE = table.freeze({ return `{stdio.style("bold")}{stdio.color("purple")}SUITE{stdio.style("reset")} {name}` end, - report = function(name: string, state: "run" | "success" | "error" | "skip", elapsed: number) - local state_color: stdio.Color = if state == "run" - then "white" - elseif state == "success" then "green" + report = function(name: string, state: "success" | "error" | "skip", elapsed: number) + local state_color: stdio.Color = if state == "success" then "green" elseif state == "error" then "red" elseif state == "skip" then "yellow" else error("Invalid test state") From 54a3f6869b2965d64853842bcb14d442ed15b4ab Mon Sep 17 00:00:00 2001 From: Erica Marigold Date: Wed, 4 Dec 2024 06:27:20 +0000 Subject: [PATCH 16/39] chore(lune): fix test skipping * Previously, skipping tests using arguments of selective files to include did not work correctly due to our directory structure. This has been fixed. * On test skip, the reporter would tyr to get the style for the report with no timestamp argument, causing a string formatting with nil error. For skips, we force the timestamp to be zero now. --- .lune/tests/init.luau | 16 +++++++++++----- .lune/tests/reporter.luau | 4 ++-- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/.lune/tests/init.luau b/.lune/tests/init.luau index 89cbbb1..8f6e7ba 100644 --- a/.lune/tests/init.luau +++ b/.lune/tests/init.luau @@ -49,13 +49,19 @@ for _, test in discoverTests("src") do -- we run all the tests -- So, to include only a certain set of test files, you can provide either - -- the full path to the test file (with or without the extension) or the test - -- file name - local withoutExt = string.sub(test, 1, -6) + -- the full path of the test file or name of the test file, with or without + -- the `.spec.luau` extension + local baseName = string.match(test, "([^/\\]+)$") + + local withoutExt = string.sub(test, 1, -11) + local baseNameWithoutExt = string.match(withoutExt, "([^/\\]+)$") + local isAllowed = #process.args == 0 - or table.find(allowedTests, `tests/{test}`) + or table.find(allowedTests, test) or table.find(allowedTests, withoutExt) - or table.find(allowedTests, `tests/{withoutExt}`) + or table.find(allowedTests, baseName) + or table.find(allowedTests, baseNameWithoutExt) + local constructors = { case = frktest.test.case, diff --git a/.lune/tests/reporter.luau b/.lune/tests/reporter.luau index a209997..887851d 100644 --- a/.lune/tests/reporter.luau +++ b/.lune/tests/reporter.luau @@ -45,13 +45,13 @@ function ReporterExt.init() if test.failed then "error" else "success", -- Await receival of the timestamp and convert the difference to ms - (os.clock() - recv_ts()) * 1000 + (os.clock() - recv_ts()) * 1000 ) ) end) frktest.test.on_test_skipped(function(test) - print(STYLE.report(test.name, "skip")) + print(STYLE.report(test.name, "skip", 0)) end) Reporter.init() From e203656294062fd7335e852bc6ce0b7b721ac54e Mon Sep 17 00:00:00 2001 From: Erica Marigold Date: Wed, 4 Dec 2024 11:58:52 +0000 Subject: [PATCH 17/39] chore(pkg): include additional metadata about scripts root dir --- pesde.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pesde.toml b/pesde.toml index dca8983..77d667c 100644 --- a/pesde.toml +++ b/pesde.toml @@ -7,6 +7,9 @@ repository = "https://github.com/pesde-pkg/scripts" license = "MIT" includes = ["src/**/*.luau", "pesde.toml", "README.md", "LICENSE.md"] +[meta] +scripts_root = ".lune" + [target] environment = "lune" lib = "src/init.luau" From 22552c0f80514e39ccc2c7564ee26c58ed21e861 Mon Sep 17 00:00:00 2001 From: Erica Marigold Date: Sun, 8 Dec 2024 11:09:53 +0000 Subject: [PATCH 18/39] refactor: restructure to use script package system General restructuring ===================== * Restructures to workspace with script packages for each tool choice. * Include build script to generate package for every supported tool choice in library into the `.pesde/` directory. * Remove testing .lune scripts for Rojo. Manifest changes ================ * Exclude `.spec.luau` test files for library package. * Include .pesde directory for library package. * Add pathfs dev-dependency. * Include metadata for build script package codegen. * Use new git URL for index repo. * Setup workspace including top level itself & tool choice script packages. These changes require an unstable version of pesde: https://github.com/pesde-pkg/pesde/commit/2b2d280fe0faf53248b223980ff54c474d41f57d. --- .gitignore | 7 +- .lune/build.luau | 164 ++++++++++++++++++++++++ .lune/lib/manifest.luau | 71 ++++++++++ .lune/roblox_sync_config_generator.luau | 19 --- .lune/sourcemap_generator.luau | 21 --- .pesde/.gitkeep | 0 .vscode/settings.json | 3 +- pesde.lock | 26 +++- pesde.toml | 30 +++-- src/init.luau | 2 +- 10 files changed, 291 insertions(+), 52 deletions(-) create mode 100644 .lune/build.luau create mode 100644 .lune/lib/manifest.luau delete mode 100644 .lune/roblox_sync_config_generator.luau delete mode 100644 .lune/sourcemap_generator.luau create mode 100644 .pesde/.gitkeep diff --git a/.gitignore b/.gitignore index a6f599a..e57fe2f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,7 @@ +# Pesde packages *_packages/ -pesde.lock \ No newline at end of file +pesde.lock + +# Generated scripts +.pesde/* +!.pesde/.gitkeep diff --git a/.lune/build.luau b/.lune/build.luau new file mode 100644 index 0000000..94652b6 --- /dev/null +++ b/.lune/build.luau @@ -0,0 +1,164 @@ +--> Generates sync config and sourcemap scripts for supported tools + +local process = require("@lune/process") +local serde = require("@lune/serde") +local stdio = require("@lune/stdio") +local task = require("@lune/task") + +local lib = require("../src") +local manifest = require("./lib/manifest") +local pathfs = require("../lune_packages/pathfs") + +type ToolChoice = "rojo" +type ManifestExt = { + scripts: { + [ToolChoice]: { + version: string, + tool_dependencies: { { [string]: manifest.DependencySpecifier } }, + }, + }, +} + +local SCRIPTS_DIR = pathfs.getAbsolutePathOf(pathfs.Path.from(".pesde")) +local MANIFEST = manifest(nil, (nil :: any) :: { meta: ManifestExt }) +local SCRIPTS = { + syncConfigGenerator = [[local process = require("@lune/process") + +local args = table.clone(process.args) +local ok, _ = +require("./lune_packages/core").generators.%s.syncConfig(table.remove(args, 1), args, { writeToFile = true }) + +process.exit(tonumber(ok))]], + + sourcemapGenerator = [[local process = require("@lune/process") + +return process.exit( + tonumber(require("./lune_packages/core").generators.%s.sourcemap(process.args[1])) +)]], +} + +local function logPrefix(type: "error" | "info") + local statusColor: stdio.Color = if type == "error" + then "red" + elseif type == "info" then "green" + else error(`Invalid type: {type}`) + + return `pesde::{stdio.style("bold")}{stdio.color(statusColor)}{type}{stdio.color("reset")}` +end + +local INFO_PREFIX = `[{logPrefix("info")}]` +local _ERROR_PREFIX = `[{logPrefix("error")}]` + +local function installDeps(): number + local STDOUT_LINE_PREFIX = `[pesde::{logPrefix("info")}]` + local PESDE_ERROR_PREFIX = `[pesde::{logPrefix("error")}]` + local SPINNER_STATES = { '⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏' } + + stdio.write(`{STDOUT_LINE_PREFIX} Installing dependencies with pesde`) + + -- Spawn our thread to display the spinner + local spinnerThread = task.spawn(function() + -- Hide the cursor + stdio.write("\x1b[?25l") + + -- Display the spinner + while true do + for _, state in SPINNER_STATES do + stdio.write(` {state}`) + stdio.write( + -- Moves the cursor back 1 spot and clears everything after + "\x1b[2D\x1b[0K" + ) + end + end + end) + + -- `process_spawn` is an async rust function, so tokio spawns a hardware + -- thread and mlua will yield the current thread, allowing for `spinnerThread` + -- to continue executing + local child = process.spawn("pesde", { "install" }, { + stdio = "default", + }) + + -- If we reach this point, that means mlua resumed the current thread and + -- `process_spawn` returned or errored. We can now close `spinnerThread` and + -- clean up + + task.cancel(spinnerThread) + stdio.write( + -- Clear the current line, move cursor back to its beginning and + -- show it + "\x1b[2K\x1b[1G\x1b[?25h" + ) + + if not child.ok then + stdio.ewrite(`{PESDE_ERROR_PREFIX} Failed to install dependencies with pesde, error:\n`) + stdio.ewrite(child.stderr) + end + + stdio.write(`{STDOUT_LINE_PREFIX} Installed dependencies with pesde successfully\n`) + + return child.code +end + +for tool, generators in lib.generators do + local startTime = os.clock() + + -- For each tool, we generate a respective manifests and scripts + local toolChoice = tool :: ToolChoice + local toolScriptsDir = SCRIPTS_DIR:join(toolChoice) + local toolMeta = MANIFEST.meta.scripts[toolChoice] + + if not pathfs.isDir(toolScriptsDir) then + pathfs.writeDir(toolScriptsDir) + end + + -- Define the manifest for the tool + local toolManifest: manifest.PesdeManifest = { + name = `pesde/scripts_{toolChoice}`, + version = toolMeta.version, + description = `Scripts for {toolChoice}-based Roblox projects`, + authors = { + "dai (https://www.daimond113.com/)", + "Erica Marigold ", + }, + repository = `https://github.com/pesde-pkg/scripts/tree/master/.pesde/{toolChoice}`, + license = "MIT", + + target = { + environment = "lune", + scripts = { + roblox_sync_config_generator = "roblox_sync_config_generator.luau", + sourcemap_generator = "sourcemap_generator.luau", + }, + }, + + peer_dependencies = toolMeta.tool_dependencies, + dependencies = { + core = { workspace = "pesde/scripts_core", version = "^" }, + }, + + indices = { + default = "https://github.com/pesde-pkg/index", + }, + } + + -- Format the scripts for the tool + local syncConfigGeneratorScript, sourcemapGeneratorScript = + string.format(SCRIPTS.syncConfigGenerator, toolChoice), string.format(SCRIPTS.sourcemapGenerator, toolChoice) + + -- Finally, write all the generated files + pathfs.writeFile(toolScriptsDir:join("pesde.toml"), serde.encode("toml", toolManifest, true)) + pathfs.writeFile(toolScriptsDir:join("roblox_sync_config_generator.luau"), syncConfigGeneratorScript) + pathfs.writeFile(toolScriptsDir:join("sourcemap_generator.luau"), sourcemapGeneratorScript) + + stdio.write( + `{INFO_PREFIX} Generated script project for tool {toolChoice} [{stdio.style("dim")}{string.format( + "%.2fs", + os.clock() - startTime + )}{stdio.style("reset")}]\n` + ) + + -- Now we install the dependencies for the newly created projects + process.exit(installDeps()) +end diff --git a/.lune/lib/manifest.luau b/.lune/lib/manifest.luau new file mode 100644 index 0000000..b2f1ede --- /dev/null +++ b/.lune/lib/manifest.luau @@ -0,0 +1,71 @@ +local fs = require("@lune/fs") +local serde = require("@lune/serde") + +export type SPDXLicense = + "MIT" + | "Apache-2.0" + | "BSD-2-Clause" + | "BSD-3-Clause" + | "GPL-2.0" + | "GPL-3.0" + | "LGPL-2.1" + | "LGPL-3.0" + | "MPL-2.0" + | "ISC" + | "Unlicense" + | "WTFPL" + | "Zlib" + | "CC0-1.0" + | "CC-BY-4.0" + | "CC-BY-SA-4.0" + | "BSL-1.0" + | "EPL-2.0" + | "AGPL-3.0" + +export type DependencySpecifier = (( + { name: string, version: string, index: string? } + | { workspace: string, version: string } + | { repo: string, rev: string, path: string? } +) & { + target: string?, +}) | { wally: string, version: string, index: string? } + +export type PackageTarget = { + environment: "luau" | "lune" | "roblox" | "roblox_server", + lib: string, +} | ({ environment: "luau" | "lune" } & ({ + bin: string, +} | { + scripts: { [string]: string }, +})) + +export type PesdeManifest = { + name: string, + version: string, + description: string?, + license: SPDXLicense?, + authors: { string }?, + repository: string?, + private: boolean?, + includes: { string }?, + pesde_version: string?, + workspace_members: { string }?, + target: PackageTarget, + build_files: { string }?, + scripts: { [string]: string }?, + indices: { [string]: string }, + wally_indices: { [string]: string }?, + overrides: { [string]: DependencySpecifier }?, + patches: { [string]: { [string]: string } }?, + place: { [string]: string }?, + dependencies: { [string]: DependencySpecifier }?, + peer_dependencies: { [string]: DependencySpecifier }?, + dev_dependencies: { [string]: DependencySpecifier }?, +} & T + +return function(path: string?, _phantom: T): PesdeManifest + local manifestContents = fs.readFile(path or "pesde.toml") + local decoded = serde.decode("toml", manifestContents) + + return decoded :: PesdeManifest +end diff --git a/.lune/roblox_sync_config_generator.luau b/.lune/roblox_sync_config_generator.luau deleted file mode 100644 index 13d1fca..0000000 --- a/.lune/roblox_sync_config_generator.luau +++ /dev/null @@ -1,19 +0,0 @@ ---> Generates a Rojo sync config from a list of input files - -local function enter(fn: (args: { string }) -> number?): never - local process = require("@lune/process") - local stdio = require("@lune/stdio") - - local startTime = os.clock() - local exitCode = fn(table.clone(process.args)) - - stdio.write(`done in {stdio.style("dim")}{string.format("%.2fs", os.clock() - startTime)}{stdio.style("reset")}!\n`) - - return process.exit(exitCode) -end - -return enter(function(args: { string }): number? - local ok, _ = require("../src").generators.rojo.syncConfig(table.remove(args, 1), args, { writeToFile = true }) - - return tonumber(ok) -end) diff --git a/.lune/sourcemap_generator.luau b/.lune/sourcemap_generator.luau deleted file mode 100644 index e98a3ef..0000000 --- a/.lune/sourcemap_generator.luau +++ /dev/null @@ -1,21 +0,0 @@ ---> Generates a Rojo sourcemap for a provided project directory - -local function enter(fn: (args: { string }) -> number?): never - local process = require("@lune/process") - local stdio = require("@lune/stdio") - - local startTime = os.clock() - local exitCode = fn(table.clone(process.args)) - - stdio.ewrite( - `\ndone in {stdio.style("dim")}{string.format("%.2fs", os.clock() - startTime)}{stdio.style("reset")}!\n` - ) - - return process.exit(exitCode) -end - -return enter(function(args: { string }): number? - return tonumber(require("../src").generators.rojo.sourcemap( - args[1] - )) -end) \ No newline at end of file diff --git a/.pesde/.gitkeep b/.pesde/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/.vscode/settings.json b/.vscode/settings.json index 17eb63b..afb4651 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,5 +2,6 @@ "luau-lsp.require.mode": "relativeToFile", "luau-lsp.require.directoryAliases": { "@lune/": "~/.lune/.typedefs/0.8.9/" - } + }, + "stylua.targetReleaseVersion": "latest" } \ No newline at end of file diff --git a/pesde.lock b/pesde.lock index 29244a6..d9a7edf 100644 --- a/pesde.lock +++ b/pesde.lock @@ -1,7 +1,13 @@ -name = "pesde/scripts" +name = "pesde/scripts_core" version = "0.1.0" target = "lune" +[workspace."pesde/scripts_core"] +lune = "" + +[workspace."pesde/scripts_rojo"] +lune = ".pesde/rojo" + [graph."itsfrank/frktest"."0.0.2 lune"] direct = ["frktest", { name = "itsfrank/frktest", version = "^0.0.2" }, "dev"] resolved_ty = "dev" @@ -19,3 +25,21 @@ index_url = "https://github.com/daimond113/pesde-index" [graph."itsfrank/frktest"."0.0.2 lune".pkg_ref.target] environment = "lune" lib = "src/_pesde_init.luau" + +[graph."jiwonz/pathfs"."0.1.0 lune"] +direct = ["pathfs", { name = "jiwonz/pathfs", version = "^0.1.0" }, "dev"] +resolved_ty = "dev" + +[graph."jiwonz/pathfs"."0.1.0 lune".target] +environment = "lune" +lib = "init.luau" + +[graph."jiwonz/pathfs"."0.1.0 lune".pkg_ref] +ref_ty = "pesde" +name = "jiwonz/pathfs" +version = "0.1.0" +index_url = "https://github.com/daimond113/pesde-index" + +[graph."jiwonz/pathfs"."0.1.0 lune".pkg_ref.target] +environment = "lune" +lib = "init.luau" diff --git a/pesde.toml b/pesde.toml index 77d667c..a4daa34 100644 --- a/pesde.toml +++ b/pesde.toml @@ -1,21 +1,35 @@ -name = "pesde/scripts" +name = "pesde/scripts_core" version = "0.1.0" pesde_version = "0.5.0-rc.14" description = "Scripts and other utilities for use with pesde" -authors = ["dai (https://www.daimond113.com/)", "Erica Marigold "] +authors = [ + "dai (https://www.daimond113.com/)", + "Erica Marigold ", +] repository = "https://github.com/pesde-pkg/scripts" license = "MIT" -includes = ["src/**/*.luau", "pesde.toml", "README.md", "LICENSE.md"] +includes = [ + "src/**/*.luau", + "!src/**/*.spec.luau", + ".pesde", + "pesde.toml", + "README.md", + "LICENSE.md", +] -[meta] -scripts_root = ".lune" +workspace_members = [".", ".pesde/rojo"] + +[meta.scripts.rojo] +version = "0.1.0" +tool_dependencies = { rojo = { name = "pesde/rojo", version = "^7.4.4" } } [target] environment = "lune" lib = "src/init.luau" -[indices] -default = "https://github.com/daimond113/pesde-index" - [dev_dependencies] frktest = { name = "itsfrank/frktest", version = "^0.0.2" } +pathfs = { name = "jiwonz/pathfs", version = "^0.1.0" } + +[indices] +default = "https://github.com/pesde-pkg/index" diff --git a/src/init.luau b/src/init.luau index cf48aa9..af8ea3f 100644 --- a/src/init.luau +++ b/src/init.luau @@ -5,4 +5,4 @@ return { syncConfig = require("./generators/rojo/sync_config"), }, }, -} +} \ No newline at end of file From e3889bffb13bd2457cff0ae8779a2ec1b7afb764 Mon Sep 17 00:00:00 2001 From: Erica Marigold Date: Sun, 8 Dec 2024 11:38:22 +0000 Subject: [PATCH 19/39] chore(lune): set repo for generated manifest correctly Also applies the stylua formatter to the file. --- .lune/build.luau | 328 +++++++++++++++++++++++------------------------ 1 file changed, 164 insertions(+), 164 deletions(-) diff --git a/.lune/build.luau b/.lune/build.luau index 94652b6..67dad66 100644 --- a/.lune/build.luau +++ b/.lune/build.luau @@ -1,164 +1,164 @@ ---> Generates sync config and sourcemap scripts for supported tools - -local process = require("@lune/process") -local serde = require("@lune/serde") -local stdio = require("@lune/stdio") -local task = require("@lune/task") - -local lib = require("../src") -local manifest = require("./lib/manifest") -local pathfs = require("../lune_packages/pathfs") - -type ToolChoice = "rojo" -type ManifestExt = { - scripts: { - [ToolChoice]: { - version: string, - tool_dependencies: { { [string]: manifest.DependencySpecifier } }, - }, - }, -} - -local SCRIPTS_DIR = pathfs.getAbsolutePathOf(pathfs.Path.from(".pesde")) -local MANIFEST = manifest(nil, (nil :: any) :: { meta: ManifestExt }) -local SCRIPTS = { - syncConfigGenerator = [[local process = require("@lune/process") - -local args = table.clone(process.args) -local ok, _ = -require("./lune_packages/core").generators.%s.syncConfig(table.remove(args, 1), args, { writeToFile = true }) - -process.exit(tonumber(ok))]], - - sourcemapGenerator = [[local process = require("@lune/process") - -return process.exit( - tonumber(require("./lune_packages/core").generators.%s.sourcemap(process.args[1])) -)]], -} - -local function logPrefix(type: "error" | "info") - local statusColor: stdio.Color = if type == "error" - then "red" - elseif type == "info" then "green" - else error(`Invalid type: {type}`) - - return `pesde::{stdio.style("bold")}{stdio.color(statusColor)}{type}{stdio.color("reset")}` -end - -local INFO_PREFIX = `[{logPrefix("info")}]` -local _ERROR_PREFIX = `[{logPrefix("error")}]` - -local function installDeps(): number - local STDOUT_LINE_PREFIX = `[pesde::{logPrefix("info")}]` - local PESDE_ERROR_PREFIX = `[pesde::{logPrefix("error")}]` - local SPINNER_STATES = { '⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏' } - - stdio.write(`{STDOUT_LINE_PREFIX} Installing dependencies with pesde`) - - -- Spawn our thread to display the spinner - local spinnerThread = task.spawn(function() - -- Hide the cursor - stdio.write("\x1b[?25l") - - -- Display the spinner - while true do - for _, state in SPINNER_STATES do - stdio.write(` {state}`) - stdio.write( - -- Moves the cursor back 1 spot and clears everything after - "\x1b[2D\x1b[0K" - ) - end - end - end) - - -- `process_spawn` is an async rust function, so tokio spawns a hardware - -- thread and mlua will yield the current thread, allowing for `spinnerThread` - -- to continue executing - local child = process.spawn("pesde", { "install" }, { - stdio = "default", - }) - - -- If we reach this point, that means mlua resumed the current thread and - -- `process_spawn` returned or errored. We can now close `spinnerThread` and - -- clean up - - task.cancel(spinnerThread) - stdio.write( - -- Clear the current line, move cursor back to its beginning and - -- show it - "\x1b[2K\x1b[1G\x1b[?25h" - ) - - if not child.ok then - stdio.ewrite(`{PESDE_ERROR_PREFIX} Failed to install dependencies with pesde, error:\n`) - stdio.ewrite(child.stderr) - end - - stdio.write(`{STDOUT_LINE_PREFIX} Installed dependencies with pesde successfully\n`) - - return child.code -end - -for tool, generators in lib.generators do - local startTime = os.clock() - - -- For each tool, we generate a respective manifests and scripts - local toolChoice = tool :: ToolChoice - local toolScriptsDir = SCRIPTS_DIR:join(toolChoice) - local toolMeta = MANIFEST.meta.scripts[toolChoice] - - if not pathfs.isDir(toolScriptsDir) then - pathfs.writeDir(toolScriptsDir) - end - - -- Define the manifest for the tool - local toolManifest: manifest.PesdeManifest = { - name = `pesde/scripts_{toolChoice}`, - version = toolMeta.version, - description = `Scripts for {toolChoice}-based Roblox projects`, - authors = { - "dai (https://www.daimond113.com/)", - "Erica Marigold ", - }, - repository = `https://github.com/pesde-pkg/scripts/tree/master/.pesde/{toolChoice}`, - license = "MIT", - - target = { - environment = "lune", - scripts = { - roblox_sync_config_generator = "roblox_sync_config_generator.luau", - sourcemap_generator = "sourcemap_generator.luau", - }, - }, - - peer_dependencies = toolMeta.tool_dependencies, - dependencies = { - core = { workspace = "pesde/scripts_core", version = "^" }, - }, - - indices = { - default = "https://github.com/pesde-pkg/index", - }, - } - - -- Format the scripts for the tool - local syncConfigGeneratorScript, sourcemapGeneratorScript = - string.format(SCRIPTS.syncConfigGenerator, toolChoice), string.format(SCRIPTS.sourcemapGenerator, toolChoice) - - -- Finally, write all the generated files - pathfs.writeFile(toolScriptsDir:join("pesde.toml"), serde.encode("toml", toolManifest, true)) - pathfs.writeFile(toolScriptsDir:join("roblox_sync_config_generator.luau"), syncConfigGeneratorScript) - pathfs.writeFile(toolScriptsDir:join("sourcemap_generator.luau"), sourcemapGeneratorScript) - - stdio.write( - `{INFO_PREFIX} Generated script project for tool {toolChoice} [{stdio.style("dim")}{string.format( - "%.2fs", - os.clock() - startTime - )}{stdio.style("reset")}]\n` - ) - - -- Now we install the dependencies for the newly created projects - process.exit(installDeps()) -end +--> Generates sync config and sourcemap scripts for supported tools + +local process = require("@lune/process") +local serde = require("@lune/serde") +local stdio = require("@lune/stdio") +local task = require("@lune/task") + +local lib = require("../src") +local manifest = require("./lib/manifest") +local pathfs = require("../lune_packages/pathfs") + +type ToolChoice = "rojo" +type ManifestExt = { + scripts: { + [ToolChoice]: { + version: string, + tool_dependencies: { { [string]: manifest.DependencySpecifier } }, + }, + }, +} + +local SCRIPTS_DIR = pathfs.getAbsolutePathOf(pathfs.Path.from(".pesde")) +local MANIFEST = manifest(nil, (nil :: any) :: { meta: ManifestExt }) +local SCRIPTS = { + syncConfigGenerator = [[local process = require("@lune/process") + +local args = table.clone(process.args) +local ok, _ = +require("./lune_packages/core").generators.%s.syncConfig(table.remove(args, 1), args, { writeToFile = true }) + +process.exit(tonumber(ok))]], + + sourcemapGenerator = [[local process = require("@lune/process") + +return process.exit( + tonumber(require("./lune_packages/core").generators.%s.sourcemap(process.args[1])) +)]], +} + +local function logPrefix(type: "error" | "info") + local statusColor: stdio.Color = if type == "error" + then "red" + elseif type == "info" then "green" + else error(`Invalid type: {type}`) + + return `pesde::{stdio.style("bold")}{stdio.color(statusColor)}{type}{stdio.color("reset")}` +end + +local INFO_PREFIX = `[{logPrefix("info")}]` +local _ERROR_PREFIX = `[{logPrefix("error")}]` + +local function installDeps(): number + local STDOUT_LINE_PREFIX = `[pesde::{logPrefix("info")}]` + local PESDE_ERROR_PREFIX = `[pesde::{logPrefix("error")}]` + local SPINNER_STATES = { "⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏" } + + stdio.write(`{STDOUT_LINE_PREFIX} Installing dependencies with pesde`) + + -- Spawn our thread to display the spinner + local spinnerThread = task.spawn(function() + -- Hide the cursor + stdio.write("\x1b[?25l") + + -- Display the spinner + while true do + for _, state in SPINNER_STATES do + stdio.write(` {state}`) + stdio.write( + -- Moves the cursor back 1 spot and clears everything after + "\x1b[2D\x1b[0K" + ) + end + end + end) + + -- `process_spawn` is an async rust function, so tokio spawns a hardware + -- thread and mlua will yield the current thread, allowing for `spinnerThread` + -- to continue executing + local child = process.spawn("pesde", { "install" }, { + stdio = "default", + }) + + -- If we reach this point, that means mlua resumed the current thread and + -- `process_spawn` returned or errored. We can now close `spinnerThread` and + -- clean up + + task.cancel(spinnerThread) + stdio.write( + -- Clear the current line, move cursor back to its beginning and + -- show it + "\x1b[2K\x1b[1G\x1b[?25h" + ) + + if not child.ok then + stdio.ewrite(`{PESDE_ERROR_PREFIX} Failed to install dependencies with pesde, error:\n`) + stdio.ewrite(child.stderr) + end + + stdio.write(`{STDOUT_LINE_PREFIX} Installed dependencies with pesde successfully\n`) + + return child.code +end + +for tool, generators in lib.generators do + local startTime = os.clock() + + -- For each tool, we generate a respective manifests and scripts + local toolChoice = tool :: ToolChoice + local toolScriptsDir = SCRIPTS_DIR:join(toolChoice) + local toolMeta = MANIFEST.meta.scripts[toolChoice] + + if not pathfs.isDir(toolScriptsDir) then + pathfs.writeDir(toolScriptsDir) + end + + -- Define the manifest for the tool + local toolManifest: manifest.PesdeManifest = { + name = `pesde/scripts_{toolChoice}`, + version = toolMeta.version, + description = `Scripts for {toolChoice}-based Roblox projects`, + authors = { + "dai (https://www.daimond113.com/)", + "Erica Marigold ", + }, + repository = "https://github.com/pesde-pkg/scripts", + license = "MIT", + + target = { + environment = "lune", + scripts = { + roblox_sync_config_generator = "roblox_sync_config_generator.luau", + sourcemap_generator = "sourcemap_generator.luau", + }, + }, + + peer_dependencies = toolMeta.tool_dependencies, + dependencies = { + core = { workspace = "pesde/scripts_core", version = "^" }, + }, + + indices = { + default = "https://github.com/pesde-pkg/index", + }, + } + + -- Format the scripts for the tool + local syncConfigGeneratorScript, sourcemapGeneratorScript = + string.format(SCRIPTS.syncConfigGenerator, toolChoice), string.format(SCRIPTS.sourcemapGenerator, toolChoice) + + -- Finally, write all the generated files + pathfs.writeFile(toolScriptsDir:join("pesde.toml"), serde.encode("toml", toolManifest, true)) + pathfs.writeFile(toolScriptsDir:join("roblox_sync_config_generator.luau"), syncConfigGeneratorScript) + pathfs.writeFile(toolScriptsDir:join("sourcemap_generator.luau"), sourcemapGeneratorScript) + + stdio.write( + `{INFO_PREFIX} Generated script project for tool {toolChoice} [{stdio.style("dim")}{string.format( + "%.2fs", + os.clock() - startTime + )}{stdio.style("reset")}]\n` + ) + + -- Now we install the dependencies for the newly created projects + process.exit(installDeps()) +end From 3c0d06c51a9dc8452f0c46bdad38be792d1ec680 Mon Sep 17 00:00:00 2001 From: Erica Marigold Date: Sun, 8 Dec 2024 11:39:17 +0000 Subject: [PATCH 20/39] style: apply stylua formatter --- src/init.luau | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/init.luau b/src/init.luau index af8ea3f..cf48aa9 100644 --- a/src/init.luau +++ b/src/init.luau @@ -5,4 +5,4 @@ return { syncConfig = require("./generators/rojo/sync_config"), }, }, -} \ No newline at end of file +} From dcfc331c3ccecdbcb2f012d4f0d973fd2378f420 Mon Sep 17 00:00:00 2001 From: Erica Marigold Date: Sun, 8 Dec 2024 11:52:32 +0000 Subject: [PATCH 21/39] chore(lune): build script fixes and improvements * Always display scopes, even for top level as `main`. * Fix prefix scope being incorrectly displayed, and make both `pesde` and `main` scopes aligned. * Name prefix constants correctly. * Use proper author name for @daimond113 in generated manifests. * Display time elapsed in ms instead of secs. --- .lune/build.luau | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.lune/build.luau b/.lune/build.luau index 67dad66..2c903cf 100644 --- a/.lune/build.luau +++ b/.lune/build.luau @@ -43,18 +43,18 @@ local function logPrefix(type: "error" | "info") elseif type == "info" then "green" else error(`Invalid type: {type}`) - return `pesde::{stdio.style("bold")}{stdio.color(statusColor)}{type}{stdio.color("reset")}` + return `main::{stdio.style("bold")}{stdio.color(statusColor)}{type}{stdio.color("reset")}` end -local INFO_PREFIX = `[{logPrefix("info")}]` +local INFO_PREFIX = `[ {logPrefix("info")}]` local _ERROR_PREFIX = `[{logPrefix("error")}]` local function installDeps(): number - local STDOUT_LINE_PREFIX = `[pesde::{logPrefix("info")}]` - local PESDE_ERROR_PREFIX = `[pesde::{logPrefix("error")}]` + local PESDE_INFO_PREFIX = string.gsub(`[{logPrefix("info")}]`, "main", "pesde") + local PESDE_ERROR_PREFIX = string.gsub(`[{logPrefix("error")}]`, "main", "pesde") local SPINNER_STATES = { "⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏" } - stdio.write(`{STDOUT_LINE_PREFIX} Installing dependencies with pesde`) + stdio.write(`{PESDE_INFO_PREFIX} Installing dependencies with pesde`) -- Spawn our thread to display the spinner local spinnerThread = task.spawn(function() @@ -96,7 +96,7 @@ local function installDeps(): number stdio.ewrite(child.stderr) end - stdio.write(`{STDOUT_LINE_PREFIX} Installed dependencies with pesde successfully\n`) + stdio.write(`{PESDE_INFO_PREFIX} Installed dependencies with pesde successfully\n`) return child.code end @@ -119,7 +119,7 @@ for tool, generators in lib.generators do version = toolMeta.version, description = `Scripts for {toolChoice}-based Roblox projects`, authors = { - "dai (https://www.daimond113.com/)", + "daimond113 (https://www.daimond113.com/)", "Erica Marigold ", }, repository = "https://github.com/pesde-pkg/scripts", @@ -153,10 +153,10 @@ for tool, generators in lib.generators do pathfs.writeFile(toolScriptsDir:join("sourcemap_generator.luau"), sourcemapGeneratorScript) stdio.write( - `{INFO_PREFIX} Generated script project for tool {toolChoice} [{stdio.style("dim")}{string.format( - "%.2fs", - os.clock() - startTime - )}{stdio.style("reset")}]\n` + `{INFO_PREFIX} Generated script project for tool {toolChoice} ({stdio.style("dim")}{string.format( + "%.2fms", + (os.clock() - startTime) * 1000 + )}{stdio.style("reset")})\n` ) -- Now we install the dependencies for the newly created projects From 9ecf9801106418021bca8f46eb0b5c8c281e0f86 Mon Sep 17 00:00:00 2001 From: Erica Marigold Date: Sun, 8 Dec 2024 12:02:45 +0000 Subject: [PATCH 22/39] chore(lune): use uppercase for letter of tool choice in manifest desc --- .lune/build.luau | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.lune/build.luau b/.lune/build.luau index 2c903cf..bca778e 100644 --- a/.lune/build.luau +++ b/.lune/build.luau @@ -117,7 +117,9 @@ for tool, generators in lib.generators do local toolManifest: manifest.PesdeManifest = { name = `pesde/scripts_{toolChoice}`, version = toolMeta.version, - description = `Scripts for {toolChoice}-based Roblox projects`, + -- For the description, we capitalize the first letter of the tool name here + -- since it is a proper noun + description = `Scripts for {string.gsub(toolChoice, "^%l", string.upper)}-based Roblox projects.`, authors = { "daimond113 (https://www.daimond113.com/)", "Erica Marigold ", From 0dbdaa82355d4cd411cd54a413365794751a49f2 Mon Sep 17 00:00:00 2001 From: Erica Marigold Date: Sun, 8 Dec 2024 12:14:33 +0000 Subject: [PATCH 23/39] chore(lune): install deps *after* generating all packages --- .lune/build.luau | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.lune/build.luau b/.lune/build.luau index bca778e..e1e4037 100644 --- a/.lune/build.luau +++ b/.lune/build.luau @@ -117,8 +117,8 @@ for tool, generators in lib.generators do local toolManifest: manifest.PesdeManifest = { name = `pesde/scripts_{toolChoice}`, version = toolMeta.version, - -- For the description, we capitalize the first letter of the tool name here - -- since it is a proper noun + -- For the description, we capitalize the first letter of the tool name here + -- since it is a proper noun description = `Scripts for {string.gsub(toolChoice, "^%l", string.upper)}-based Roblox projects.`, authors = { "daimond113 (https://www.daimond113.com/)", @@ -160,7 +160,7 @@ for tool, generators in lib.generators do (os.clock() - startTime) * 1000 )}{stdio.style("reset")})\n` ) - - -- Now we install the dependencies for the newly created projects - process.exit(installDeps()) end + +-- Now we install the dependencies for the newly created projects +process.exit(installDeps()) From c94110ced970de65a6c975e0b2c7bfed55706d08 Mon Sep 17 00:00:00 2001 From: Erica Marigold Date: Sun, 8 Dec 2024 12:27:18 +0000 Subject: [PATCH 24/39] chore(pkg): start versioning from v0.0.1 --- pesde.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pesde.toml b/pesde.toml index a4daa34..b3887d8 100644 --- a/pesde.toml +++ b/pesde.toml @@ -1,5 +1,5 @@ name = "pesde/scripts_core" -version = "0.1.0" +version = "0.0.1" pesde_version = "0.5.0-rc.14" description = "Scripts and other utilities for use with pesde" authors = [ From 8ac67b89cfe2e39941681c6d6ce7e6747eef1f0c Mon Sep 17 00:00:00 2001 From: Erica Marigold Date: Sun, 8 Dec 2024 12:33:31 +0000 Subject: [PATCH 25/39] chore(lune): only use `process.exit` on error to prevent lune panic --- .lune/build.luau | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/.lune/build.luau b/.lune/build.luau index e1e4037..2474aff 100644 --- a/.lune/build.luau +++ b/.lune/build.luau @@ -27,14 +27,17 @@ local SCRIPTS = { local args = table.clone(process.args) local ok, _ = require("./lune_packages/core").generators.%s.syncConfig(table.remove(args, 1), args, { writeToFile = true }) - -process.exit(tonumber(ok))]], +if not ok then + return process.exit(1) +end]], sourcemapGenerator = [[local process = require("@lune/process") -return process.exit( - tonumber(require("./lune_packages/core").generators.%s.sourcemap(process.args[1])) -)]], +local ok = require("./lune_packages/core").generators.%s.sourcemap(process.args[1]) +if not ok then + return process.exit(1) +end +]], } local function logPrefix(type: "error" | "info") From b8687d7e7804b9821baa1675f19d85ea678b3a4d Mon Sep 17 00:00:00 2001 From: Erica Marigold Date: Sun, 8 Dec 2024 12:53:25 +0000 Subject: [PATCH 26/39] chore(pkg): do not include `.pesde` in lib package --- pesde.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pesde.toml b/pesde.toml index b3887d8..0764878 100644 --- a/pesde.toml +++ b/pesde.toml @@ -11,7 +11,6 @@ license = "MIT" includes = [ "src/**/*.luau", "!src/**/*.spec.luau", - ".pesde", "pesde.toml", "README.md", "LICENSE.md", From 2c852e0466c280c68e7bfb64ad7c73b0358c3079 Mon Sep 17 00:00:00 2001 From: Erica Marigold Date: Sun, 8 Dec 2024 13:26:42 +0000 Subject: [PATCH 27/39] chore(pkg): bump minimum `pesde_version` --- pesde.lock | 5 +---- pesde.toml | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/pesde.lock b/pesde.lock index d9a7edf..5c43444 100644 --- a/pesde.lock +++ b/pesde.lock @@ -1,13 +1,10 @@ name = "pesde/scripts_core" -version = "0.1.0" +version = "0.0.1" target = "lune" [workspace."pesde/scripts_core"] lune = "" -[workspace."pesde/scripts_rojo"] -lune = ".pesde/rojo" - [graph."itsfrank/frktest"."0.0.2 lune"] direct = ["frktest", { name = "itsfrank/frktest", version = "^0.0.2" }, "dev"] resolved_ty = "dev" diff --git a/pesde.toml b/pesde.toml index 0764878..9230cf6 100644 --- a/pesde.toml +++ b/pesde.toml @@ -1,6 +1,6 @@ name = "pesde/scripts_core" version = "0.0.1" -pesde_version = "0.5.0-rc.14" +pesde_version = "0.5.0-rc.16" description = "Scripts and other utilities for use with pesde" authors = [ "dai (https://www.daimond113.com/)", From dafb986a47bbd11a543bd6b92c70dbcfeff5bc9d Mon Sep 17 00:00:00 2001 From: daimond113 <72147841+daimond113@users.noreply.github.com> Date: Sun, 8 Dec 2024 16:05:52 +0100 Subject: [PATCH 28/39] docs: include README & LICENSE in scripts packages --- .lune/build.luau | 29 ++++++++++++++++++++++++++++- LICENSE | 21 +++++++++++++++++++++ LICENSE.md | 7 ------- pesde.toml | 2 +- 4 files changed, 50 insertions(+), 9 deletions(-) create mode 100644 LICENSE delete mode 100644 LICENSE.md diff --git a/.lune/build.luau b/.lune/build.luau index 2474aff..baa11a0 100644 --- a/.lune/build.luau +++ b/.lune/build.luau @@ -40,6 +40,19 @@ end ]], } +local README_TMPL = [[# `pesde/scripts_%s` +Common scripts intended for use with %s. + +## Included scripts +### roblox_sync_config_generator + +Generates a %s sync config for pesde Roblox packages. + +### sourcemap_generator + +Prints out a sourcemap for Wally dependencies for type extraction. +]] + local function logPrefix(type: "error" | "info") local statusColor: stdio.Color = if type == "error" then "red" @@ -116,19 +129,28 @@ for tool, generators in lib.generators do pathfs.writeDir(toolScriptsDir) end + local capitalisedToolChoice = string.gsub(toolChoice, "^%l", string.upper) + -- Define the manifest for the tool local toolManifest: manifest.PesdeManifest = { name = `pesde/scripts_{toolChoice}`, version = toolMeta.version, -- For the description, we capitalize the first letter of the tool name here -- since it is a proper noun - description = `Scripts for {string.gsub(toolChoice, "^%l", string.upper)}-based Roblox projects.`, + description = `Scripts for {capitalisedToolChoice}-based Roblox projects.`, authors = { "daimond113 (https://www.daimond113.com/)", "Erica Marigold ", }, repository = "https://github.com/pesde-pkg/scripts", license = "MIT", + includes = { + "roblox_sync_config_generator.luau", + "sourcemap_generator.luau", + "pesde.toml", + "README.md", + "LICENSE", + }, target = { environment = "lune", @@ -156,6 +178,11 @@ for tool, generators in lib.generators do pathfs.writeFile(toolScriptsDir:join("pesde.toml"), serde.encode("toml", toolManifest, true)) pathfs.writeFile(toolScriptsDir:join("roblox_sync_config_generator.luau"), syncConfigGeneratorScript) pathfs.writeFile(toolScriptsDir:join("sourcemap_generator.luau"), sourcemapGeneratorScript) + pathfs.writeFile( + toolScriptsDir:join("README.md"), + string.format(README_TMPL, toolChoice, capitalisedToolChoice, capitalisedToolChoice) + ) + pathfs.copy(pathfs.cwd:join("LICENSE"), toolScriptsDir:join("LICENSE"), true) stdio.write( `{INFO_PREFIX} Generated script project for tool {toolChoice} ({stdio.style("dim")}{string.format( diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f4d84c0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 pesde-pkg + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/LICENSE.md b/LICENSE.md deleted file mode 100644 index 7924a3f..0000000 --- a/LICENSE.md +++ /dev/null @@ -1,7 +0,0 @@ -Copyright © 2024 pesde-pkg - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/pesde.toml b/pesde.toml index 9230cf6..fa6d169 100644 --- a/pesde.toml +++ b/pesde.toml @@ -13,7 +13,7 @@ includes = [ "!src/**/*.spec.luau", "pesde.toml", "README.md", - "LICENSE.md", + "LICENSE", ] workspace_members = [".", ".pesde/rojo"] From 1fc76f7cd7e3988187116401f96ec3843ce19f08 Mon Sep 17 00:00:00 2001 From: daimond113 <72147841+daimond113@users.noreply.github.com> Date: Sun, 8 Dec 2024 17:58:44 +0100 Subject: [PATCH 29/39] docs: update root README --- README.md | 39 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 85a7e80..bb7dd88 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,38 @@ -# pesde scripts +# pesde/scripts_core -This repository hosts scripts & utilities for [pesde](https://github.com/daimond113/pesde) +Discord +Lune +pesde/scripts_core + + + + +Scripts and other utilities for use with pesde. + +## Prerequisites + +To ensure proper functionality, please make sure you have the following dependencies installed: + +- **pesde**: Version `>= 0.5.0-rc.15` +- **lune**: Version `>= 0.8.9` + +## Usage + +For example, to install Rojo scripts, run: + +```sh +# Add the scripts themselves +pesde add --dev pesde/scripts_rojo --target lune +# Add the Rojo CLI +pesde add --dev pesde/rojo --target lune +# Install dependencies +pesde install +``` + +If a sync tool you would like is not present here, please open an issue or submit a PR, following the format of one of the existing tools. + + + +# License + +This project is licensed under the [MIT](https://github.com/pesde-pkg/scripts/blob/master/LICENSE) license. From be2ae619c692ae89c02904b519c4d5b32ea27a74 Mon Sep 17 00:00:00 2001 From: Erica Marigold Date: Sun, 8 Dec 2024 17:24:55 +0000 Subject: [PATCH 30/39] chore(README): fix package badge urls --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bb7dd88..39e8e38 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Discord Lune -pesde/scripts_core +pesde/scripts_core From 0045c01c7255856bed81a85f72e8cb7e9c88867b Mon Sep 17 00:00:00 2001 From: Erica Marigold Date: Sun, 8 Dec 2024 17:42:05 +0000 Subject: [PATCH 31/39] style: apply stylua formatter --- .lune/fmt.luau | 14 +-- .lune/lib/channel.luau | 96 +++++++++--------- .lune/lib/exec.luau | 206 +++++++++++++++++++------------------- .lune/lib/manifest.luau | 142 +++++++++++++------------- .lune/tests/init.luau | 159 +++++++++++++++-------------- .lune/tests/reporter.luau | 119 +++++++++++----------- .lune/typecheck.luau | 32 +++--- 7 files changed, 383 insertions(+), 385 deletions(-) diff --git a/.lune/fmt.luau b/.lune/fmt.luau index aad2818..77be22a 100644 --- a/.lune/fmt.luau +++ b/.lune/fmt.luau @@ -1,7 +1,7 @@ ---> Run stylua to check for formatting errors - -local process = require("@lune/process") - -local CommandBuilder = require("./lib/exec") - -process.exit(CommandBuilder.new("stylua"):withArg("."):withArgs(process.args):withStdioStrategy("forward"):exec().code) +--> Run stylua to check for formatting errors + +local process = require("@lune/process") + +local CommandBuilder = require("./lib/exec") + +process.exit(CommandBuilder.new("stylua"):withArg("."):withArgs(process.args):withStdioStrategy("forward"):exec().code) diff --git a/.lune/lib/channel.luau b/.lune/lib/channel.luau index 03cf12c..3df65fc 100644 --- a/.lune/lib/channel.luau +++ b/.lune/lib/channel.luau @@ -1,48 +1,48 @@ ---- An MPSC synchronization primitive powered by Lua upvalues which retains only ---- one value at a time. - ---- ## Usage ---- ```luau ---- local send, recv = watch((nil :: any) :: string) ---- task.delay(5, send, "hello, world!") ---- task.spawn(function() ---- local value = recv() ---- print("received value:", value) ---- end) ---- ``` -type Watch = { - value: T?, - receivers: { thread }, -} - ---- Crates a new `Watch` channel, returning its send and receive handles. -local function chan(_phantom: T): ((T) -> (), () -> T?) - local watch: Watch = { - value = nil, - receivers = {}, - } - - local function send(value: T) - watch.value = value - - for _, receiver in watch.receivers do - coroutine.resume(receiver, value) - end - end - - local function recv(): T - local value = watch.value - watch.value = nil - - if value == nil then - table.insert(watch.receivers, coroutine.running()) - return coroutine.yield() - end - - return value :: T - end - - return send, recv -end - -return chan \ No newline at end of file +--- An MPSC synchronization primitive powered by Lua upvalues which retains only +--- one value at a time. + +--- ## Usage +--- ```luau +--- local send, recv = watch((nil :: any) :: string) +--- task.delay(5, send, "hello, world!") +--- task.spawn(function() +--- local value = recv() +--- print("received value:", value) +--- end) +--- ``` +type Watch = { + value: T?, + receivers: { thread }, +} + +--- Crates a new `Watch` channel, returning its send and receive handles. +local function chan(_phantom: T): ((T) -> (), () -> T?) + local watch: Watch = { + value = nil, + receivers = {}, + } + + local function send(value: T) + watch.value = value + + for _, receiver in watch.receivers do + coroutine.resume(receiver, value) + end + end + + local function recv(): T + local value = watch.value + watch.value = nil + + if value == nil then + table.insert(watch.receivers, coroutine.running()) + return coroutine.yield() + end + + return value :: T + end + + return send, recv +end + +return chan diff --git a/.lune/lib/exec.luau b/.lune/lib/exec.luau index 8ce24da..9550729 100644 --- a/.lune/lib/exec.luau +++ b/.lune/lib/exec.luau @@ -1,103 +1,103 @@ ---> lib: Builder pattern class to spawn child processes - -local process = require("@lune/process") -local stdio = require("@lune/stdio") - -local CommandBuilder = {} - -export type CommandBuilder = typeof(setmetatable({} :: CommandBuilderFields, { __index = CommandBuilder })) -type CommandBuilderFields = { - program: string, - args: { string }, - stdioStrategy: IoStrategyMapping?, -} -export type StdioStrategy = "pipe" | "forward" | "none" -export type IoStrategyMapping = { - stdout: StdioStrategy?, - stderr: StdioStrategy?, -} - -local DEFAULT_STDIO_STRATEGY: IoStrategyMapping = { - stdout = "pipe", - stderr = "pipe", -} -function CommandBuilder.new(program: string) - return setmetatable( - { - program = program, - args = {}, - stdioStrategy = nil, - } :: CommandBuilderFields, - { - __index = CommandBuilder, - } - ) -end - -function CommandBuilder.withArg(self: CommandBuilder, arg: string): CommandBuilder - table.insert(self.args, arg) - return self -end - -function CommandBuilder.withArgs(self: CommandBuilder, args: { string }): CommandBuilder - for _, arg in args do - self:withArg(arg) - end - - return self -end - -function CommandBuilder.withStdioStrategy( - self: CommandBuilder, - strategy: StdioStrategy | IoStrategyMapping -): CommandBuilder - self.stdioStrategy = if typeof(strategy) == "string" - then { - stdout = strategy, - stderr = strategy, - } - else strategy - return self -end - -local function intoSpawnOptionsStdioKind(strategy: StdioStrategy): process.SpawnOptionsStdioKind - if strategy == "pipe" then - return "default" - end - - if strategy == "forward" then - return "forward" - end - - if strategy == "none" then - return "none" - end - - error(`Non-strategy provided: {strategy}`) -end - -function CommandBuilder.exec(self: CommandBuilder): process.SpawnResult - print("$", stdio.style("dim") .. self.program, table.concat(self.args, " ") .. stdio.style("reset")) - - local function translateIoStrategyMappings(mappings: IoStrategyMapping) - local translatedMappings: process.SpawnOptionsStdio = {} - for field, value in mappings do - translatedMappings[field] = intoSpawnOptionsStdioKind(value) - end - - return translatedMappings - end - - local child = process.spawn(self.program, self.args, { - shell = true, - stdio = translateIoStrategyMappings(self.stdioStrategy or DEFAULT_STDIO_STRATEGY), - }) - - if not child.ok then - print(`\n{stdio.color("red")}[luau-lsp]{stdio.color("reset")} Exited with code`, child.code) - end - - return child -end - -return CommandBuilder +--> lib: Builder pattern class to spawn child processes + +local process = require("@lune/process") +local stdio = require("@lune/stdio") + +local CommandBuilder = {} + +export type CommandBuilder = typeof(setmetatable({} :: CommandBuilderFields, { __index = CommandBuilder })) +type CommandBuilderFields = { + program: string, + args: { string }, + stdioStrategy: IoStrategyMapping?, +} +export type StdioStrategy = "pipe" | "forward" | "none" +export type IoStrategyMapping = { + stdout: StdioStrategy?, + stderr: StdioStrategy?, +} + +local DEFAULT_STDIO_STRATEGY: IoStrategyMapping = { + stdout = "pipe", + stderr = "pipe", +} +function CommandBuilder.new(program: string) + return setmetatable( + { + program = program, + args = {}, + stdioStrategy = nil, + } :: CommandBuilderFields, + { + __index = CommandBuilder, + } + ) +end + +function CommandBuilder.withArg(self: CommandBuilder, arg: string): CommandBuilder + table.insert(self.args, arg) + return self +end + +function CommandBuilder.withArgs(self: CommandBuilder, args: { string }): CommandBuilder + for _, arg in args do + self:withArg(arg) + end + + return self +end + +function CommandBuilder.withStdioStrategy( + self: CommandBuilder, + strategy: StdioStrategy | IoStrategyMapping +): CommandBuilder + self.stdioStrategy = if typeof(strategy) == "string" + then { + stdout = strategy, + stderr = strategy, + } + else strategy + return self +end + +local function intoSpawnOptionsStdioKind(strategy: StdioStrategy): process.SpawnOptionsStdioKind + if strategy == "pipe" then + return "default" + end + + if strategy == "forward" then + return "forward" + end + + if strategy == "none" then + return "none" + end + + error(`Non-strategy provided: {strategy}`) +end + +function CommandBuilder.exec(self: CommandBuilder): process.SpawnResult + print("$", stdio.style("dim") .. self.program, table.concat(self.args, " ") .. stdio.style("reset")) + + local function translateIoStrategyMappings(mappings: IoStrategyMapping) + local translatedMappings: process.SpawnOptionsStdio = {} + for field, value in mappings do + translatedMappings[field] = intoSpawnOptionsStdioKind(value) + end + + return translatedMappings + end + + local child = process.spawn(self.program, self.args, { + shell = true, + stdio = translateIoStrategyMappings(self.stdioStrategy or DEFAULT_STDIO_STRATEGY), + }) + + if not child.ok then + print(`\n{stdio.color("red")}[luau-lsp]{stdio.color("reset")} Exited with code`, child.code) + end + + return child +end + +return CommandBuilder diff --git a/.lune/lib/manifest.luau b/.lune/lib/manifest.luau index b2f1ede..a9398fa 100644 --- a/.lune/lib/manifest.luau +++ b/.lune/lib/manifest.luau @@ -1,71 +1,71 @@ -local fs = require("@lune/fs") -local serde = require("@lune/serde") - -export type SPDXLicense = - "MIT" - | "Apache-2.0" - | "BSD-2-Clause" - | "BSD-3-Clause" - | "GPL-2.0" - | "GPL-3.0" - | "LGPL-2.1" - | "LGPL-3.0" - | "MPL-2.0" - | "ISC" - | "Unlicense" - | "WTFPL" - | "Zlib" - | "CC0-1.0" - | "CC-BY-4.0" - | "CC-BY-SA-4.0" - | "BSL-1.0" - | "EPL-2.0" - | "AGPL-3.0" - -export type DependencySpecifier = (( - { name: string, version: string, index: string? } - | { workspace: string, version: string } - | { repo: string, rev: string, path: string? } -) & { - target: string?, -}) | { wally: string, version: string, index: string? } - -export type PackageTarget = { - environment: "luau" | "lune" | "roblox" | "roblox_server", - lib: string, -} | ({ environment: "luau" | "lune" } & ({ - bin: string, -} | { - scripts: { [string]: string }, -})) - -export type PesdeManifest = { - name: string, - version: string, - description: string?, - license: SPDXLicense?, - authors: { string }?, - repository: string?, - private: boolean?, - includes: { string }?, - pesde_version: string?, - workspace_members: { string }?, - target: PackageTarget, - build_files: { string }?, - scripts: { [string]: string }?, - indices: { [string]: string }, - wally_indices: { [string]: string }?, - overrides: { [string]: DependencySpecifier }?, - patches: { [string]: { [string]: string } }?, - place: { [string]: string }?, - dependencies: { [string]: DependencySpecifier }?, - peer_dependencies: { [string]: DependencySpecifier }?, - dev_dependencies: { [string]: DependencySpecifier }?, -} & T - -return function(path: string?, _phantom: T): PesdeManifest - local manifestContents = fs.readFile(path or "pesde.toml") - local decoded = serde.decode("toml", manifestContents) - - return decoded :: PesdeManifest -end +local fs = require("@lune/fs") +local serde = require("@lune/serde") + +export type SPDXLicense = + "MIT" + | "Apache-2.0" + | "BSD-2-Clause" + | "BSD-3-Clause" + | "GPL-2.0" + | "GPL-3.0" + | "LGPL-2.1" + | "LGPL-3.0" + | "MPL-2.0" + | "ISC" + | "Unlicense" + | "WTFPL" + | "Zlib" + | "CC0-1.0" + | "CC-BY-4.0" + | "CC-BY-SA-4.0" + | "BSL-1.0" + | "EPL-2.0" + | "AGPL-3.0" + +export type DependencySpecifier = (( + { name: string, version: string, index: string? } + | { workspace: string, version: string } + | { repo: string, rev: string, path: string? } +) & { + target: string?, +}) | { wally: string, version: string, index: string? } + +export type PackageTarget = { + environment: "luau" | "lune" | "roblox" | "roblox_server", + lib: string, +} | ({ environment: "luau" | "lune" } & ({ + bin: string, +} | { + scripts: { [string]: string }, +})) + +export type PesdeManifest = { + name: string, + version: string, + description: string?, + license: SPDXLicense?, + authors: { string }?, + repository: string?, + private: boolean?, + includes: { string }?, + pesde_version: string?, + workspace_members: { string }?, + target: PackageTarget, + build_files: { string }?, + scripts: { [string]: string }?, + indices: { [string]: string }, + wally_indices: { [string]: string }?, + overrides: { [string]: DependencySpecifier }?, + patches: { [string]: { [string]: string } }?, + place: { [string]: string }?, + dependencies: { [string]: DependencySpecifier }?, + peer_dependencies: { [string]: DependencySpecifier }?, + dev_dependencies: { [string]: DependencySpecifier }?, +} & T + +return function(path: string?, _phantom: T): PesdeManifest + local manifestContents = fs.readFile(path or "pesde.toml") + local decoded = serde.decode("toml", manifestContents) + + return decoded :: PesdeManifest +end diff --git a/.lune/tests/init.luau b/.lune/tests/init.luau index 8f6e7ba..054ad32 100644 --- a/.lune/tests/init.luau +++ b/.lune/tests/init.luau @@ -1,80 +1,79 @@ ---> Run tests using frktest runner - -local fs = require("@lune/fs") -local process = require("@lune/process") - -local frktest = require("../../lune_packages/frktest") -local reporter = require("./reporter") - --- HACK: Cast require to allow for dynamic paths in strict mode --- A more proper solution would be to use luau.load instead, but --- frktest requires its global state to be modified by test suites -local require = require :: ( - path: string -) -> ( - test: typeof(setmetatable( - {} :: { - case: (name: string, fn: () -> nil) -> (), - suite: (name: string, fn: () -> ()) -> (), - }, - { __index = frktest.test } - )) -) -> () - -local function discoverTests(dir: string) - local tests = {} - for _, file in fs.readDir(dir) do - local fullPath = `{dir}/{file}` - - -- Look for files ending in `.spec.luau` as tests - if fs.isFile(fullPath) and string.sub(file, -10) == ".spec.luau" then - table.insert(tests, fullPath) - end - - -- Recurse for directories - if fs.isDir(fullPath) then - local moreTests = discoverTests(fullPath) - - -- Why are the indices starting at 0???? What???? - table.move(moreTests, 0, #moreTests, #tests, tests) - end - end - - return tests -end - -local allowedTests = process.args -for _, test in discoverTests("src") do - -- If we are given any arguments, we only run those tests, otherwise, - -- we run all the tests - - -- So, to include only a certain set of test files, you can provide either - -- the full path of the test file or name of the test file, with or without - -- the `.spec.luau` extension - local baseName = string.match(test, "([^/\\]+)$") - - local withoutExt = string.sub(test, 1, -11) - local baseNameWithoutExt = string.match(withoutExt, "([^/\\]+)$") - - local isAllowed = #process.args == 0 - or table.find(allowedTests, test) - or table.find(allowedTests, withoutExt) - or table.find(allowedTests, baseName) - or table.find(allowedTests, baseNameWithoutExt) - - - local constructors = { - case = frktest.test.case, - suite = frktest.test.suite, - } - - if not isAllowed then - constructors.case = frktest.test.skip.case - constructors.suite = frktest.test.skip.suite - end - - require(`../../{test}`)(setmetatable(constructors, { __index = frktest.test })) -end - -reporter.init() -process.exit(tonumber(frktest.run())) \ No newline at end of file +--> Run tests using frktest runner + +local fs = require("@lune/fs") +local process = require("@lune/process") + +local frktest = require("../../lune_packages/frktest") +local reporter = require("./reporter") + +-- HACK: Cast require to allow for dynamic paths in strict mode +-- A more proper solution would be to use luau.load instead, but +-- frktest requires its global state to be modified by test suites +local require = require :: ( + path: string +) -> ( + test: typeof(setmetatable( + {} :: { + case: (name: string, fn: () -> nil) -> (), + suite: (name: string, fn: () -> ()) -> (), + }, + { __index = frktest.test } + )) +) -> () + +local function discoverTests(dir: string) + local tests = {} + for _, file in fs.readDir(dir) do + local fullPath = `{dir}/{file}` + + -- Look for files ending in `.spec.luau` as tests + if fs.isFile(fullPath) and string.sub(file, -10) == ".spec.luau" then + table.insert(tests, fullPath) + end + + -- Recurse for directories + if fs.isDir(fullPath) then + local moreTests = discoverTests(fullPath) + + -- Why are the indices starting at 0???? What???? + table.move(moreTests, 0, #moreTests, #tests, tests) + end + end + + return tests +end + +local allowedTests = process.args +for _, test in discoverTests("src") do + -- If we are given any arguments, we only run those tests, otherwise, + -- we run all the tests + + -- So, to include only a certain set of test files, you can provide either + -- the full path of the test file or name of the test file, with or without + -- the `.spec.luau` extension + local baseName = string.match(test, "([^/\\]+)$") + + local withoutExt = string.sub(test, 1, -11) + local baseNameWithoutExt = string.match(withoutExt, "([^/\\]+)$") + + local isAllowed = #process.args == 0 + or table.find(allowedTests, test) + or table.find(allowedTests, withoutExt) + or table.find(allowedTests, baseName) + or table.find(allowedTests, baseNameWithoutExt) + + local constructors = { + case = frktest.test.case, + suite = frktest.test.suite, + } + + if not isAllowed then + constructors.case = frktest.test.skip.case + constructors.suite = frktest.test.skip.suite + end + + require(`../../{test}`)(setmetatable(constructors, { __index = frktest.test })) +end + +reporter.init() +process.exit(tonumber(frktest.run())) diff --git a/.lune/tests/reporter.luau b/.lune/tests/reporter.luau index 887851d..ea4160f 100644 --- a/.lune/tests/reporter.luau +++ b/.lune/tests/reporter.luau @@ -1,60 +1,59 @@ -local stdio = require("@lune/stdio") - -local frktest = require("../../lune_packages/frktest") -local Reporter = frktest._reporters.lune_console_reporter - -local watch = require("../lib/channel") - -local STYLE = table.freeze({ - suite = function(name: string) - return `{stdio.style("bold")}{stdio.color("purple")}SUITE{stdio.style("reset")} {name}` - end, - - report = function(name: string, state: "success" | "error" | "skip", elapsed: number) - local state_color: stdio.Color = if state == "success" then "green" - elseif state == "error" then "red" - elseif state == "skip" then "yellow" - else error("Invalid test state") - return ` {stdio.style("bold")}{stdio.color(state_color)}{if state == "skip" then "SKIP" else "TEST"}{stdio.style( - "reset" - )} {name} [{stdio.style("dim")}{string.format("%.2fms", elapsed)}{stdio.style("reset")}]` - end, -}) - -local ReporterExt = {} -function ReporterExt.init() - frktest.test.on_suite_enter(function(suite) - print(STYLE.suite(suite.name)) - end) - - frktest.test.on_suite_leave(function() - stdio.write("\n") - end) - - local send_ts, recv_ts = watch((nil :: any) :: number) - - frktest.test.on_test_enter(function() - -- Send over some high precision timestamp when the test starts - return send_ts(os.clock()) - end) - - frktest.test.on_test_leave(function(test) - print( - STYLE.report( - test.name, - if test.failed then "error" else "success", - - -- Await receival of the timestamp and convert the difference to ms - (os.clock() - recv_ts()) * 1000 - ) - ) - end) - - frktest.test.on_test_skipped(function(test) - print(STYLE.report(test.name, "skip", 0)) - end) - - Reporter.init() -end - -return setmetatable(ReporterExt, { __index = Reporter }) +local stdio = require("@lune/stdio") + +local frktest = require("../../lune_packages/frktest") +local Reporter = frktest._reporters.lune_console_reporter + +local watch = require("../lib/channel") + +local STYLE = table.freeze({ + suite = function(name: string) + return `{stdio.style("bold")}{stdio.color("purple")}SUITE{stdio.style("reset")} {name}` + end, + + report = function(name: string, state: "success" | "error" | "skip", elapsed: number) + local state_color: stdio.Color = if state == "success" + then "green" + elseif state == "error" then "red" + elseif state == "skip" then "yellow" + else error("Invalid test state") + return ` {stdio.style("bold")}{stdio.color(state_color)}{if state == "skip" then "SKIP" else "TEST"}{stdio.style( + "reset" + )} {name} [{stdio.style("dim")}{string.format("%.2fms", elapsed)}{stdio.style("reset")}]` + end, +}) + +local ReporterExt = {} +function ReporterExt.init() + frktest.test.on_suite_enter(function(suite) + print(STYLE.suite(suite.name)) + end) + + frktest.test.on_suite_leave(function() + stdio.write("\n") + end) + + local send_ts, recv_ts = watch((nil :: any) :: number) + + frktest.test.on_test_enter(function() + -- Send over some high precision timestamp when the test starts + return send_ts(os.clock()) + end) + + frktest.test.on_test_leave(function(test) + print(STYLE.report( + test.name, + if test.failed then "error" else "success", + + -- Await receival of the timestamp and convert the difference to ms + (os.clock() - recv_ts()) * 1000 + )) + end) + + frktest.test.on_test_skipped(function(test) + print(STYLE.report(test.name, "skip", 0)) + end) + + Reporter.init() +end + +return setmetatable(ReporterExt, { __index = Reporter }) diff --git a/.lune/typecheck.luau b/.lune/typecheck.luau index 2818b79..3e56319 100644 --- a/.lune/typecheck.luau +++ b/.lune/typecheck.luau @@ -1,16 +1,16 @@ ---> Run luau-lsp analysis to check for type errors - -local process = require("@lune/process") - -local CommandBuilder = require("./lib/exec") - -process.exit( - CommandBuilder.new("~/.rokit/bin/luau-lsp") - :withArg("analyze") - :withArgs({ "--settings", ".vscode/settings.json" }) - :withArgs({ "--ignore", "'**/.pesde/**'" }) - :withArgs({ "--ignore", "'./test-files/**'" }) - :withArg(".") - :withStdioStrategy("forward") - :exec().code -) \ No newline at end of file +--> Run luau-lsp analysis to check for type errors + +local process = require("@lune/process") + +local CommandBuilder = require("./lib/exec") + +process.exit( + CommandBuilder.new("~/.rokit/bin/luau-lsp") + :withArg("analyze") + :withArgs({ "--settings", ".vscode/settings.json" }) + :withArgs({ "--ignore", "'**/.pesde/**'" }) + :withArgs({ "--ignore", "'./test-files/**'" }) + :withArg(".") + :withStdioStrategy("forward") + :exec().code +) From 88c305eb02956d9543b4d6d87bf8a302804b280c Mon Sep 17 00:00:00 2001 From: Erica Marigold Date: Sun, 8 Dec 2024 17:44:30 +0000 Subject: [PATCH 32/39] chore: include `.styluaignore` to apply formatting for `.lune` --- .styluaignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .styluaignore diff --git a/.styluaignore b/.styluaignore new file mode 100644 index 0000000..4b17827 --- /dev/null +++ b/.styluaignore @@ -0,0 +1 @@ +!.lune/ From 57f7719b8139a79ec1f0bf602ded040ec580adea Mon Sep 17 00:00:00 2001 From: Erica Marigold Date: Sun, 8 Dec 2024 17:49:21 +0000 Subject: [PATCH 33/39] chore(lune): use pesde-managed luau-lsp install --- .lune/typecheck.luau | 2 +- pesde.lock | 179 +++++++++++++++++++++++++++++++++++++++++++ pesde.toml | 1 + 3 files changed, 181 insertions(+), 1 deletion(-) diff --git a/.lune/typecheck.luau b/.lune/typecheck.luau index 3e56319..2b41268 100644 --- a/.lune/typecheck.luau +++ b/.lune/typecheck.luau @@ -5,7 +5,7 @@ local process = require("@lune/process") local CommandBuilder = require("./lib/exec") process.exit( - CommandBuilder.new("~/.rokit/bin/luau-lsp") + CommandBuilder.new("luau_lsp") :withArg("analyze") :withArgs({ "--settings", ".vscode/settings.json" }) :withArgs({ "--ignore", "'**/.pesde/**'" }) diff --git a/pesde.lock b/pesde.lock index 5c43444..9f12d5e 100644 --- a/pesde.lock +++ b/pesde.lock @@ -5,6 +5,35 @@ target = "lune" [workspace."pesde/scripts_core"] lune = "" +[workspace."pesde/scripts_rojo"] +lune = ".pesde/rojo" + +[graph."0x5eal/semver"."0.1.1 luau"] +resolved_ty = "peer" + +[graph."0x5eal/semver"."0.1.1 luau".target] +environment = "luau" +lib = "lib/init.luau" + +[graph."0x5eal/semver"."0.1.1 luau".dependencies] +"lukadev_0/option" = ["1.2.0 luau", "option"] +"lukadev_0/result" = ["1.2.0 luau", "result"] + +[graph."0x5eal/semver"."0.1.1 luau".pkg_ref] +ref_ty = "pesde" +name = "0x5eal/semver" +version = "0.1.1" +index_url = "https://github.com/daimond113/pesde-index" + +[graph."0x5eal/semver"."0.1.1 luau".pkg_ref.dependencies] +frktest = [{ name = "itsfrank/frktest", version = "^0.0.2", index = "https://github.com/daimond113/pesde-index", target = "lune" }, "dev"] +option = [{ name = "lukadev_0/option", version = "^1.2.0", index = "https://github.com/daimond113/pesde-index" }, "peer"] +result = [{ name = "lukadev_0/result", version = "^1.2.0", index = "https://github.com/daimond113/pesde-index" }, "peer"] + +[graph."0x5eal/semver"."0.1.1 luau".pkg_ref.target] +environment = "luau" +lib = "lib/init.luau" + [graph."itsfrank/frktest"."0.0.2 lune"] direct = ["frktest", { name = "itsfrank/frktest", version = "^0.0.2" }, "dev"] resolved_ty = "dev" @@ -23,6 +52,29 @@ index_url = "https://github.com/daimond113/pesde-index" environment = "lune" lib = "src/_pesde_init.luau" +[graph."jiwonz/dirs"."0.1.2 lune"] +resolved_ty = "standard" + +[graph."jiwonz/dirs"."0.1.2 lune".target] +environment = "lune" +lib = "src/init.luau" + +[graph."jiwonz/dirs"."0.1.2 lune".dependencies] +"jiwonz/pathfs" = ["0.1.0 lune", "pathfs"] + +[graph."jiwonz/dirs"."0.1.2 lune".pkg_ref] +ref_ty = "pesde" +name = "jiwonz/dirs" +version = "0.1.2" +index_url = "https://github.com/daimond113/pesde-index" + +[graph."jiwonz/dirs"."0.1.2 lune".pkg_ref.dependencies] +pathfs = [{ name = "jiwonz/pathfs", version = "^0.1.0", index = "https://github.com/daimond113/pesde-index" }, "standard"] + +[graph."jiwonz/dirs"."0.1.2 lune".pkg_ref.target] +environment = "lune" +lib = "src/init.luau" + [graph."jiwonz/pathfs"."0.1.0 lune"] direct = ["pathfs", { name = "jiwonz/pathfs", version = "^0.1.0" }, "dev"] resolved_ty = "dev" @@ -40,3 +92,130 @@ index_url = "https://github.com/daimond113/pesde-index" [graph."jiwonz/pathfs"."0.1.0 lune".pkg_ref.target] environment = "lune" lib = "init.luau" + +[graph."lukadev_0/option"."1.2.0 lune"] +resolved_ty = "standard" + +[graph."lukadev_0/option"."1.2.0 lune".target] +environment = "lune" +lib = "lib/init.luau" + +[graph."lukadev_0/option"."1.2.0 lune".pkg_ref] +ref_ty = "pesde" +name = "lukadev_0/option" +version = "1.2.0" +index_url = "https://github.com/daimond113/pesde-index" + +[graph."lukadev_0/option"."1.2.0 lune".pkg_ref.target] +environment = "lune" +lib = "lib/init.luau" + +[graph."lukadev_0/option"."1.2.0 luau"] +resolved_ty = "peer" + +[graph."lukadev_0/option"."1.2.0 luau".target] +environment = "luau" +lib = "lib/init.luau" + +[graph."lukadev_0/option"."1.2.0 luau".pkg_ref] +ref_ty = "pesde" +name = "lukadev_0/option" +version = "1.2.0" +index_url = "https://github.com/daimond113/pesde-index" + +[graph."lukadev_0/option"."1.2.0 luau".pkg_ref.target] +environment = "luau" +lib = "lib/init.luau" + +[graph."lukadev_0/result"."1.2.0 lune"] +resolved_ty = "standard" + +[graph."lukadev_0/result"."1.2.0 lune".target] +environment = "lune" +lib = "lib/init.luau" + +[graph."lukadev_0/result"."1.2.0 lune".pkg_ref] +ref_ty = "pesde" +name = "lukadev_0/result" +version = "1.2.0" +index_url = "https://github.com/daimond113/pesde-index" + +[graph."lukadev_0/result"."1.2.0 lune".pkg_ref.target] +environment = "lune" +lib = "lib/init.luau" + +[graph."lukadev_0/result"."1.2.0 luau"] +resolved_ty = "peer" + +[graph."lukadev_0/result"."1.2.0 luau".target] +environment = "luau" +lib = "lib/init.luau" + +[graph."lukadev_0/result"."1.2.0 luau".pkg_ref] +ref_ty = "pesde" +name = "lukadev_0/result" +version = "1.2.0" +index_url = "https://github.com/daimond113/pesde-index" + +[graph."lukadev_0/result"."1.2.0 luau".pkg_ref.target] +environment = "luau" +lib = "lib/init.luau" + +[graph."pesde/luau_lsp"."1.36.0 lune"] +direct = ["luau_lsp", { name = "pesde/luau_lsp", version = "^1.36.0" }, "dev"] +resolved_ty = "dev" + +[graph."pesde/luau_lsp"."1.36.0 lune".target] +environment = "lune" +bin = "init.luau" + +[graph."pesde/luau_lsp"."1.36.0 lune".dependencies] +"lukadev_0/option" = ["1.2.0 lune", "option"] +"lukadev_0/result" = ["1.2.0 lune", "result"] +"pesde/toolchainlib" = ["0.1.2 lune", "core"] + +[graph."pesde/luau_lsp"."1.36.0 lune".pkg_ref] +ref_ty = "pesde" +name = "pesde/luau_lsp" +version = "1.36.0" +index_url = "https://github.com/pesde-pkg/index" + +[graph."pesde/luau_lsp"."1.36.0 lune".pkg_ref.dependencies] +core = [{ name = "pesde/toolchainlib", version = "^0.1.2", index = "https://github.com/daimond113/pesde-index", target = "lune" }, "standard"] +option = [{ name = "lukadev_0/option", version = "^1.2.0", index = "https://github.com/daimond113/pesde-index" }, "standard"] +result = [{ name = "lukadev_0/result", version = "^1.2.0", index = "https://github.com/daimond113/pesde-index" }, "standard"] + +[graph."pesde/luau_lsp"."1.36.0 lune".pkg_ref.target] +environment = "lune" +bin = "init.luau" + +[graph."pesde/toolchainlib"."0.1.2 lune"] +resolved_ty = "standard" + +[graph."pesde/toolchainlib"."0.1.2 lune".target] +environment = "lune" +lib = "src/init.luau" + +[graph."pesde/toolchainlib"."0.1.2 lune".dependencies] +"0x5eal/semver" = ["0.1.1 luau", "semver"] +"jiwonz/dirs" = ["0.1.2 lune", "dirs"] +"jiwonz/pathfs" = ["0.1.0 lune", "pathfs"] +"lukadev_0/option" = ["1.2.0 lune", "option"] +"lukadev_0/result" = ["1.2.0 lune", "result"] + +[graph."pesde/toolchainlib"."0.1.2 lune".pkg_ref] +ref_ty = "pesde" +name = "pesde/toolchainlib" +version = "0.1.2" +index_url = "https://github.com/daimond113/pesde-index" + +[graph."pesde/toolchainlib"."0.1.2 lune".pkg_ref.dependencies] +dirs = [{ name = "jiwonz/dirs", version = "^0.1.1", index = "https://github.com/daimond113/pesde-index" }, "standard"] +option = [{ name = "lukadev_0/option", version = "^1.2.0", index = "https://github.com/daimond113/pesde-index" }, "peer"] +pathfs = [{ name = "jiwonz/pathfs", version = "^0.1.0", index = "https://github.com/daimond113/pesde-index" }, "standard"] +result = [{ name = "lukadev_0/result", version = "^1.2.0", index = "https://github.com/daimond113/pesde-index" }, "peer"] +semver = [{ name = "0x5eal/semver", version = "^0.1.1", index = "https://github.com/daimond113/pesde-index", target = "luau" }, "peer"] + +[graph."pesde/toolchainlib"."0.1.2 lune".pkg_ref.target] +environment = "lune" +lib = "src/init.luau" diff --git a/pesde.toml b/pesde.toml index fa6d169..e75edea 100644 --- a/pesde.toml +++ b/pesde.toml @@ -29,6 +29,7 @@ lib = "src/init.luau" [dev_dependencies] frktest = { name = "itsfrank/frktest", version = "^0.0.2" } pathfs = { name = "jiwonz/pathfs", version = "^0.1.0" } +luau_lsp = { name = "pesde/luau_lsp", version = "^1.36.0" } [indices] default = "https://github.com/pesde-pkg/index" From 79cbf8fdf4814397cf9f21dda71e2115cbb0b56b Mon Sep 17 00:00:00 2001 From: Erica Marigold Date: Sun, 8 Dec 2024 17:51:01 +0000 Subject: [PATCH 34/39] chore(pkg): use pesde-managed stylua --- pesde.lock | 28 ++++++++++++++++++++++++++++ pesde.toml | 1 + 2 files changed, 29 insertions(+) diff --git a/pesde.lock b/pesde.lock index 9f12d5e..431e5bd 100644 --- a/pesde.lock +++ b/pesde.lock @@ -189,6 +189,34 @@ result = [{ name = "lukadev_0/result", version = "^1.2.0", index = "https://gith environment = "lune" bin = "init.luau" +[graph."pesde/stylua"."2.0.1 lune"] +direct = ["stylua", { name = "pesde/stylua", version = "^2.0.1" }, "dev"] +resolved_ty = "dev" + +[graph."pesde/stylua"."2.0.1 lune".target] +environment = "lune" +bin = "init.luau" + +[graph."pesde/stylua"."2.0.1 lune".dependencies] +"lukadev_0/option" = ["1.2.0 lune", "option"] +"lukadev_0/result" = ["1.2.0 lune", "result"] +"pesde/toolchainlib" = ["0.1.2 lune", "core"] + +[graph."pesde/stylua"."2.0.1 lune".pkg_ref] +ref_ty = "pesde" +name = "pesde/stylua" +version = "2.0.1" +index_url = "https://github.com/pesde-pkg/index" + +[graph."pesde/stylua"."2.0.1 lune".pkg_ref.dependencies] +core = [{ name = "pesde/toolchainlib", version = "^0.1.0", index = "https://github.com/daimond113/pesde-index", target = "lune" }, "standard"] +option = [{ name = "lukadev_0/option", version = "^1.2.0", index = "https://github.com/daimond113/pesde-index" }, "standard"] +result = [{ name = "lukadev_0/result", version = "^1.2.0", index = "https://github.com/daimond113/pesde-index" }, "standard"] + +[graph."pesde/stylua"."2.0.1 lune".pkg_ref.target] +environment = "lune" +bin = "init.luau" + [graph."pesde/toolchainlib"."0.1.2 lune"] resolved_ty = "standard" diff --git a/pesde.toml b/pesde.toml index e75edea..e17782d 100644 --- a/pesde.toml +++ b/pesde.toml @@ -30,6 +30,7 @@ lib = "src/init.luau" frktest = { name = "itsfrank/frktest", version = "^0.0.2" } pathfs = { name = "jiwonz/pathfs", version = "^0.1.0" } luau_lsp = { name = "pesde/luau_lsp", version = "^1.36.0" } +stylua = { name = "pesde/stylua", version = "^2.0.1" } [indices] default = "https://github.com/pesde-pkg/index" From 80df6f053daebcc18da1f5bd7321441e744362f6 Mon Sep 17 00:00:00 2001 From: Erica Marigold Date: Sun, 8 Dec 2024 17:52:46 +0000 Subject: [PATCH 35/39] chore(lune): override alias and use proper luau-lsp bin name --- .lune/typecheck.luau | 2 +- pesde.lock | 2 +- pesde.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.lune/typecheck.luau b/.lune/typecheck.luau index 2b41268..bb7a176 100644 --- a/.lune/typecheck.luau +++ b/.lune/typecheck.luau @@ -5,7 +5,7 @@ local process = require("@lune/process") local CommandBuilder = require("./lib/exec") process.exit( - CommandBuilder.new("luau_lsp") + CommandBuilder.new("luau-lsp") :withArg("analyze") :withArgs({ "--settings", ".vscode/settings.json" }) :withArgs({ "--ignore", "'**/.pesde/**'" }) diff --git a/pesde.lock b/pesde.lock index 431e5bd..aa705e2 100644 --- a/pesde.lock +++ b/pesde.lock @@ -162,7 +162,7 @@ environment = "luau" lib = "lib/init.luau" [graph."pesde/luau_lsp"."1.36.0 lune"] -direct = ["luau_lsp", { name = "pesde/luau_lsp", version = "^1.36.0" }, "dev"] +direct = ["luau-lsp", { name = "pesde/luau_lsp", version = "^1.36.0" }, "dev"] resolved_ty = "dev" [graph."pesde/luau_lsp"."1.36.0 lune".target] diff --git a/pesde.toml b/pesde.toml index e17782d..407ac9d 100644 --- a/pesde.toml +++ b/pesde.toml @@ -29,7 +29,7 @@ lib = "src/init.luau" [dev_dependencies] frktest = { name = "itsfrank/frktest", version = "^0.0.2" } pathfs = { name = "jiwonz/pathfs", version = "^0.1.0" } -luau_lsp = { name = "pesde/luau_lsp", version = "^1.36.0" } +luau-lsp = { name = "pesde/luau_lsp", version = "^1.36.0" } stylua = { name = "pesde/stylua", version = "^2.0.1" } [indices] From 02c835f80fa0cde4e32c9c0de36dd517af5ecaf6 Mon Sep 17 00:00:00 2001 From: Erica Marigold Date: Sun, 8 Dec 2024 23:51:56 +0530 Subject: [PATCH 36/39] chore(lune): fix manifest `PackageTarget` type Co-authored-by: Luka <47296785+lukadev-0@users.noreply.github.com> --- .lune/lib/manifest.luau | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.lune/lib/manifest.luau b/.lune/lib/manifest.luau index a9398fa..63a69d2 100644 --- a/.lune/lib/manifest.luau +++ b/.lune/lib/manifest.luau @@ -31,13 +31,14 @@ export type DependencySpecifier = (( }) | { wally: string, version: string, index: string? } export type PackageTarget = { - environment: "luau" | "lune" | "roblox" | "roblox_server", + environment: "roblox" | "roblox_server", lib: string, -} | ({ environment: "luau" | "lune" } & ({ - bin: string, } | { + environment: "luau" | "lune", + lib: string, + bin: string, scripts: { [string]: string }, -})) +} export type PesdeManifest = { name: string, From d515a89d012758ede1fb2fba227cfa1227bc8b4d Mon Sep 17 00:00:00 2001 From: Erica Marigold Date: Sun, 8 Dec 2024 18:24:10 +0000 Subject: [PATCH 37/39] chore(lune): address requested review changes Co-authored-by: Luka <47296785+lukadev-0@users.noreply.github.com> --- .lune/lib/channel.luau | 2 +- .lune/lib/manifest.luau | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.lune/lib/channel.luau b/.lune/lib/channel.luau index 3df65fc..8fa1479 100644 --- a/.lune/lib/channel.luau +++ b/.lune/lib/channel.luau @@ -15,7 +15,7 @@ type Watch = { receivers: { thread }, } ---- Crates a new `Watch` channel, returning its send and receive handles. +--- Creates a new `Watch` channel, returning its send and receive handles. local function chan(_phantom: T): ((T) -> (), () -> T?) local watch: Watch = { value = nil, diff --git a/.lune/lib/manifest.luau b/.lune/lib/manifest.luau index 63a69d2..0c524d7 100644 --- a/.lune/lib/manifest.luau +++ b/.lune/lib/manifest.luau @@ -25,10 +25,11 @@ export type SPDXLicense = export type DependencySpecifier = (( { name: string, version: string, index: string? } | { workspace: string, version: string } - | { repo: string, rev: string, path: string? } ) & { target: string?, -}) | { wally: string, version: string, index: string? } +}) + | { wally: string, version: string, index: string? } + | { repo: string, rev: string, path: string? } export type PackageTarget = { environment: "roblox" | "roblox_server", From e907258396302bbd09c519ae0cee0af7d2ccfa25 Mon Sep 17 00:00:00 2001 From: Erica Marigold Date: Sun, 8 Dec 2024 18:30:02 +0000 Subject: [PATCH 38/39] chore(stylua): use tabs instead of spaces and bring back 80 width --- stylua.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stylua.toml b/stylua.toml index 8fa16f6..4821978 100644 --- a/stylua.toml +++ b/stylua.toml @@ -1,10 +1,10 @@ line_endings = "Unix" quote_style = "AutoPreferDouble" -indent_type = "Spaces" +indent_type = "Tabs" call_parentheses = "Always" indent_width = 4 -# column_width = 80 +column_width = 80 [sort_requires] enabled = true From ef8953fdcc3bdacdc90633b7e3519ec8e1b61bc4 Mon Sep 17 00:00:00 2001 From: Erica Marigold Date: Sun, 8 Dec 2024 18:30:38 +0000 Subject: [PATCH 39/39] style: apply stylua formatter --- .lune/build.luau | 259 ++++++++++++---------- .lune/fmt.luau | 8 +- .lune/lib/channel.luau | 44 ++-- .lune/lib/exec.luau | 144 ++++++------ .lune/lib/manifest.luau | 121 +++++----- .lune/tests/init.luau | 92 ++++---- .lune/tests/reporter.luau | 82 ++++--- .lune/typecheck.luau | 16 +- src/generators/rojo/sourcemap.luau | 54 ++--- src/generators/rojo/sourcemap.spec.luau | 131 ++++++----- src/generators/rojo/sync_config.luau | 138 ++++++------ src/generators/rojo/sync_config.spec.luau | 43 ++-- src/init.luau | 12 +- 13 files changed, 625 insertions(+), 519 deletions(-) diff --git a/.lune/build.luau b/.lune/build.luau index baa11a0..a3577cd 100644 --- a/.lune/build.luau +++ b/.lune/build.luau @@ -11,18 +11,18 @@ local pathfs = require("../lune_packages/pathfs") type ToolChoice = "rojo" type ManifestExt = { - scripts: { - [ToolChoice]: { - version: string, - tool_dependencies: { { [string]: manifest.DependencySpecifier } }, - }, - }, + scripts: { + [ToolChoice]: { + version: string, + tool_dependencies: { { [string]: manifest.DependencySpecifier } }, + }, + }, } local SCRIPTS_DIR = pathfs.getAbsolutePathOf(pathfs.Path.from(".pesde")) local MANIFEST = manifest(nil, (nil :: any) :: { meta: ManifestExt }) local SCRIPTS = { - syncConfigGenerator = [[local process = require("@lune/process") + syncConfigGenerator = [[local process = require("@lune/process") local args = table.clone(process.args) local ok, _ = @@ -31,7 +31,7 @@ if not ok then return process.exit(1) end]], - sourcemapGenerator = [[local process = require("@lune/process") + sourcemapGenerator = [[local process = require("@lune/process") local ok = require("./lune_packages/core").generators.%s.sourcemap(process.args[1]) if not ok then @@ -54,142 +54,171 @@ Prints out a sourcemap for Wally dependencies for type extraction. ]] local function logPrefix(type: "error" | "info") - local statusColor: stdio.Color = if type == "error" - then "red" - elseif type == "info" then "green" - else error(`Invalid type: {type}`) + local statusColor: stdio.Color = if type == "error" + then "red" + elseif type == "info" then "green" + else error(`Invalid type: {type}`) - return `main::{stdio.style("bold")}{stdio.color(statusColor)}{type}{stdio.color("reset")}` + return `main::{stdio.style("bold")}{stdio.color(statusColor)}{type}{stdio.color( + "reset" + )}` end local INFO_PREFIX = `[ {logPrefix("info")}]` local _ERROR_PREFIX = `[{logPrefix("error")}]` local function installDeps(): number - local PESDE_INFO_PREFIX = string.gsub(`[{logPrefix("info")}]`, "main", "pesde") - local PESDE_ERROR_PREFIX = string.gsub(`[{logPrefix("error")}]`, "main", "pesde") - local SPINNER_STATES = { "⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏" } + local PESDE_INFO_PREFIX = + string.gsub(`[{logPrefix("info")}]`, "main", "pesde") + local PESDE_ERROR_PREFIX = + string.gsub(`[{logPrefix("error")}]`, "main", "pesde") + local SPINNER_STATES = + { "⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏" } - stdio.write(`{PESDE_INFO_PREFIX} Installing dependencies with pesde`) + stdio.write(`{PESDE_INFO_PREFIX} Installing dependencies with pesde`) - -- Spawn our thread to display the spinner - local spinnerThread = task.spawn(function() - -- Hide the cursor - stdio.write("\x1b[?25l") + -- Spawn our thread to display the spinner + local spinnerThread = task.spawn(function() + -- Hide the cursor + stdio.write("\x1b[?25l") - -- Display the spinner - while true do - for _, state in SPINNER_STATES do - stdio.write(` {state}`) - stdio.write( - -- Moves the cursor back 1 spot and clears everything after - "\x1b[2D\x1b[0K" - ) - end - end - end) + -- Display the spinner + while true do + for _, state in SPINNER_STATES do + stdio.write(` {state}`) + stdio.write( + -- Moves the cursor back 1 spot and clears everything after + "\x1b[2D\x1b[0K" + ) + end + end + end) - -- `process_spawn` is an async rust function, so tokio spawns a hardware - -- thread and mlua will yield the current thread, allowing for `spinnerThread` - -- to continue executing - local child = process.spawn("pesde", { "install" }, { - stdio = "default", - }) + -- `process_spawn` is an async rust function, so tokio spawns a hardware + -- thread and mlua will yield the current thread, allowing for `spinnerThread` + -- to continue executing + local child = process.spawn("pesde", { "install" }, { + stdio = "default", + }) - -- If we reach this point, that means mlua resumed the current thread and - -- `process_spawn` returned or errored. We can now close `spinnerThread` and - -- clean up + -- If we reach this point, that means mlua resumed the current thread and + -- `process_spawn` returned or errored. We can now close `spinnerThread` and + -- clean up - task.cancel(spinnerThread) - stdio.write( - -- Clear the current line, move cursor back to its beginning and - -- show it - "\x1b[2K\x1b[1G\x1b[?25h" - ) + task.cancel(spinnerThread) + stdio.write( + -- Clear the current line, move cursor back to its beginning and + -- show it + "\x1b[2K\x1b[1G\x1b[?25h" + ) - if not child.ok then - stdio.ewrite(`{PESDE_ERROR_PREFIX} Failed to install dependencies with pesde, error:\n`) - stdio.ewrite(child.stderr) - end + if not child.ok then + stdio.ewrite( + `{PESDE_ERROR_PREFIX} Failed to install dependencies with pesde, error:\n` + ) + stdio.ewrite(child.stderr) + end - stdio.write(`{PESDE_INFO_PREFIX} Installed dependencies with pesde successfully\n`) + stdio.write( + `{PESDE_INFO_PREFIX} Installed dependencies with pesde successfully\n` + ) - return child.code + return child.code end for tool, generators in lib.generators do - local startTime = os.clock() + local startTime = os.clock() - -- For each tool, we generate a respective manifests and scripts - local toolChoice = tool :: ToolChoice - local toolScriptsDir = SCRIPTS_DIR:join(toolChoice) - local toolMeta = MANIFEST.meta.scripts[toolChoice] + -- For each tool, we generate a respective manifests and scripts + local toolChoice = tool :: ToolChoice + local toolScriptsDir = SCRIPTS_DIR:join(toolChoice) + local toolMeta = MANIFEST.meta.scripts[toolChoice] - if not pathfs.isDir(toolScriptsDir) then - pathfs.writeDir(toolScriptsDir) - end + if not pathfs.isDir(toolScriptsDir) then + pathfs.writeDir(toolScriptsDir) + end - local capitalisedToolChoice = string.gsub(toolChoice, "^%l", string.upper) + local capitalisedToolChoice = string.gsub(toolChoice, "^%l", string.upper) - -- Define the manifest for the tool - local toolManifest: manifest.PesdeManifest = { - name = `pesde/scripts_{toolChoice}`, - version = toolMeta.version, - -- For the description, we capitalize the first letter of the tool name here - -- since it is a proper noun - description = `Scripts for {capitalisedToolChoice}-based Roblox projects.`, - authors = { - "daimond113 (https://www.daimond113.com/)", - "Erica Marigold ", - }, - repository = "https://github.com/pesde-pkg/scripts", - license = "MIT", - includes = { - "roblox_sync_config_generator.luau", - "sourcemap_generator.luau", - "pesde.toml", - "README.md", - "LICENSE", - }, + -- Define the manifest for the tool + local toolManifest: manifest.PesdeManifest = { + name = `pesde/scripts_{toolChoice}`, + version = toolMeta.version, + -- For the description, we capitalize the first letter of the tool name here + -- since it is a proper noun + description = `Scripts for {capitalisedToolChoice}-based Roblox projects.`, + authors = { + "daimond113 (https://www.daimond113.com/)", + "Erica Marigold ", + }, + repository = "https://github.com/pesde-pkg/scripts", + license = "MIT", + includes = { + "roblox_sync_config_generator.luau", + "sourcemap_generator.luau", + "pesde.toml", + "README.md", + "LICENSE", + }, - target = { - environment = "lune", - scripts = { - roblox_sync_config_generator = "roblox_sync_config_generator.luau", - sourcemap_generator = "sourcemap_generator.luau", - }, - }, + target = { + environment = "lune", + scripts = { + roblox_sync_config_generator = "roblox_sync_config_generator.luau", + sourcemap_generator = "sourcemap_generator.luau", + }, + }, - peer_dependencies = toolMeta.tool_dependencies, - dependencies = { - core = { workspace = "pesde/scripts_core", version = "^" }, - }, + peer_dependencies = toolMeta.tool_dependencies, + dependencies = { + core = { workspace = "pesde/scripts_core", version = "^" }, + }, - indices = { - default = "https://github.com/pesde-pkg/index", - }, - } + indices = { + default = "https://github.com/pesde-pkg/index", + }, + } - -- Format the scripts for the tool - local syncConfigGeneratorScript, sourcemapGeneratorScript = - string.format(SCRIPTS.syncConfigGenerator, toolChoice), string.format(SCRIPTS.sourcemapGenerator, toolChoice) + -- Format the scripts for the tool + local syncConfigGeneratorScript, sourcemapGeneratorScript = + string.format(SCRIPTS.syncConfigGenerator, toolChoice), + string.format(SCRIPTS.sourcemapGenerator, toolChoice) - -- Finally, write all the generated files - pathfs.writeFile(toolScriptsDir:join("pesde.toml"), serde.encode("toml", toolManifest, true)) - pathfs.writeFile(toolScriptsDir:join("roblox_sync_config_generator.luau"), syncConfigGeneratorScript) - pathfs.writeFile(toolScriptsDir:join("sourcemap_generator.luau"), sourcemapGeneratorScript) - pathfs.writeFile( - toolScriptsDir:join("README.md"), - string.format(README_TMPL, toolChoice, capitalisedToolChoice, capitalisedToolChoice) - ) - pathfs.copy(pathfs.cwd:join("LICENSE"), toolScriptsDir:join("LICENSE"), true) + -- Finally, write all the generated files + pathfs.writeFile( + toolScriptsDir:join("pesde.toml"), + serde.encode("toml", toolManifest, true) + ) + pathfs.writeFile( + toolScriptsDir:join("roblox_sync_config_generator.luau"), + syncConfigGeneratorScript + ) + pathfs.writeFile( + toolScriptsDir:join("sourcemap_generator.luau"), + sourcemapGeneratorScript + ) + pathfs.writeFile( + toolScriptsDir:join("README.md"), + string.format( + README_TMPL, + toolChoice, + capitalisedToolChoice, + capitalisedToolChoice + ) + ) + pathfs.copy( + pathfs.cwd:join("LICENSE"), + toolScriptsDir:join("LICENSE"), + true + ) - stdio.write( - `{INFO_PREFIX} Generated script project for tool {toolChoice} ({stdio.style("dim")}{string.format( - "%.2fms", - (os.clock() - startTime) * 1000 - )}{stdio.style("reset")})\n` - ) + stdio.write( + `{INFO_PREFIX} Generated script project for tool {toolChoice} ({stdio.style( + "dim" + )}{string.format("%.2fms", (os.clock() - startTime) * 1000)}{stdio.style( + "reset" + )})\n` + ) end -- Now we install the dependencies for the newly created projects diff --git a/.lune/fmt.luau b/.lune/fmt.luau index 77be22a..1976516 100644 --- a/.lune/fmt.luau +++ b/.lune/fmt.luau @@ -4,4 +4,10 @@ local process = require("@lune/process") local CommandBuilder = require("./lib/exec") -process.exit(CommandBuilder.new("stylua"):withArg("."):withArgs(process.args):withStdioStrategy("forward"):exec().code) +process.exit( + CommandBuilder.new("stylua") + :withArg(".") + :withArgs(process.args) + :withStdioStrategy("forward") + :exec().code +) diff --git a/.lune/lib/channel.luau b/.lune/lib/channel.luau index 8fa1479..bce40c0 100644 --- a/.lune/lib/channel.luau +++ b/.lune/lib/channel.luau @@ -11,38 +11,38 @@ --- end) --- ``` type Watch = { - value: T?, - receivers: { thread }, + value: T?, + receivers: { thread }, } --- Creates a new `Watch` channel, returning its send and receive handles. local function chan(_phantom: T): ((T) -> (), () -> T?) - local watch: Watch = { - value = nil, - receivers = {}, - } + local watch: Watch = { + value = nil, + receivers = {}, + } - local function send(value: T) - watch.value = value + local function send(value: T) + watch.value = value - for _, receiver in watch.receivers do - coroutine.resume(receiver, value) - end - end + for _, receiver in watch.receivers do + coroutine.resume(receiver, value) + end + end - local function recv(): T - local value = watch.value - watch.value = nil + local function recv(): T + local value = watch.value + watch.value = nil - if value == nil then - table.insert(watch.receivers, coroutine.running()) - return coroutine.yield() - end + if value == nil then + table.insert(watch.receivers, coroutine.running()) + return coroutine.yield() + end - return value :: T - end + return value :: T + end - return send, recv + return send, recv end return chan diff --git a/.lune/lib/exec.luau b/.lune/lib/exec.luau index 9550729..0bcc6dd 100644 --- a/.lune/lib/exec.luau +++ b/.lune/lib/exec.luau @@ -5,99 +5,119 @@ local stdio = require("@lune/stdio") local CommandBuilder = {} -export type CommandBuilder = typeof(setmetatable({} :: CommandBuilderFields, { __index = CommandBuilder })) +export type CommandBuilder = typeof(setmetatable( + {} :: CommandBuilderFields, + { __index = CommandBuilder } +)) type CommandBuilderFields = { - program: string, - args: { string }, - stdioStrategy: IoStrategyMapping?, + program: string, + args: { string }, + stdioStrategy: IoStrategyMapping?, } export type StdioStrategy = "pipe" | "forward" | "none" export type IoStrategyMapping = { - stdout: StdioStrategy?, - stderr: StdioStrategy?, + stdout: StdioStrategy?, + stderr: StdioStrategy?, } local DEFAULT_STDIO_STRATEGY: IoStrategyMapping = { - stdout = "pipe", - stderr = "pipe", + stdout = "pipe", + stderr = "pipe", } function CommandBuilder.new(program: string) - return setmetatable( - { - program = program, - args = {}, - stdioStrategy = nil, - } :: CommandBuilderFields, - { - __index = CommandBuilder, - } - ) + return setmetatable( + { + program = program, + args = {}, + stdioStrategy = nil, + } :: CommandBuilderFields, + { + __index = CommandBuilder, + } + ) end -function CommandBuilder.withArg(self: CommandBuilder, arg: string): CommandBuilder - table.insert(self.args, arg) - return self +function CommandBuilder.withArg( + self: CommandBuilder, + arg: string +): CommandBuilder + table.insert(self.args, arg) + return self end -function CommandBuilder.withArgs(self: CommandBuilder, args: { string }): CommandBuilder - for _, arg in args do - self:withArg(arg) - end +function CommandBuilder.withArgs( + self: CommandBuilder, + args: { string } +): CommandBuilder + for _, arg in args do + self:withArg(arg) + end - return self + return self end function CommandBuilder.withStdioStrategy( - self: CommandBuilder, - strategy: StdioStrategy | IoStrategyMapping + self: CommandBuilder, + strategy: StdioStrategy | IoStrategyMapping ): CommandBuilder - self.stdioStrategy = if typeof(strategy) == "string" - then { - stdout = strategy, - stderr = strategy, - } - else strategy - return self + self.stdioStrategy = if typeof(strategy) == "string" + then { + stdout = strategy, + stderr = strategy, + } + else strategy + return self end -local function intoSpawnOptionsStdioKind(strategy: StdioStrategy): process.SpawnOptionsStdioKind - if strategy == "pipe" then - return "default" - end +local function intoSpawnOptionsStdioKind( + strategy: StdioStrategy +): process.SpawnOptionsStdioKind + if strategy == "pipe" then + return "default" + end - if strategy == "forward" then - return "forward" - end + if strategy == "forward" then + return "forward" + end - if strategy == "none" then - return "none" - end + if strategy == "none" then + return "none" + end - error(`Non-strategy provided: {strategy}`) + error(`Non-strategy provided: {strategy}`) end function CommandBuilder.exec(self: CommandBuilder): process.SpawnResult - print("$", stdio.style("dim") .. self.program, table.concat(self.args, " ") .. stdio.style("reset")) + print( + "$", + stdio.style("dim") .. self.program, + table.concat(self.args, " ") .. stdio.style("reset") + ) - local function translateIoStrategyMappings(mappings: IoStrategyMapping) - local translatedMappings: process.SpawnOptionsStdio = {} - for field, value in mappings do - translatedMappings[field] = intoSpawnOptionsStdioKind(value) - end + local function translateIoStrategyMappings(mappings: IoStrategyMapping) + local translatedMappings: process.SpawnOptionsStdio = {} + for field, value in mappings do + translatedMappings[field] = intoSpawnOptionsStdioKind(value) + end - return translatedMappings - end + return translatedMappings + end - local child = process.spawn(self.program, self.args, { - shell = true, - stdio = translateIoStrategyMappings(self.stdioStrategy or DEFAULT_STDIO_STRATEGY), - }) + local child = process.spawn(self.program, self.args, { + shell = true, + stdio = translateIoStrategyMappings( + self.stdioStrategy or DEFAULT_STDIO_STRATEGY + ), + }) - if not child.ok then - print(`\n{stdio.color("red")}[luau-lsp]{stdio.color("reset")} Exited with code`, child.code) - end + if not child.ok then + print( + `\n{stdio.color("red")}[luau-lsp]{stdio.color("reset")} Exited with code`, + child.code + ) + end - return child + return child end return CommandBuilder diff --git a/.lune/lib/manifest.luau b/.lune/lib/manifest.luau index 0c524d7..5380bf1 100644 --- a/.lune/lib/manifest.luau +++ b/.lune/lib/manifest.luau @@ -2,72 +2,79 @@ local fs = require("@lune/fs") local serde = require("@lune/serde") export type SPDXLicense = - "MIT" - | "Apache-2.0" - | "BSD-2-Clause" - | "BSD-3-Clause" - | "GPL-2.0" - | "GPL-3.0" - | "LGPL-2.1" - | "LGPL-3.0" - | "MPL-2.0" - | "ISC" - | "Unlicense" - | "WTFPL" - | "Zlib" - | "CC0-1.0" - | "CC-BY-4.0" - | "CC-BY-SA-4.0" - | "BSL-1.0" - | "EPL-2.0" - | "AGPL-3.0" + "MIT" + | "Apache-2.0" + | "BSD-2-Clause" + | "BSD-3-Clause" + | "GPL-2.0" + | "GPL-3.0" + | "LGPL-2.1" + | "LGPL-3.0" + | "MPL-2.0" + | "ISC" + | "Unlicense" + | "WTFPL" + | "Zlib" + | "CC0-1.0" + | "CC-BY-4.0" + | "CC-BY-SA-4.0" + | "BSL-1.0" + | "EPL-2.0" + | "AGPL-3.0" -export type DependencySpecifier = (( - { name: string, version: string, index: string? } - | { workspace: string, version: string } -) & { - target: string?, -}) - | { wally: string, version: string, index: string? } - | { repo: string, rev: string, path: string? } +export type DependencySpecifier = (({ + name: string, + version: string, + index: string?, +} | { workspace: string, version: string }) & { + target: string?, +}) | { + wally: string, + version: string, + index: string?, +} | { + repo: string, + rev: string, + path: string?, +} export type PackageTarget = { - environment: "roblox" | "roblox_server", - lib: string, + environment: "roblox" | "roblox_server", + lib: string, } | { - environment: "luau" | "lune", - lib: string, - bin: string, - scripts: { [string]: string }, + environment: "luau" | "lune", + lib: string, + bin: string, + scripts: { [string]: string }, } export type PesdeManifest = { - name: string, - version: string, - description: string?, - license: SPDXLicense?, - authors: { string }?, - repository: string?, - private: boolean?, - includes: { string }?, - pesde_version: string?, - workspace_members: { string }?, - target: PackageTarget, - build_files: { string }?, - scripts: { [string]: string }?, - indices: { [string]: string }, - wally_indices: { [string]: string }?, - overrides: { [string]: DependencySpecifier }?, - patches: { [string]: { [string]: string } }?, - place: { [string]: string }?, - dependencies: { [string]: DependencySpecifier }?, - peer_dependencies: { [string]: DependencySpecifier }?, - dev_dependencies: { [string]: DependencySpecifier }?, + name: string, + version: string, + description: string?, + license: SPDXLicense?, + authors: { string }?, + repository: string?, + private: boolean?, + includes: { string }?, + pesde_version: string?, + workspace_members: { string }?, + target: PackageTarget, + build_files: { string }?, + scripts: { [string]: string }?, + indices: { [string]: string }, + wally_indices: { [string]: string }?, + overrides: { [string]: DependencySpecifier }?, + patches: { [string]: { [string]: string } }?, + place: { [string]: string }?, + dependencies: { [string]: DependencySpecifier }?, + peer_dependencies: { [string]: DependencySpecifier }?, + dev_dependencies: { [string]: DependencySpecifier }?, } & T return function(path: string?, _phantom: T): PesdeManifest - local manifestContents = fs.readFile(path or "pesde.toml") - local decoded = serde.decode("toml", manifestContents) + local manifestContents = fs.readFile(path or "pesde.toml") + local decoded = serde.decode("toml", manifestContents) - return decoded :: PesdeManifest + return decoded :: PesdeManifest end diff --git a/.lune/tests/init.luau b/.lune/tests/init.luau index 054ad32..42de320 100644 --- a/.lune/tests/init.luau +++ b/.lune/tests/init.luau @@ -10,69 +10,71 @@ local reporter = require("./reporter") -- A more proper solution would be to use luau.load instead, but -- frktest requires its global state to be modified by test suites local require = require :: ( - path: string + path: string ) -> ( - test: typeof(setmetatable( - {} :: { - case: (name: string, fn: () -> nil) -> (), - suite: (name: string, fn: () -> ()) -> (), - }, - { __index = frktest.test } - )) + test: typeof(setmetatable( + {} :: { + case: (name: string, fn: () -> nil) -> (), + suite: (name: string, fn: () -> ()) -> (), + }, + { __index = frktest.test } + )) ) -> () local function discoverTests(dir: string) - local tests = {} - for _, file in fs.readDir(dir) do - local fullPath = `{dir}/{file}` + local tests = {} + for _, file in fs.readDir(dir) do + local fullPath = `{dir}/{file}` - -- Look for files ending in `.spec.luau` as tests - if fs.isFile(fullPath) and string.sub(file, -10) == ".spec.luau" then - table.insert(tests, fullPath) - end + -- Look for files ending in `.spec.luau` as tests + if fs.isFile(fullPath) and string.sub(file, -10) == ".spec.luau" then + table.insert(tests, fullPath) + end - -- Recurse for directories - if fs.isDir(fullPath) then - local moreTests = discoverTests(fullPath) + -- Recurse for directories + if fs.isDir(fullPath) then + local moreTests = discoverTests(fullPath) - -- Why are the indices starting at 0???? What???? - table.move(moreTests, 0, #moreTests, #tests, tests) - end - end + -- Why are the indices starting at 0???? What???? + table.move(moreTests, 0, #moreTests, #tests, tests) + end + end - return tests + return tests end local allowedTests = process.args for _, test in discoverTests("src") do - -- If we are given any arguments, we only run those tests, otherwise, - -- we run all the tests + -- If we are given any arguments, we only run those tests, otherwise, + -- we run all the tests - -- So, to include only a certain set of test files, you can provide either - -- the full path of the test file or name of the test file, with or without - -- the `.spec.luau` extension - local baseName = string.match(test, "([^/\\]+)$") + -- So, to include only a certain set of test files, you can provide either + -- the full path of the test file or name of the test file, with or without + -- the `.spec.luau` extension + local baseName = string.match(test, "([^/\\]+)$") - local withoutExt = string.sub(test, 1, -11) - local baseNameWithoutExt = string.match(withoutExt, "([^/\\]+)$") + local withoutExt = string.sub(test, 1, -11) + local baseNameWithoutExt = string.match(withoutExt, "([^/\\]+)$") - local isAllowed = #process.args == 0 - or table.find(allowedTests, test) - or table.find(allowedTests, withoutExt) - or table.find(allowedTests, baseName) - or table.find(allowedTests, baseNameWithoutExt) + local isAllowed = #process.args == 0 + or table.find(allowedTests, test) + or table.find(allowedTests, withoutExt) + or table.find(allowedTests, baseName) + or table.find(allowedTests, baseNameWithoutExt) - local constructors = { - case = frktest.test.case, - suite = frktest.test.suite, - } + local constructors = { + case = frktest.test.case, + suite = frktest.test.suite, + } - if not isAllowed then - constructors.case = frktest.test.skip.case - constructors.suite = frktest.test.skip.suite - end + if not isAllowed then + constructors.case = frktest.test.skip.case + constructors.suite = frktest.test.skip.suite + end - require(`../../{test}`)(setmetatable(constructors, { __index = frktest.test })) + require(`../../{test}`)( + setmetatable(constructors, { __index = frktest.test }) + ) end reporter.init() diff --git a/.lune/tests/reporter.luau b/.lune/tests/reporter.luau index ea4160f..ac0cd82 100644 --- a/.lune/tests/reporter.luau +++ b/.lune/tests/reporter.luau @@ -6,54 +6,64 @@ local Reporter = frktest._reporters.lune_console_reporter local watch = require("../lib/channel") local STYLE = table.freeze({ - suite = function(name: string) - return `{stdio.style("bold")}{stdio.color("purple")}SUITE{stdio.style("reset")} {name}` - end, + suite = function(name: string) + return `{stdio.style("bold")}{stdio.color("purple")}SUITE{stdio.style( + "reset" + )} {name}` + end, - report = function(name: string, state: "success" | "error" | "skip", elapsed: number) - local state_color: stdio.Color = if state == "success" - then "green" - elseif state == "error" then "red" - elseif state == "skip" then "yellow" - else error("Invalid test state") - return ` {stdio.style("bold")}{stdio.color(state_color)}{if state == "skip" then "SKIP" else "TEST"}{stdio.style( - "reset" - )} {name} [{stdio.style("dim")}{string.format("%.2fms", elapsed)}{stdio.style("reset")}]` - end, + report = function( + name: string, + state: "success" | "error" | "skip", + elapsed: number + ) + local state_color: stdio.Color = if state == "success" + then "green" + elseif state == "error" then "red" + elseif state == "skip" then "yellow" + else error("Invalid test state") + return ` {stdio.style("bold")}{stdio.color(state_color)}{if state + == "skip" + then "SKIP" + else "TEST"}{stdio.style("reset")} {name} [{stdio.style("dim")}{string.format( + "%.2fms", + elapsed + )}{stdio.style("reset")}]` + end, }) local ReporterExt = {} function ReporterExt.init() - frktest.test.on_suite_enter(function(suite) - print(STYLE.suite(suite.name)) - end) + frktest.test.on_suite_enter(function(suite) + print(STYLE.suite(suite.name)) + end) - frktest.test.on_suite_leave(function() - stdio.write("\n") - end) + frktest.test.on_suite_leave(function() + stdio.write("\n") + end) - local send_ts, recv_ts = watch((nil :: any) :: number) + local send_ts, recv_ts = watch((nil :: any) :: number) - frktest.test.on_test_enter(function() - -- Send over some high precision timestamp when the test starts - return send_ts(os.clock()) - end) + frktest.test.on_test_enter(function() + -- Send over some high precision timestamp when the test starts + return send_ts(os.clock()) + end) - frktest.test.on_test_leave(function(test) - print(STYLE.report( - test.name, - if test.failed then "error" else "success", + frktest.test.on_test_leave(function(test) + print(STYLE.report( + test.name, + if test.failed then "error" else "success", - -- Await receival of the timestamp and convert the difference to ms - (os.clock() - recv_ts()) * 1000 - )) - end) + -- Await receival of the timestamp and convert the difference to ms + (os.clock() - recv_ts()) * 1000 + )) + end) - frktest.test.on_test_skipped(function(test) - print(STYLE.report(test.name, "skip", 0)) - end) + frktest.test.on_test_skipped(function(test) + print(STYLE.report(test.name, "skip", 0)) + end) - Reporter.init() + Reporter.init() end return setmetatable(ReporterExt, { __index = Reporter }) diff --git a/.lune/typecheck.luau b/.lune/typecheck.luau index bb7a176..3ee89d9 100644 --- a/.lune/typecheck.luau +++ b/.lune/typecheck.luau @@ -5,12 +5,12 @@ local process = require("@lune/process") local CommandBuilder = require("./lib/exec") process.exit( - CommandBuilder.new("luau-lsp") - :withArg("analyze") - :withArgs({ "--settings", ".vscode/settings.json" }) - :withArgs({ "--ignore", "'**/.pesde/**'" }) - :withArgs({ "--ignore", "'./test-files/**'" }) - :withArg(".") - :withStdioStrategy("forward") - :exec().code + CommandBuilder.new("luau-lsp") + :withArg("analyze") + :withArgs({ "--settings", ".vscode/settings.json" }) + :withArgs({ "--ignore", "'**/.pesde/**'" }) + :withArgs({ "--ignore", "'./test-files/**'" }) + :withArg(".") + :withStdioStrategy("forward") + :exec().code ) diff --git a/src/generators/rojo/sourcemap.luau b/src/generators/rojo/sourcemap.luau index 4402d9f..7404cad 100644 --- a/src/generators/rojo/sourcemap.luau +++ b/src/generators/rojo/sourcemap.luau @@ -7,20 +7,24 @@ local PLATFORM_SEP = if process.os == "windows" then "\\" else "/" -- A mapping of things to do depending on the file present local PATH_ACTION_MAP: { [string]: (dir: string) -> number? } = { - ["default.project.json"] = function(dir) - return process.spawn("rojo", { "sourcemap", dir }, { - cwd = process.cwd, - env = process.env, - stdio = "forward", - }).code - end, + ["default.project.json"] = function(dir) + return process.spawn("rojo", { "sourcemap", dir }, { + cwd = process.cwd, + env = process.env, + stdio = "forward", + }).code + end, - ["init.lua"] = function() - return stdio.write(serde.encode("json", { filePaths = { "init.lua" } }, false)) - end, - ["init.luau"] = function() - return stdio.write(serde.encode("json", { filePaths = { "init.luau" } }, false)) - end, + ["init.lua"] = function() + return stdio.write( + serde.encode("json", { filePaths = { "init.lua" } }, false) + ) + end, + ["init.luau"] = function() + return stdio.write( + serde.encode("json", { filePaths = { "init.luau" } }, false) + ) + end, } --- Writes a Rojo sourcemap for the project in the provided directory or the current @@ -31,18 +35,18 @@ local PATH_ACTION_MAP: { [string]: (dir: string) -> number? } = { --- * Failure to spawn `rojo` command --- * Any I/O error occurs return function(packageDirectory: string?): boolean - packageDirectory = packageDirectory or process.cwd + packageDirectory = packageDirectory or process.cwd - -- We go through the action mappings in order of priority and check for the - -- file predicates, if present, we execute the action and report our status - for path, action in PATH_ACTION_MAP do - if fs.isFile(`{packageDirectory}{PLATFORM_SEP}{path}`) then - local status = action(packageDirectory) - return if status ~= nil then status == 0 else true - end - end + -- We go through the action mappings in order of priority and check for the + -- file predicates, if present, we execute the action and report our status + for path, action in PATH_ACTION_MAP do + if fs.isFile(`{packageDirectory}{PLATFORM_SEP}{path}`) then + local status = action(packageDirectory) + return if status ~= nil then status == 0 else true + end + end - -- If we reached so far, that must mean none of the file predicates matched, - -- so we return a `false` signifying an error - return false + -- If we reached so far, that must mean none of the file predicates matched, + -- so we return a `false` signifying an error + return false end diff --git a/src/generators/rojo/sourcemap.spec.luau b/src/generators/rojo/sourcemap.spec.luau index bc2bb5f..7f7b497 100644 --- a/src/generators/rojo/sourcemap.spec.luau +++ b/src/generators/rojo/sourcemap.spec.luau @@ -9,79 +9,98 @@ local check = frktest.assert.check local TEST_PROJECTS_DIR = "./test-files/rojo/test-projects" local TEST_PROJECT_EXCLUDES = { - "json_model", - "bad_json_model", - "plugins", - "legacy-0.5.x-unknown-names", + "json_model", + "bad_json_model", + "plugins", + "legacy-0.5.x-unknown-names", } local BUILTIN_PATCHES: { - [string]: { [string]: (...any) -> ...any? }, + [string]: { [string]: (...any) -> ...any? }, } = { - ["@lune/process"] = { - spawn = function(program: string, params: { string }, options: process.SpawnOptions): process.SpawnResult - local patchedOptions: process.SpawnOptions = options or {} - patchedOptions.stdio = "default" + ["@lune/process"] = { + spawn = function( + program: string, + params: { string }, + options: process.SpawnOptions + ): process.SpawnResult + local patchedOptions: process.SpawnOptions = options or {} + patchedOptions.stdio = "default" - local result = process.spawn(program, params, patchedOptions) + local result = process.spawn(program, params, patchedOptions) - -- First we make sure the command exited properly - assert(result.ok, `Expected \`rojo\` command to not error, got:\n{string.rep(" ", 6)}{result.stderr}`) + -- First we make sure the command exited properly + assert( + result.ok, + `Expected \`rojo\` command to not error, got:\n{string.rep( + " ", + 6 + )}{result.stderr}` + ) - -- We also make sure that the output JSON was valid - serde.decode("json", result.stdout) + -- We also make sure that the output JSON was valid + serde.decode("json", result.stdout) - return result - end, - }, + return result + end, + }, - ["@lune/stdio"] = { - write = function(msg: string) - -- Only make sure output JSON is valid - serde.decode("json", msg) - end, - }, + ["@lune/stdio"] = { + write = function(msg: string) + -- Only make sure output JSON is valid + serde.decode("json", msg) + end, + }, } local function requireWithPatches(path: string) - for builtin, patch in BUILTIN_PATCHES do - if path == builtin then - return setmetatable(patch, { __index = require(builtin) }) - end - end + for builtin, patch in BUILTIN_PATCHES do + if path == builtin then + return setmetatable(patch, { __index = require(builtin) }) + end + end - return require(path) + return require(path) end return function(test: typeof(frktest.test)) - test.suite("Generates Rojo sourcemaps for test projects successfully", function() - for _, file in fs.readDir(TEST_PROJECTS_DIR) do - if table.find(TEST_PROJECT_EXCLUDES, file) then - -- It does not make sense to test sourcemap generation for some of the test projects - continue - end + test.suite( + "Generates Rojo sourcemaps for test projects successfully", + function() + for _, file in fs.readDir(TEST_PROJECTS_DIR) do + if table.find(TEST_PROJECT_EXCLUDES, file) then + -- It does not make sense to test sourcemap generation for some of the test projects + continue + end - local testProject = `{TEST_PROJECTS_DIR}/{file}` - test.case(file, function() - -- If a file starts with `bad_` but not `bad_meta_`, we should expect a failure - -- Also, sorry about this shitty regex, regex-rs does not support look-around :( - local isBadMeta = regex.new("^bad_[^m]|^bad_m[^e]|^bad_me[^t]|^bad_met[^a]") - local expect = if isBadMeta:isMatch(file) then check.should_error else check.should_not_error + local testProject = `{TEST_PROJECTS_DIR}/{file}` + test.case(file, function() + -- If a file starts with `bad_` but not `bad_meta_`, we should expect a failure + -- Also, sorry about this shitty regex, regex-rs does not support look-around :( + local isBadMeta = regex.new( + "^bad_[^m]|^bad_m[^e]|^bad_me[^t]|^bad_met[^a]" + ) + local expect = if isBadMeta:isMatch(file) + then check.should_error + else check.should_not_error - expect(function() - -- We override the require which applies some patches to some builtins, mainly preventing - -- command stdout forwarding and `stdio.write` outputs to stdout in order to not fill - -- test ouput with large amounts of JSON data - local sourcemap: (string) -> boolean = - luau.load(fs.readFile("./src/generators/rojo/sourcemap.luau"), { - environment = { - require = requireWithPatches, - }, - })() + expect(function() + -- We override the require which applies some patches to some builtins, mainly preventing + -- command stdout forwarding and `stdio.write` outputs to stdout in order to not fill + -- test ouput with large amounts of JSON data + local sourcemap: (string) -> boolean = luau.load( + fs.readFile("./src/generators/rojo/sourcemap.luau"), + { + environment = { + require = requireWithPatches, + }, + } + )() - return check.is_true(sourcemap(testProject)) - end) - end) - end - end) + return check.is_true(sourcemap(testProject)) + end) + end) + end + end + ) end diff --git a/src/generators/rojo/sync_config.luau b/src/generators/rojo/sync_config.luau index 7b06134..2048bba 100644 --- a/src/generators/rojo/sync_config.luau +++ b/src/generators/rojo/sync_config.luau @@ -3,49 +3,49 @@ local process = require("@lune/process") local serde = require("@lune/serde") export type TreeProperties = { - Name: never?, - Parent: never?, + Name: never?, + Parent: never?, } export type TreeBase = { - ["$className"]: string?, - ["$ignoreUnknownInstances"]: boolean?, - ["$path"]: string | { optional: string }?, - ["$properties"]: TreeProperties?, + ["$className"]: string?, + ["$ignoreUnknownInstances"]: boolean?, + ["$path"]: string | { optional: string }?, + ["$properties"]: TreeProperties?, } export type TreeNormal = TreeBase & { - [string]: TreeNormal, + [string]: TreeNormal, } & ({ ["$className"]: string } | { - ["$path"]: string | { optional: string }, + ["$path"]: string | { optional: string }, }) export type TreeService = TreeBase & { - [string]: TreeNormal, + [string]: TreeNormal, } export type DataModelTree = TreeBase & { - StarterPlayer: (TreeBase & { - StarterPlayerScripts: TreeService?, - StarterCharacterScripts: TreeService?, - [string]: TreeNormal, - })?, - [string]: TreeService, + StarterPlayer: (TreeBase & { + StarterPlayerScripts: TreeService?, + StarterCharacterScripts: TreeService?, + [string]: TreeNormal, + })?, + [string]: TreeService, } export type Tree = (DataModelTree & { - ["$className"]: "DataModel", + ["$className"]: "DataModel", }) | TreeNormal export type SyncConfig = { - name: string, - servePort: number?, - servePlaceIds: { number }?, - placeId: number?, - gameId: number?, - serveAddress: string?, - globIgnorePaths: { string }?, - tree: Tree, + name: string, + servePort: number?, + servePlaceIds: { number }?, + placeId: number?, + gameId: number?, + serveAddress: string?, + globIgnorePaths: { string }?, + tree: Tree, } local PLATFORM_SEP = if process.os == "windows" then "\\" else "/" @@ -57,57 +57,59 @@ local PLATFORM_SEP = if process.os == "windows" then "\\" else "/" --- * The current process lacks permissions to a file --- * Any I/O error occurs return function( - packageDirectory: string?, - files: { string }, - options: { - writeToFile: boolean?, - force: boolean?, - } + packageDirectory: string?, + files: { string }, + options: { + writeToFile: boolean?, + force: boolean?, + } ): (boolean, string?) - packageDirectory = packageDirectory or process.cwd - local syncConfigPath = `{packageDirectory}{PLATFORM_SEP}default.project.json` - if fs.isFile(syncConfigPath) and not options.force then - return true, nil - end + packageDirectory = packageDirectory or process.cwd + local syncConfigPath = + `{packageDirectory}{PLATFORM_SEP}default.project.json` + if fs.isFile(syncConfigPath) and not options.force then + return true, nil + end - local syncConfigTree = {} :: Tree + local syncConfigTree = {} :: Tree - for _, file in files do - -- Remove the `.lua` or `.luau` file extension from the file name - local name = string.gsub(file, ".luau?$", "") + for _, file in files do + -- Remove the `.lua` or `.luau` file extension from the file name + local name = string.gsub(file, ".luau?$", "") - if name == "init" then - syncConfigTree["$path"] = name - continue - end + if name == "init" then + syncConfigTree["$path"] = name + continue + end - syncConfigTree[name] = { - ["$path"] = file, - } - end + syncConfigTree[name] = { + ["$path"] = file, + } + end - -- If there isn't a top level path, we mark the entire thing as a Folder - if not syncConfigTree["$path"] then - syncConfigTree["$className"] = "Folder" - end + -- If there isn't a top level path, we mark the entire thing as a Folder + if not syncConfigTree["$path"] then + syncConfigTree["$className"] = "Folder" + end - -- If the config tree does not include pesde's downloaded roblox dependencies - -- directory, we add it as an optional one for the future, once dependencies - -- are installed - if not syncConfigTree["roblox_packages"] then - syncConfigTree["roblox_packages"] = { - ["$path"] = { - optional = "roblox_packages", - }, - } - end + -- If the config tree does not include pesde's downloaded roblox dependencies + -- directory, we add it as an optional one for the future, once dependencies + -- are installed + if not syncConfigTree["roblox_packages"] then + syncConfigTree["roblox_packages"] = { + ["$path"] = { + optional = "roblox_packages", + }, + } + end - -- Finally, we serialize the config to a JSON string and optionally write it - -- to the sync config path - local serializedConfig = serde.encode("json", { tree = syncConfigTree }, true) - if options.writeToFile then - fs.writeFile(syncConfigPath, serializedConfig) - end + -- Finally, we serialize the config to a JSON string and optionally write it + -- to the sync config path + local serializedConfig = + serde.encode("json", { tree = syncConfigTree }, true) + if options.writeToFile then + fs.writeFile(syncConfigPath, serializedConfig) + end - return true, serializedConfig + return true, serializedConfig end diff --git a/src/generators/rojo/sync_config.spec.luau b/src/generators/rojo/sync_config.spec.luau index 02a5844..014d247 100644 --- a/src/generators/rojo/sync_config.spec.luau +++ b/src/generators/rojo/sync_config.spec.luau @@ -7,27 +7,34 @@ local check = frktest.assert.check local syncConfig = require("./sync_config") local TEST_PROJECTS_DIRS = { - "./test-files/rojo/test-projects", - "./test-files/rojo/rojo-test/serve-tests", + "./test-files/rojo/test-projects", + "./test-files/rojo/rojo-test/serve-tests", } return function(test: typeof(frktest.test)) - test.suite("Generates Rojo valid sync configs", function() - for _, dir in TEST_PROJECTS_DIRS do - for _, file in fs.readDir(dir) do - local fullPath = `{dir}/{file}` - test.case(`{file}`, function() - local ok, config = syncConfig(fullPath, fs.readDir(fullPath), { writeToFile = false, force = true }) - check.is_true(ok) + test.suite("Generates Rojo valid sync configs", function() + for _, dir in TEST_PROJECTS_DIRS do + for _, file in fs.readDir(dir) do + local fullPath = `{dir}/{file}` + test.case(`{file}`, function() + local ok, config = syncConfig( + fullPath, + fs.readDir(fullPath), + { writeToFile = false, force = true } + ) + check.is_true(ok) - -- Make sure that the generated config and the real configs are similar - local generatedConfig, realConfig = - serde.decode("json", config), - serde.decode("json", fs.readFile(`{fullPath}/default.project.json`)) + -- Make sure that the generated config and the real configs are similar + local generatedConfig, realConfig = + serde.decode("json", config), + serde.decode( + "json", + fs.readFile(`{fullPath}/default.project.json`) + ) - check.table.contains(realConfig, generatedConfig) - end) - end - end - end) + check.table.contains(realConfig, generatedConfig) + end) + end + end + end) end diff --git a/src/init.luau b/src/init.luau index cf48aa9..f0d77f3 100644 --- a/src/init.luau +++ b/src/init.luau @@ -1,8 +1,8 @@ return { - generators = { - rojo = { - sourcemap = require("./generators/rojo/sourcemap"), - syncConfig = require("./generators/rojo/sync_config"), - }, - }, + generators = { + rojo = { + sourcemap = require("./generators/rojo/sourcemap"), + syncConfig = require("./generators/rojo/sync_config"), + }, + }, }