mirror of
https://github.com/pesde-pkg/scripts.git
synced 2024-12-12 07:00:35 +00:00
style: apply stylua formatter
This commit is contained in:
parent
e907258396
commit
ef8953fdcc
13 changed files with 625 additions and 519 deletions
259
.lune/build.luau
259
.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 <contact@daimond113.com> (https://www.daimond113.com/)",
|
||||
"Erica Marigold <hi@devcomp.xyz>",
|
||||
},
|
||||
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 <contact@daimond113.com> (https://www.daimond113.com/)",
|
||||
"Erica Marigold <hi@devcomp.xyz>",
|
||||
},
|
||||
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
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -11,38 +11,38 @@
|
|||
--- end)
|
||||
--- ```
|
||||
type Watch<T> = {
|
||||
value: T?,
|
||||
receivers: { thread },
|
||||
value: T?,
|
||||
receivers: { thread },
|
||||
}
|
||||
|
||||
--- Creates a new `Watch` channel, returning its send and receive handles.
|
||||
local function chan<T>(_phantom: T): ((T) -> (), () -> T?)
|
||||
local watch: Watch<T> = {
|
||||
value = nil,
|
||||
receivers = {},
|
||||
}
|
||||
local watch: Watch<T> = {
|
||||
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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<T = {}> = {
|
||||
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<T>(path: string?, _phantom: T): PesdeManifest<T>
|
||||
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<T>
|
||||
return decoded :: PesdeManifest<T>
|
||||
end
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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 })
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue