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
|
/.vscode
|
||||||
/.pesde
|
/.pesde
|
||||||
/build
|
/build
|
||||||
/DevPackages
|
/packages/**/wally.lock
|
||||||
/pkgs/**/wally.lock
|
|
||||||
/sourcemap.json
|
/sourcemap.json
|
||||||
|
node_modules
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
*.rbxm
|
*.rbxm
|
||||||
.DS_Store
|
.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": {
|
"aliases": {
|
||||||
"test": "test",
|
"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 bench = require("./bench")
|
||||||
|
|
||||||
local atom = Charm.atom
|
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 bench = require("./bench")
|
||||||
|
|
||||||
local signals = Charm.signals
|
local signals = Charm.signals
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
mkdir build
|
mkdir build
|
||||||
rojo build pkgs/charm -o build/charm.rbxm
|
rojo build packages/charm -o build/charm.rbxm
|
||||||
rojo build pkgs/charm-sync -o build/charm-sync.rbxm
|
rojo build packages/charm-sync -o build/charm-sync.rbxm
|
||||||
rojo build pkgs/react-charm -o build/react-charm.rbxm
|
rojo build packages/react-charm -o build/react-charm.rbxm
|
||||||
rojo build pkgs/vide-charm -o build/vide-charm.rbxm
|
rojo build packages/vide-charm -o build/vide-charm.rbxm
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,11 @@ luau-lsp analyze \
|
||||||
--defs=bin/roblox.d.luau \
|
--defs=bin/roblox.d.luau \
|
||||||
--flag:LuauFixIndexerSubtypingOrdering=true \
|
--flag:LuauFixIndexerSubtypingOrdering=true \
|
||||||
--sourcemap=sourcemap.json \
|
--sourcemap=sourcemap.json \
|
||||||
--ignore="**/_Index/**" \
|
--ignore="**/node_modules/**" \
|
||||||
pkgs test
|
packages test
|
||||||
|
|
||||||
selene pkgs test
|
selene packages test
|
||||||
|
|
||||||
stylua --check pkgs test
|
stylua --check packages test
|
||||||
|
|
||||||
rm bin/roblox.d.luau
|
rm bin/roblox.d.luau
|
||||||
|
|
|
||||||
|
|
@ -5,21 +5,22 @@
|
||||||
"ReplicatedStorage": {
|
"ReplicatedStorage": {
|
||||||
"$className": "ReplicatedStorage",
|
"$className": "ReplicatedStorage",
|
||||||
"Packages": {
|
"Packages": {
|
||||||
"$path": "DevPackages",
|
"$path": "node_modules/@rbxts-js",
|
||||||
"Charm": { "$path": "pkgs/charm" },
|
"Vide": { "$path": "node_modules/@rbxts/vide/src" },
|
||||||
"CharmSync": { "$path": "pkgs/charm-sync" },
|
"Charm": { "$path": "packages/charm" },
|
||||||
"ReactCharm": { "$path": "pkgs/react-charm" },
|
"CharmSync": { "$path": "packages/charm-sync" },
|
||||||
"VideCharm": { "$path": "pkgs/vide-charm" }
|
"ReactCharm": { "$path": "packages/react-charm" },
|
||||||
|
"VideCharm": { "$path": "packages/vide-charm" }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ServerScriptService": {
|
"ServerScriptService": {
|
||||||
"$className": "ServerScriptService",
|
"$className": "ServerScriptService",
|
||||||
"Tests": {
|
"Tests": {
|
||||||
"$path": "test",
|
"$path": "test",
|
||||||
"Charm": { "$path": "pkgs/charm/tests" },
|
"Charm": { "$path": "packages/charm/tests" },
|
||||||
"CharmSync": { "$path": "pkgs/charm-sync/tests" },
|
"CharmSync": { "$path": "packages/charm-sync/tests" },
|
||||||
"ReactCharm": { "$path": "pkgs/react-charm/tests" },
|
"ReactCharm": { "$path": "packages/react-charm/tests" },
|
||||||
"VideCharm": { "$path": "pkgs/vide-charm/tests" }
|
"VideCharm": { "$path": "packages/vide-charm/tests" }
|
||||||
},
|
},
|
||||||
"Benchmarks": {
|
"Benchmarks": {
|
||||||
"$path": "benches"
|
"$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, ...)
|
local function handleBatched(ok: boolean, ...)
|
||||||
endBatch()
|
endBatch()
|
||||||
if not ok then
|
if ok then
|
||||||
return ...
|
return ...
|
||||||
else
|
else
|
||||||
error(...)
|
error(...)
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
local atom = require("./atom")
|
local atom = require("@self/atom")
|
||||||
local batched = require("./batched")
|
local batched = require("@self/batched")
|
||||||
local effect = require("./effect")
|
local effect = require("@self/effect")
|
||||||
local listen = require("./listen")
|
local listen = require("@self/listen")
|
||||||
local mapped = require("./mapped")
|
local mapped = require("@self/mapped")
|
||||||
local observe = require("./observe")
|
local observe = require("@self/observe")
|
||||||
local onCleanup = require("./onCleanup")
|
local onCleanup = require("@self/onCleanup")
|
||||||
local signals = require("./signals")
|
local signals = require("@self/signals")
|
||||||
local subscribe = require("./subscribe")
|
local subscribe = require("@self/subscribe")
|
||||||
local types = require("./types")
|
local types = require("@self/types")
|
||||||
local untracked = require("./untracked")
|
local untracked = require("@self/untracked")
|
||||||
|
|
||||||
export type Atom<T = any> = types.Atom<T>
|
export type Atom<T = any> = types.Atom<T>
|
||||||
export type Getter<T = any> = types.Getter<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 suite = require("@test/suite")
|
||||||
|
|
||||||
local atom = Charm.atom
|
local atom = Charm.atom
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
local Charm = require("../src/lib")
|
local Charm = require("../src")
|
||||||
local suite = require("@test/suite")
|
local suite = require("@test/suite")
|
||||||
|
|
||||||
local atom = Charm.atom
|
local atom = Charm.atom
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
local Charm = require("../src/lib")
|
local Charm = require("../src")
|
||||||
local suite = require("@test/suite")
|
local suite = require("@test/suite")
|
||||||
|
|
||||||
local atom = Charm.atom
|
local atom = Charm.atom
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
local Charm = require("../src/lib")
|
local Charm = require("../src")
|
||||||
local suite = require("@test/suite")
|
local suite = require("@test/suite")
|
||||||
|
|
||||||
type Atom<T> = Charm.Atom<T>
|
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 suite = require("@test/suite")
|
||||||
|
|
||||||
local atom = Charm.atom
|
local atom = Charm.atom
|
||||||
|
|
@ -5,6 +5,7 @@ local effect = Charm.effect
|
||||||
local useBinding = React.useBinding
|
local useBinding = React.useBinding
|
||||||
local useEffect = React.useEffect
|
local useEffect = React.useEffect
|
||||||
local useState = React.useState
|
local useState = React.useState
|
||||||
|
local useMemo = React.useMemo
|
||||||
|
|
||||||
--- A hook that subscribes to changes in the given atom or selector. The
|
--- A hook that subscribes to changes in the given atom or selector. The
|
||||||
--- component is re-rendered whenever the state changes.
|
--- component is re-rendered whenever the state changes.
|
||||||
|
|
@ -39,7 +40,8 @@ end
|
||||||
--- @param dependencies An array of values that the subscription depends on.
|
--- @param dependencies An array of values that the subscription depends on.
|
||||||
--- @return A binding that stores the current state.
|
--- @return A binding that stores the current state.
|
||||||
local function useAtomBinding<State>(selector: () -> State, dependencies: { any }?)
|
local function useAtomBinding<State>(selector: () -> State, dependencies: { any }?)
|
||||||
local binding, setBinding = useBinding(selector)
|
local initialState = useMemo(selector, {})
|
||||||
|
local binding, setBinding = useBinding(initialState)
|
||||||
|
|
||||||
useEffect(function()
|
useEffect(function()
|
||||||
return effect(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"
|
rojo = "rojo-rbx/rojo@7.5.1"
|
||||||
stylua = "johnnymorganz/stylua@2.1.0"
|
stylua = "johnnymorganz/stylua@2.1.0"
|
||||||
selene = "Kampfkarren/selene@0.28.0"
|
selene = "Kampfkarren/selene@0.28.0"
|
||||||
luau-lsp = "johnnymorganz/luau-lsp@1.44.1"
|
luau-lsp = "johnnymorganz/luau-lsp@1.47.0"
|
||||||
lute = "luau-lang/lute@0.1.0-nightly.20250511"
|
lute = "luau-lang/lute@0.1.0-nightly.20250518"
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,6 @@ if not table.isfrozen(_G) then
|
||||||
_G.__DEV__ = true
|
_G.__DEV__ = true
|
||||||
end
|
end
|
||||||
|
|
||||||
require("@pkgs/charm/tests/spec")
|
require("@packages/charm/tests/spec")
|
||||||
|
|
||||||
suite.finish({
|
suite.finish()
|
||||||
colors = true,
|
|
||||||
verbose = false,
|
|
||||||
})
|
|
||||||
|
|
|
||||||
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