mirror of
https://github.com/littensy/charm.git
synced 2026-10-20 17:51:37 +00:00
chore: housekeeping
This commit is contained in:
parent
4931515a95
commit
4edc952c05
77 changed files with 230 additions and 602 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -1,9 +1,9 @@
|
|||
/.vscode
|
||||
/.pesde
|
||||
/build
|
||||
/DevPackages
|
||||
/pkgs/**/wally.lock
|
||||
/packages/**/wally.lock
|
||||
/sourcemap.json
|
||||
node_modules
|
||||
*.tsbuildinfo
|
||||
*.rbxm
|
||||
.DS_Store
|
||||
|
|
|
|||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
[submodule "test/suite"]
|
||||
path = test/suite
|
||||
url = https://gist.github.com/littensy/fa9afcc5d66d25e86538634982be4ee4
|
||||
2
.luaurc
2
.luaurc
|
|
@ -5,6 +5,6 @@
|
|||
},
|
||||
"aliases": {
|
||||
"test": "test",
|
||||
"pkgs": "pkgs"
|
||||
"packages": "packages"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
1
.npmrc
Normal file
1
.npmrc
Normal file
|
|
@ -0,0 +1 @@
|
|||
public-hoist-pattern[]=*@rbxts*
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
local Charm = require("@pkgs/charm/src/lib")
|
||||
local Charm = require("@packages/charm/src")
|
||||
local bench = require("./bench")
|
||||
|
||||
local atom = Charm.atom
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
local Charm = require("@pkgs/charm/src/lib")
|
||||
local Charm = require("@packages/charm/src")
|
||||
local bench = require("./bench")
|
||||
|
||||
local signals = Charm.signals
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
mkdir build
|
||||
rojo build pkgs/charm -o build/charm.rbxm
|
||||
rojo build pkgs/charm-sync -o build/charm-sync.rbxm
|
||||
rojo build pkgs/react-charm -o build/react-charm.rbxm
|
||||
rojo build pkgs/vide-charm -o build/vide-charm.rbxm
|
||||
rojo build packages/charm -o build/charm.rbxm
|
||||
rojo build packages/charm-sync -o build/charm-sync.rbxm
|
||||
rojo build packages/react-charm -o build/react-charm.rbxm
|
||||
rojo build packages/vide-charm -o build/vide-charm.rbxm
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ luau-lsp analyze \
|
|||
--defs=bin/roblox.d.luau \
|
||||
--flag:LuauFixIndexerSubtypingOrdering=true \
|
||||
--sourcemap=sourcemap.json \
|
||||
--ignore="**/_Index/**" \
|
||||
pkgs test
|
||||
--ignore="**/node_modules/**" \
|
||||
packages test
|
||||
|
||||
selene pkgs test
|
||||
selene packages test
|
||||
|
||||
stylua --check pkgs test
|
||||
stylua --check packages test
|
||||
|
||||
rm bin/roblox.d.luau
|
||||
|
|
|
|||
|
|
@ -5,21 +5,22 @@
|
|||
"ReplicatedStorage": {
|
||||
"$className": "ReplicatedStorage",
|
||||
"Packages": {
|
||||
"$path": "DevPackages",
|
||||
"Charm": { "$path": "pkgs/charm" },
|
||||
"CharmSync": { "$path": "pkgs/charm-sync" },
|
||||
"ReactCharm": { "$path": "pkgs/react-charm" },
|
||||
"VideCharm": { "$path": "pkgs/vide-charm" }
|
||||
"$path": "node_modules/@rbxts-js",
|
||||
"Vide": { "$path": "node_modules/@rbxts/vide/src" },
|
||||
"Charm": { "$path": "packages/charm" },
|
||||
"CharmSync": { "$path": "packages/charm-sync" },
|
||||
"ReactCharm": { "$path": "packages/react-charm" },
|
||||
"VideCharm": { "$path": "packages/vide-charm" }
|
||||
}
|
||||
},
|
||||
"ServerScriptService": {
|
||||
"$className": "ServerScriptService",
|
||||
"Tests": {
|
||||
"$path": "test",
|
||||
"Charm": { "$path": "pkgs/charm/tests" },
|
||||
"CharmSync": { "$path": "pkgs/charm-sync/tests" },
|
||||
"ReactCharm": { "$path": "pkgs/react-charm/tests" },
|
||||
"VideCharm": { "$path": "pkgs/vide-charm/tests" }
|
||||
"Charm": { "$path": "packages/charm/tests" },
|
||||
"CharmSync": { "$path": "packages/charm-sync/tests" },
|
||||
"ReactCharm": { "$path": "packages/react-charm/tests" },
|
||||
"VideCharm": { "$path": "packages/vide-charm/tests" }
|
||||
},
|
||||
"Benchmarks": {
|
||||
"$path": "benches"
|
||||
|
|
|
|||
14
package.json
Normal file
14
package.json
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"test": "lute test/init.luau",
|
||||
"build": "sh bin/build.sh",
|
||||
"typecheck": "sh bin/check.sh"
|
||||
},
|
||||
"packageManager": "pnpm@10.11.0",
|
||||
"devDependencies": {
|
||||
"@rbxts-js/react": "17.1.20-ts.1",
|
||||
"@rbxts-js/react-roblox": "17.1.20-ts.1",
|
||||
"@rbxts/vide": "^0.5.7"
|
||||
}
|
||||
}
|
||||
|
|
@ -7,7 +7,7 @@ local endBatch = signals.endBatch
|
|||
|
||||
local function handleBatched(ok: boolean, ...)
|
||||
endBatch()
|
||||
if not ok then
|
||||
if ok then
|
||||
return ...
|
||||
else
|
||||
error(...)
|
||||
|
|
@ -1,14 +1,14 @@
|
|||
local atom = require("./atom")
|
||||
local batched = require("./batched")
|
||||
local effect = require("./effect")
|
||||
local listen = require("./listen")
|
||||
local mapped = require("./mapped")
|
||||
local observe = require("./observe")
|
||||
local onCleanup = require("./onCleanup")
|
||||
local signals = require("./signals")
|
||||
local subscribe = require("./subscribe")
|
||||
local types = require("./types")
|
||||
local untracked = require("./untracked")
|
||||
local atom = require("@self/atom")
|
||||
local batched = require("@self/batched")
|
||||
local effect = require("@self/effect")
|
||||
local listen = require("@self/listen")
|
||||
local mapped = require("@self/mapped")
|
||||
local observe = require("@self/observe")
|
||||
local onCleanup = require("@self/onCleanup")
|
||||
local signals = require("@self/signals")
|
||||
local subscribe = require("@self/subscribe")
|
||||
local types = require("@self/types")
|
||||
local untracked = require("@self/untracked")
|
||||
|
||||
export type Atom<T = any> = types.Atom<T>
|
||||
export type Getter<T = any> = types.Getter<T>
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
local Charm = require("../src/lib")
|
||||
local Charm = require("../src")
|
||||
local suite = require("@test/suite")
|
||||
|
||||
local atom = Charm.atom
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
local Charm = require("../src/lib")
|
||||
local Charm = require("../src")
|
||||
local suite = require("@test/suite")
|
||||
|
||||
local atom = Charm.atom
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
local Charm = require("../src/lib")
|
||||
local Charm = require("../src")
|
||||
local suite = require("@test/suite")
|
||||
|
||||
local atom = Charm.atom
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
local Charm = require("../src/lib")
|
||||
local Charm = require("../src")
|
||||
local suite = require("@test/suite")
|
||||
|
||||
type Atom<T> = Charm.Atom<T>
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
local Charm = require("../src/lib")
|
||||
local Charm = require("../src")
|
||||
local suite = require("@test/suite")
|
||||
|
||||
local atom = Charm.atom
|
||||
|
|
@ -5,6 +5,7 @@ local effect = Charm.effect
|
|||
local useBinding = React.useBinding
|
||||
local useEffect = React.useEffect
|
||||
local useState = React.useState
|
||||
local useMemo = React.useMemo
|
||||
|
||||
--- A hook that subscribes to changes in the given atom or selector. The
|
||||
--- component is re-rendered whenever the state changes.
|
||||
|
|
@ -39,7 +40,8 @@ end
|
|||
--- @param dependencies An array of values that the subscription depends on.
|
||||
--- @return A binding that stores the current state.
|
||||
local function useAtomBinding<State>(selector: () -> State, dependencies: { any }?)
|
||||
local binding, setBinding = useBinding(selector)
|
||||
local initialState = useMemo(selector, {})
|
||||
local binding, setBinding = useBinding(initialState)
|
||||
|
||||
useEffect(function()
|
||||
return effect(function()
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
local lib = require(script.lib)
|
||||
|
||||
export type Atom<T = any> = lib.Atom<T>
|
||||
export type Getter<T = any> = lib.Getter<T>
|
||||
export type Selector<T = any> = lib.Selector<T>
|
||||
export type Setter<T = any> = lib.Setter<T>
|
||||
export type Update<T = any> = lib.Update<T>
|
||||
|
||||
return lib
|
||||
164
pnpm-lock.yaml
generated
Normal file
164
pnpm-lock.yaml
generated
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
lockfileVersion: '9.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
devDependencies:
|
||||
'@rbxts-js/react':
|
||||
specifier: 17.1.20-ts.1
|
||||
version: 17.1.20-ts.1
|
||||
'@rbxts-js/react-roblox':
|
||||
specifier: 17.1.20-ts.1
|
||||
version: 17.1.20-ts.1
|
||||
'@rbxts/vide':
|
||||
specifier: ^0.5.7
|
||||
version: 0.5.7
|
||||
|
||||
packages:
|
||||
|
||||
'@rbxts-js/boolean@1.2.3-ts.1':
|
||||
resolution: {integrity: sha512-IlYFG9uatXQKXOLR1mdptVeEcxudwBCierO+Vu6VZRRaI+/+JDxUClm+QFZyce/kitwnDTmtNvhqVAUtFvCU0Q==}
|
||||
|
||||
'@rbxts-js/collections@1.2.3-ts.1':
|
||||
resolution: {integrity: sha512-RYT73QyEdsV+DCQMtyzT2bZ1P12j2b+JSHVdb1x6xrDq/KdVEkk/QAzW+o22vPrLXbHa6/jA5bmVlgVcOOrIjA==}
|
||||
|
||||
'@rbxts-js/console@1.2.3-ts.1':
|
||||
resolution: {integrity: sha512-qacyJE7bYf4n9448F7mn9e8Y5qljZb7QkSmsmnbQT2HA9Mecvg3gqc9RwCIdVkBV6o1AGVAHbatNQ3a39zPjaw==}
|
||||
|
||||
'@rbxts-js/es7-types@1.2.3-ts.1':
|
||||
resolution: {integrity: sha512-Z7NitBQp6tyzsr5eKlSX54rmopKXpzGu33lYikjF37llrnGFBpnRcZgCJwHKYciloRmjUtnR/UohUNwbWyG9Zg==}
|
||||
|
||||
'@rbxts-js/instance-of@1.2.3-ts.1':
|
||||
resolution: {integrity: sha512-3fKvDPvxHuEZALNBP2XdbXfzjEfbgsXAheCm1Jf5kC2r+HgszW+yqg4zY6TjW0pcsJM2JjH+fM52oomSNu3LLQ==}
|
||||
|
||||
'@rbxts-js/luau-polyfill@1.2.3-ts.1':
|
||||
resolution: {integrity: sha512-2asgvWrOhf311rNgEM/fxMO3+xOVI63Cz1K1PobyZVtDdd87ZJ6RGiZSFl1EAeU77SZc8280toJDVs9YSwxWnA==}
|
||||
|
||||
'@rbxts-js/math@1.2.3-ts.1':
|
||||
resolution: {integrity: sha512-mF7IMJJyxYE02cRNWJ1wsq4pPaAEXKX+CbGlmI0G3QBgWVHj+Oi02IR6pssyUyE6Y2BKvgvP3dcJuTO51OX25A==}
|
||||
|
||||
'@rbxts-js/number@1.2.3-ts.1':
|
||||
resolution: {integrity: sha512-tPdt2s7fEBSxImVFBUvuSMtBKIvaZUCtlOjBl58Y0+GKB15VPXFIRW/5biVruXPnv9d4sDE3mh986ZxxN4UQ5w==}
|
||||
|
||||
'@rbxts-js/react-reconciler@17.1.20-ts.1':
|
||||
resolution: {integrity: sha512-t4vY4nHeM+Scrs+9FbgNsuhK0QR6lg3SNWUhjbzvptNP+IA4+4F8TkHQghR2SSTxQDhegU02BTR7NBpk+AQcUw==}
|
||||
|
||||
'@rbxts-js/react-roblox@17.1.20-ts.1':
|
||||
resolution: {integrity: sha512-Jv0mq+9txXptgsJ6MDWHgCTwsohaon9HhmllkUe29zkaMwE+di4cHqzF1tgV8VHYGG/LQoitXDddK03CA3L+2g==}
|
||||
|
||||
'@rbxts-js/react@17.1.20-ts.1':
|
||||
resolution: {integrity: sha512-u/dNRTNj5AaVPuqJLza7FEJ24lyRfJ0CdjflbIS8JiUS1iXiO7yZeHQWwPk6V3oHuAOckv6tA4FN/Eju2rPMrQ==}
|
||||
|
||||
'@rbxts-js/roblox-lua-promise@3.5.0-ts.2':
|
||||
resolution: {integrity: sha512-H8fxKVJkRnM60yTZ/riO4X43GZD/MViFm+ftCzzAALkAhE4vSaBDNv9E7IUjZZoWi21bG6vZ8mGIiYfavaljvw==}
|
||||
|
||||
'@rbxts-js/safe-flags@17.1.20-ts.1':
|
||||
resolution: {integrity: sha512-k+16jmCP63LT8sNfhLoytUrp3UB+CGHH7s54BFUAHte+zCANK3wfAKXbpT36NqnUfRqgKDM8grO4lWp6awuacQ==}
|
||||
|
||||
'@rbxts-js/scheduler@17.1.20-ts.1':
|
||||
resolution: {integrity: sha512-RoXs6bAW3LIB6HL/voMilR2hjcGAtutaVsr0yumu0hUYVFY2FwIkbOVepsdYWDrYhVqsk5zrj8CcVGgUxDldGg==}
|
||||
|
||||
'@rbxts-js/shared@17.1.20-ts.1':
|
||||
resolution: {integrity: sha512-/3K1BnWV519+any6JMWniIDx1rtq20H6RVgJStSCj4XofrM63kzju0EzRQg1af4lnXCfZNZBusRW8IGQ14xuZQ==}
|
||||
|
||||
'@rbxts-js/string@1.2.3-ts.1':
|
||||
resolution: {integrity: sha512-Je/WKgZbXF/sAHEsQPuns5n4rWzux327zjr0KRf9Gy62ieUzl2FRZZHjVYce3xdMtHif2JstIf/J3p513TUtRg==}
|
||||
|
||||
'@rbxts-js/symbol-luau@1.0.0-ts.1':
|
||||
resolution: {integrity: sha512-TQS/ImDCK5KPGvbmyScH5EZS0ELSCDvZEFR3W+ItPlsLl+bDRSI/0uaKtJ7rbwXgdGwBDKM1QXFPjqe+KOphIA==}
|
||||
|
||||
'@rbxts-js/timers@1.2.3-ts.1':
|
||||
resolution: {integrity: sha512-HKP4ymFX3QLWbR/2Gy4LXwvkhjC9OzbwZ5bNunxu+M8Ocbi1cjRduiZK/y5ogLr9cNIfSoxs6e1DQ3soc8/hwA==}
|
||||
|
||||
'@rbxts/vide@0.5.7':
|
||||
resolution: {integrity: sha512-Q9R7ybzuHK1phtLicBHgAjQnlJ/dMsXDs2k296PIMLgLqK9aX/f1skOP7F1K/vo7IrSE0jCG3B3XTOtQL1Sb0Q==}
|
||||
|
||||
snapshots:
|
||||
|
||||
'@rbxts-js/boolean@1.2.3-ts.1':
|
||||
dependencies:
|
||||
'@rbxts-js/collections': 1.2.3-ts.1
|
||||
|
||||
'@rbxts-js/collections@1.2.3-ts.1':
|
||||
dependencies:
|
||||
'@rbxts-js/es7-types': 1.2.3-ts.1
|
||||
'@rbxts-js/instance-of': 1.2.3-ts.1
|
||||
|
||||
'@rbxts-js/console@1.2.3-ts.1':
|
||||
dependencies:
|
||||
'@rbxts-js/collections': 1.2.3-ts.1
|
||||
|
||||
'@rbxts-js/es7-types@1.2.3-ts.1': {}
|
||||
|
||||
'@rbxts-js/instance-of@1.2.3-ts.1': {}
|
||||
|
||||
'@rbxts-js/luau-polyfill@1.2.3-ts.1':
|
||||
dependencies:
|
||||
'@rbxts-js/boolean': 1.2.3-ts.1
|
||||
'@rbxts-js/collections': 1.2.3-ts.1
|
||||
'@rbxts-js/console': 1.2.3-ts.1
|
||||
'@rbxts-js/es7-types': 1.2.3-ts.1
|
||||
'@rbxts-js/instance-of': 1.2.3-ts.1
|
||||
'@rbxts-js/math': 1.2.3-ts.1
|
||||
'@rbxts-js/number': 1.2.3-ts.1
|
||||
'@rbxts-js/string': 1.2.3-ts.1
|
||||
'@rbxts-js/symbol-luau': 1.0.0-ts.1
|
||||
'@rbxts-js/timers': 1.2.3-ts.1
|
||||
|
||||
'@rbxts-js/math@1.2.3-ts.1': {}
|
||||
|
||||
'@rbxts-js/number@1.2.3-ts.1': {}
|
||||
|
||||
'@rbxts-js/react-reconciler@17.1.20-ts.1':
|
||||
dependencies:
|
||||
'@rbxts-js/luau-polyfill': 1.2.3-ts.1
|
||||
'@rbxts-js/react': 17.1.20-ts.1
|
||||
'@rbxts-js/roblox-lua-promise': 3.5.0-ts.2
|
||||
'@rbxts-js/safe-flags': 17.1.20-ts.1
|
||||
'@rbxts-js/scheduler': 17.1.20-ts.1
|
||||
'@rbxts-js/shared': 17.1.20-ts.1
|
||||
|
||||
'@rbxts-js/react-roblox@17.1.20-ts.1':
|
||||
dependencies:
|
||||
'@rbxts-js/luau-polyfill': 1.2.3-ts.1
|
||||
'@rbxts-js/react': 17.1.20-ts.1
|
||||
'@rbxts-js/react-reconciler': 17.1.20-ts.1
|
||||
'@rbxts-js/scheduler': 17.1.20-ts.1
|
||||
'@rbxts-js/shared': 17.1.20-ts.1
|
||||
|
||||
'@rbxts-js/react@17.1.20-ts.1':
|
||||
dependencies:
|
||||
'@rbxts-js/luau-polyfill': 1.2.3-ts.1
|
||||
'@rbxts-js/shared': 17.1.20-ts.1
|
||||
|
||||
'@rbxts-js/roblox-lua-promise@3.5.0-ts.2': {}
|
||||
|
||||
'@rbxts-js/safe-flags@17.1.20-ts.1': {}
|
||||
|
||||
'@rbxts-js/scheduler@17.1.20-ts.1':
|
||||
dependencies:
|
||||
'@rbxts-js/luau-polyfill': 1.2.3-ts.1
|
||||
'@rbxts-js/safe-flags': 17.1.20-ts.1
|
||||
'@rbxts-js/shared': 17.1.20-ts.1
|
||||
|
||||
'@rbxts-js/shared@17.1.20-ts.1':
|
||||
dependencies:
|
||||
'@rbxts-js/luau-polyfill': 1.2.3-ts.1
|
||||
'@rbxts-js/safe-flags': 17.1.20-ts.1
|
||||
|
||||
'@rbxts-js/string@1.2.3-ts.1':
|
||||
dependencies:
|
||||
'@rbxts-js/es7-types': 1.2.3-ts.1
|
||||
'@rbxts-js/number': 1.2.3-ts.1
|
||||
|
||||
'@rbxts-js/symbol-luau@1.0.0-ts.1': {}
|
||||
|
||||
'@rbxts-js/timers@1.2.3-ts.1':
|
||||
dependencies:
|
||||
'@rbxts-js/collections': 1.2.3-ts.1
|
||||
|
||||
'@rbxts/vide@0.5.7': {}
|
||||
|
|
@ -8,5 +8,5 @@ wally = "UpliftGames/wally@0.3.2"
|
|||
rojo = "rojo-rbx/rojo@7.5.1"
|
||||
stylua = "johnnymorganz/stylua@2.1.0"
|
||||
selene = "Kampfkarren/selene@0.28.0"
|
||||
luau-lsp = "johnnymorganz/luau-lsp@1.44.1"
|
||||
lute = "luau-lang/lute@0.1.0-nightly.20250511"
|
||||
luau-lsp = "johnnymorganz/luau-lsp@1.47.0"
|
||||
lute = "luau-lang/lute@0.1.0-nightly.20250518"
|
||||
|
|
|
|||
|
|
@ -4,9 +4,6 @@ if not table.isfrozen(_G) then
|
|||
_G.__DEV__ = true
|
||||
end
|
||||
|
||||
require("@pkgs/charm/tests/spec")
|
||||
require("@packages/charm/tests/spec")
|
||||
|
||||
suite.finish({
|
||||
colors = true,
|
||||
verbose = false,
|
||||
})
|
||||
suite.finish()
|
||||
|
|
|
|||
1
test/suite
Submodule
1
test/suite
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 2692eb055839cd56d0075ca72482658a5bdaddb9
|
||||
438
test/suite.luau
438
test/suite.luau
|
|
@ -1,438 +0,0 @@
|
|||
type Config = {
|
||||
colors: boolean?,
|
||||
verbose: boolean?,
|
||||
}
|
||||
|
||||
type Description = {
|
||||
status: number,
|
||||
name: string,
|
||||
only: boolean,
|
||||
skip: boolean,
|
||||
parent: Description?,
|
||||
children: { Description | Test },
|
||||
passes: number,
|
||||
fails: number,
|
||||
skips: number,
|
||||
runtime: number,
|
||||
}
|
||||
|
||||
type Test = {
|
||||
status: number,
|
||||
name: string,
|
||||
only: boolean,
|
||||
skip: boolean,
|
||||
parent: Description?,
|
||||
children: nil,
|
||||
run: () -> (),
|
||||
result: unknown?,
|
||||
line: number?,
|
||||
runtime: number,
|
||||
}
|
||||
|
||||
local options = {
|
||||
colors = true,
|
||||
verbose = false,
|
||||
}
|
||||
|
||||
local color = {
|
||||
white = function(text: string)
|
||||
return if options.colors then `\27[37;1m{text}\27[0m` else text
|
||||
end,
|
||||
gray = function(text: string)
|
||||
return if options.colors then `\27[90;1m{text}\27[0m` else text
|
||||
end,
|
||||
green = function(text: string)
|
||||
return if options.colors then `\27[32;1m{text}\27[0m` else `\u{1F7E2} {text}`
|
||||
end,
|
||||
red = function(text: string)
|
||||
return if options.colors then `\27[31;1m{text}\27[0m` else `\u{1F534} {text}`
|
||||
end,
|
||||
blue = function(text: string)
|
||||
return if options.colors then `\27[34;1m{text}\27[0m` else `\u{1F535} {text}`
|
||||
end,
|
||||
}
|
||||
|
||||
local PENDING = 0
|
||||
local PASS = 1
|
||||
local FAIL = 2
|
||||
local SKIP = 3
|
||||
local ERROR = 4
|
||||
|
||||
local tests: { Test } = {}
|
||||
local descriptions: { Description } = {}
|
||||
local root: Description = {
|
||||
status = PENDING,
|
||||
name = "root",
|
||||
only = false,
|
||||
skip = false,
|
||||
children = {},
|
||||
passes = 0,
|
||||
fails = 0,
|
||||
skips = 0,
|
||||
runtime = 0,
|
||||
}
|
||||
local parent: Description = root
|
||||
|
||||
local function describe(name: string, run: () -> ()): Description
|
||||
local this: Description = {
|
||||
status = PENDING,
|
||||
name = name,
|
||||
only = false,
|
||||
skip = false,
|
||||
parent = parent,
|
||||
children = {},
|
||||
passes = 0,
|
||||
fails = 0,
|
||||
skips = 0,
|
||||
runtime = 0,
|
||||
}
|
||||
|
||||
local prevDescription = parent
|
||||
parent = this
|
||||
run()
|
||||
parent = prevDescription
|
||||
|
||||
table.insert(parent.children, this)
|
||||
table.insert(descriptions, this)
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
local function test(name: string, run: () -> ()): Test
|
||||
local this: Test = {
|
||||
status = PENDING,
|
||||
name = name,
|
||||
only = false,
|
||||
skip = false,
|
||||
parent = parent,
|
||||
run = run,
|
||||
runtime = 0,
|
||||
}
|
||||
|
||||
table.insert(parent.children, this)
|
||||
table.insert(tests, this)
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
local function skip()
|
||||
parent.skip = true
|
||||
end
|
||||
|
||||
local function only()
|
||||
parent.only = true
|
||||
root.only = true
|
||||
end
|
||||
|
||||
local function testSkip(name: string, run: () -> ()): Test
|
||||
local this = test(name, run)
|
||||
this.skip = true
|
||||
return this
|
||||
end
|
||||
|
||||
local function testOnly(name: string, run: () -> ()): Test
|
||||
local this = test(name, run)
|
||||
this.only = true
|
||||
root.only = true
|
||||
return this
|
||||
end
|
||||
|
||||
local function describeSkip(name: string, run: () -> ()): Description
|
||||
local this = describe(name, run)
|
||||
this.skip = true
|
||||
return this
|
||||
end
|
||||
|
||||
local function describeOnly(name: string, run: () -> ()): Description
|
||||
local this = describe(name, run)
|
||||
this.only = true
|
||||
root.only = true
|
||||
return this
|
||||
end
|
||||
|
||||
local function isOnly(test: Test): boolean
|
||||
if not root.only or test.only then
|
||||
return true
|
||||
end
|
||||
|
||||
local description = test.parent
|
||||
while description and description ~= root do
|
||||
if description.only then
|
||||
return true
|
||||
elseif description.skip then
|
||||
return false
|
||||
end
|
||||
description = description.parent
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
local function isSkip(test: Test): boolean
|
||||
if test.only then
|
||||
return false
|
||||
elseif test.skip then
|
||||
return true
|
||||
end
|
||||
|
||||
local description = test.parent
|
||||
while description do
|
||||
if description.only and description ~= root then
|
||||
return false
|
||||
elseif description.skip then
|
||||
return true
|
||||
end
|
||||
description = description.parent
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
local function runTests()
|
||||
for _, test in tests do
|
||||
if isSkip(test) or not isOnly(test) then
|
||||
test.status = SKIP
|
||||
continue
|
||||
end
|
||||
|
||||
local start = os.clock()
|
||||
local success = xpcall(test.run, function(result)
|
||||
test.status = FAIL
|
||||
test.result = result
|
||||
test.line = debug.info(3, "l")
|
||||
end)
|
||||
|
||||
test.runtime = os.clock() - start
|
||||
|
||||
if success then
|
||||
test.status = PASS
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function updateDescription(description: Description)
|
||||
if not description.children[1] then
|
||||
description.status = PASS
|
||||
return
|
||||
end
|
||||
|
||||
description.status = SKIP
|
||||
|
||||
for _, child in description.children do
|
||||
if child.children then
|
||||
updateDescription(child)
|
||||
description.passes += child.passes
|
||||
description.fails += child.fails
|
||||
elseif child.status == PASS then
|
||||
description.passes += 1
|
||||
elseif child.status == FAIL then
|
||||
description.fails += 1
|
||||
end
|
||||
|
||||
-- Only count skips in direct children
|
||||
if child.status == SKIP then
|
||||
description.skips += 1
|
||||
end
|
||||
|
||||
description.runtime += child.runtime
|
||||
|
||||
if child.status == FAIL then
|
||||
description.status = FAIL
|
||||
elseif description.status == SKIP and child.status == PASS then
|
||||
description.status = PASS
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function getIndent(node: Description | Test): string
|
||||
local depth = 0
|
||||
local description = node.parent
|
||||
while description and description ~= root do
|
||||
depth += 1
|
||||
description = description.parent
|
||||
end
|
||||
return if depth > 0 then string.rep(" ", depth - 1) .. color.gray("· ") else ""
|
||||
end
|
||||
|
||||
local function getPath(node: Description | Test): string
|
||||
local path = { "" }
|
||||
local description = node.parent
|
||||
while description and description ~= root do
|
||||
table.insert(path, 1, description.name)
|
||||
description = description.parent
|
||||
end
|
||||
return table.concat(path, " > ")
|
||||
end
|
||||
|
||||
local function greenOrRed(text: string, good: boolean): string
|
||||
return if good then color.green(text) else color.red(text)
|
||||
end
|
||||
|
||||
local function colorStatus(status: number): string
|
||||
local wall = color.gray("│")
|
||||
|
||||
if status == PASS then
|
||||
return color.green("PASS") .. wall
|
||||
elseif status == FAIL then
|
||||
return color.red("FAIL") .. wall
|
||||
elseif status == SKIP then
|
||||
return color.blue("SKIP") .. wall
|
||||
else
|
||||
return color.red("ERROR") .. wall
|
||||
end
|
||||
end
|
||||
|
||||
local function report()
|
||||
local output = {}
|
||||
|
||||
local function reportDescription(description: Description)
|
||||
local name = color.white(description.name)
|
||||
local text = `{colorStatus(description.status)} {getIndent(description)}{name}`
|
||||
|
||||
if description.fails > 0 then
|
||||
text ..= " " .. color.red(`({description.fails})`)
|
||||
end
|
||||
|
||||
if description.skips > 0 then
|
||||
text ..= " " .. color.blue(`({description.skips} skipped)`)
|
||||
elseif description.fails == 0 then
|
||||
text ..= " " .. color.gray(string.format("%.2fms", description.runtime * 1000))
|
||||
end
|
||||
|
||||
table.insert(output, text)
|
||||
end
|
||||
|
||||
local function reportTest(test: Test, reportError: boolean?)
|
||||
if test.status ~= FAIL and not options.verbose then
|
||||
return
|
||||
end
|
||||
|
||||
local line = color.red(`{test.line}:`)
|
||||
|
||||
if reportError then
|
||||
local name = color.white(test.name)
|
||||
local path = color.gray(getPath(test))
|
||||
|
||||
table.insert(output, `{colorStatus(ERROR)} {path}{line}{name}`)
|
||||
table.insert(output, `{test.result}`)
|
||||
else
|
||||
local name = color.gray(test.name)
|
||||
local indent = getIndent(test)
|
||||
|
||||
if test.status == FAIL then
|
||||
table.insert(output, `{colorStatus(FAIL)} {indent}{line}{name}`)
|
||||
else
|
||||
table.insert(output, `{colorStatus(test.status)} {indent}{name}`)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function reportNode(node: Description | Test)
|
||||
if not node.children then
|
||||
return reportTest(node :: Test)
|
||||
end
|
||||
|
||||
reportDescription(node)
|
||||
|
||||
for _, child in node.children do
|
||||
reportNode(child)
|
||||
end
|
||||
end
|
||||
|
||||
local function reportSummary()
|
||||
local describePasses = 0
|
||||
local describeFails = 0
|
||||
|
||||
for _, description in descriptions do
|
||||
if description.status == PASS then
|
||||
describePasses += 1
|
||||
elseif description.status == FAIL then
|
||||
describeFails += 1
|
||||
end
|
||||
end
|
||||
|
||||
local describes =
|
||||
`{greenOrRed(`{describePasses} passed`, describeFails == 0)} {color.gray(`({#descriptions})`)}`
|
||||
local tests = `{greenOrRed(`{root.passes} passed`, root.fails == 0)} {color.gray(`({#tests})`)}`
|
||||
local duration = `{color.blue(`{string.format("%.2fms", root.runtime * 1000)}`)}`
|
||||
|
||||
table.insert(output, "")
|
||||
table.insert(output, `Describe {describes}`)
|
||||
table.insert(output, ` Tests {tests}`)
|
||||
table.insert(output, `Duration {duration}`)
|
||||
table.insert(output, "")
|
||||
end
|
||||
|
||||
local function reportErrors()
|
||||
if root.fails == 0 then
|
||||
return
|
||||
end
|
||||
|
||||
for _, test in tests do
|
||||
if test.status == FAIL then
|
||||
reportTest(test, true)
|
||||
table.insert(output, "")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(output, "")
|
||||
table.insert(output, color.blue("Test Results"))
|
||||
table.insert(output, "")
|
||||
|
||||
for _, child in root.children do
|
||||
reportNode(child)
|
||||
end
|
||||
|
||||
reportSummary()
|
||||
reportErrors()
|
||||
|
||||
print(table.concat(output, "\n"))
|
||||
|
||||
if root.fails > 0 then
|
||||
error(color.red(`{root.fails} test(s) failed`))
|
||||
end
|
||||
end
|
||||
|
||||
local function finish(config: Config?)
|
||||
if config then
|
||||
options.colors = if config.colors ~= nil then config.colors else options.colors
|
||||
options.verbose = if config.verbose ~= nil then config.verbose else options.verbose
|
||||
end
|
||||
|
||||
runTests()
|
||||
updateDescription(root)
|
||||
report()
|
||||
end
|
||||
|
||||
local testCtor = setmetatable({
|
||||
skip = testSkip,
|
||||
only = testOnly,
|
||||
}, {
|
||||
__call = function(_, ...)
|
||||
return test(...)
|
||||
end,
|
||||
})
|
||||
|
||||
local describeCtor = setmetatable({
|
||||
skip = describeSkip,
|
||||
only = describeOnly,
|
||||
}, {
|
||||
__call = function(_, ...)
|
||||
return describe(...)
|
||||
end,
|
||||
})
|
||||
|
||||
return {
|
||||
test = testCtor :: ((name: string, callback: () -> ()) -> ()) & {
|
||||
skip: (name: string, callback: () -> ()) -> (),
|
||||
only: (name: string, callback: () -> ()) -> (),
|
||||
},
|
||||
describe = describeCtor :: ((name: string, callback: () -> ()) -> ()) & {
|
||||
skip: (name: string, callback: () -> ()) -> (),
|
||||
only: (name: string, callback: () -> ()) -> (),
|
||||
},
|
||||
skip = skip,
|
||||
only = only,
|
||||
finish = finish,
|
||||
}
|
||||
98
wally.lock
98
wally.lock
|
|
@ -1,98 +0,0 @@
|
|||
# This file is automatically @generated by Wally.
|
||||
# It is not intended for manual editing.
|
||||
registry = "test"
|
||||
|
||||
[[package]]
|
||||
name = "centau/vide"
|
||||
version = "0.3.1"
|
||||
dependencies = []
|
||||
|
||||
[[package]]
|
||||
name = "jsdotlua/boolean"
|
||||
version = "1.2.7"
|
||||
dependencies = [["number", "jsdotlua/number@1.2.7"]]
|
||||
|
||||
[[package]]
|
||||
name = "jsdotlua/collections"
|
||||
version = "1.2.7"
|
||||
dependencies = [["es7-types", "jsdotlua/es7-types@1.2.7"], ["instance-of", "jsdotlua/instance-of@1.2.7"]]
|
||||
|
||||
[[package]]
|
||||
name = "jsdotlua/console"
|
||||
version = "1.2.7"
|
||||
dependencies = [["collections", "jsdotlua/collections@1.2.7"]]
|
||||
|
||||
[[package]]
|
||||
name = "jsdotlua/es7-types"
|
||||
version = "1.2.7"
|
||||
dependencies = []
|
||||
|
||||
[[package]]
|
||||
name = "jsdotlua/instance-of"
|
||||
version = "1.2.7"
|
||||
dependencies = []
|
||||
|
||||
[[package]]
|
||||
name = "jsdotlua/luau-polyfill"
|
||||
version = "1.2.7"
|
||||
dependencies = [["boolean", "jsdotlua/boolean@1.2.7"], ["collections", "jsdotlua/collections@1.2.7"], ["console", "jsdotlua/console@1.2.7"], ["es7-types", "jsdotlua/es7-types@1.2.7"], ["instance-of", "jsdotlua/instance-of@1.2.7"], ["math", "jsdotlua/math@1.2.7"], ["number", "jsdotlua/number@1.2.7"], ["string", "jsdotlua/string@1.2.7"], ["symbol-luau", "jsdotlua/symbol-luau@1.0.1"], ["timers", "jsdotlua/timers@1.2.7"]]
|
||||
|
||||
[[package]]
|
||||
name = "jsdotlua/math"
|
||||
version = "1.2.7"
|
||||
dependencies = []
|
||||
|
||||
[[package]]
|
||||
name = "jsdotlua/number"
|
||||
version = "1.2.7"
|
||||
dependencies = []
|
||||
|
||||
[[package]]
|
||||
name = "jsdotlua/promise"
|
||||
version = "3.5.2"
|
||||
dependencies = []
|
||||
|
||||
[[package]]
|
||||
name = "jsdotlua/react"
|
||||
version = "17.2.1"
|
||||
dependencies = [["luau-polyfill", "jsdotlua/luau-polyfill@1.2.7"], ["shared", "jsdotlua/shared@17.2.1"]]
|
||||
|
||||
[[package]]
|
||||
name = "jsdotlua/react-reconciler"
|
||||
version = "17.2.1"
|
||||
dependencies = [["luau-polyfill", "jsdotlua/luau-polyfill@1.2.7"], ["promise", "jsdotlua/promise@3.5.2"], ["react", "jsdotlua/react@17.2.1"], ["scheduler", "jsdotlua/scheduler@17.2.1"], ["shared", "jsdotlua/shared@17.2.1"]]
|
||||
|
||||
[[package]]
|
||||
name = "jsdotlua/react-roblox"
|
||||
version = "17.2.1"
|
||||
dependencies = [["luau-polyfill", "jsdotlua/luau-polyfill@1.2.7"], ["react", "jsdotlua/react@17.2.1"], ["react-reconciler", "jsdotlua/react-reconciler@17.2.1"], ["scheduler", "jsdotlua/scheduler@17.2.1"], ["shared", "jsdotlua/shared@17.2.1"]]
|
||||
|
||||
[[package]]
|
||||
name = "jsdotlua/scheduler"
|
||||
version = "17.2.1"
|
||||
dependencies = [["luau-polyfill", "jsdotlua/luau-polyfill@1.2.7"], ["shared", "jsdotlua/shared@17.2.1"]]
|
||||
|
||||
[[package]]
|
||||
name = "jsdotlua/shared"
|
||||
version = "17.2.1"
|
||||
dependencies = [["luau-polyfill", "jsdotlua/luau-polyfill@1.2.7"]]
|
||||
|
||||
[[package]]
|
||||
name = "jsdotlua/string"
|
||||
version = "1.2.7"
|
||||
dependencies = [["es7-types", "jsdotlua/es7-types@1.2.7"], ["number", "jsdotlua/number@1.2.7"]]
|
||||
|
||||
[[package]]
|
||||
name = "jsdotlua/symbol-luau"
|
||||
version = "1.0.1"
|
||||
dependencies = []
|
||||
|
||||
[[package]]
|
||||
name = "jsdotlua/timers"
|
||||
version = "1.2.7"
|
||||
dependencies = [["collections", "jsdotlua/collections@1.2.7"]]
|
||||
|
||||
[[package]]
|
||||
name = "richerd/charm"
|
||||
version = "0.1.0"
|
||||
dependencies = [["React", "jsdotlua/react@17.2.1"], ["ReactRoblox", "jsdotlua/react-roblox@17.2.1"], ["Vide", "centau/vide@0.3.1"]]
|
||||
10
wally.toml
10
wally.toml
|
|
@ -1,10 +0,0 @@
|
|||
[package]
|
||||
name = "richerd/charm"
|
||||
version = "0.1.0"
|
||||
registry = "https://github.com/UpliftGames/wally-index"
|
||||
realm = "shared"
|
||||
|
||||
[dev-dependencies]
|
||||
React = "jsdotlua/react@17"
|
||||
ReactRoblox = "jsdotlua/react-roblox@17"
|
||||
Vide = "centau/vide@0.3"
|
||||
Loading…
Add table
Add a link
Reference in a new issue