diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..9e7830f --- /dev/null +++ b/.eslintrc @@ -0,0 +1,25 @@ +{ + "parser": "@typescript-eslint/parser", + "parserOptions": { + "jsx": true, + "useJSXTextNode": true, + "ecmaVersion": 2018, + "sourceType": "module", + "project": "./tsconfig.json" + }, + "plugins": [ + "@typescript-eslint", + "roblox-ts", + "prettier" + ], + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:roblox-ts/recommended", + "plugin:prettier/recommended" + ], + "rules": { + "prettier/prettier": "warn", + "@typescript-eslint/no-explicit-any": "off" + } +} diff --git a/.gitignore b/.gitignore index bd0480d..9e5c89e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,11 @@ .vscode _local +.DS_Store + +node_modules +out +src/test.tsx +*.tgz aftman.toml sourcemap.json diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..5c55faa --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "printWidth": 120, + "tabWidth": 2, + "trailingComma": "all", + "useTabs": true +} diff --git a/README.md b/README.md index 8ce077b..62426af 100644 --- a/README.md +++ b/README.md @@ -36,3 +36,125 @@ local function Counter() } end ``` + +## JSX + +Vide for roblox-ts brings JSX support to the library. As a result, this extension adds a set of components and utilities to improve usage. + +> [!TIP] +> +> - Vide JSX adds new components for syntax sugar, including ``, ``/``, ``/``, and ``. +> - Use the `action` prop to create a Vide action that receives the new instance as an argument. +> - `switch` is a reserved keyword in TypeScript, so the `switch()` function is exposed under the alias `match()`. + +### Installation + +To use JSX with Vide, you need to configure the `jsx` option in your `tsconfig.json`: + +```json +"compilerOptions": { + "jsx": "react", + "jsxFactory": "Vide.jsx", + "jsxFragmentFactory": "Vide.Fragment", +} +``` + +> [!NOTE] +> Vide JSX requires roblox-ts version 3.0 or higher. +> You can update roblox-ts by running `npm install -D roblox-ts@latest`. + +### Code sample + +```tsx +function Counter() { + const count = source(0); + + return ( + `count: ${count()}`} + TextChanged={(text) => print(text)} + Activated={() => count(count() + 1)} + /> + ); +} +``` + +### `` + +A conditional rendering component that accepts a boolean value and a function that returns the element to render when the condition is true. + +```tsx +const show = source(true); + + + {() => { + return ; + }} +; +``` + +### ``/`` + +A conditional rendering component that accepts a value and a list of cases. Each case is denoted by a `` component, and if the `condition` matches the `match` prop of a case, the corresponding element is rendered. + +```tsx +const value = source("a"); + + + {() => } + {() => } + {() => } +; +``` + +### `` + +A referentially keyed loop (rendered nodes are keyed to a table value). The `each` prop accepts an array or a map, and calls the `children` function for each element in the array or map. + +If an entry is removed or changed, the corresponding node is updated or cleaned up. + +```tsx +const items = source(["a", "b", "c"]); + + + {(item: string, index: () => number) => { + return ; + }} +; +``` + +### `` + +A referentially keyed loop (rendered nodes are keyed to a table index). The `each` prop accepts an array or a map, and calls the `children` function for each element in the array or map. + +If an entry is removed or changed, the corresponding node is updated or cleaned up. + +```tsx +const items = source(["a", "b", "c"]); + + + {(item: () => string, index: number) => { + return item()} />; + }} +; +``` + +### `` + +A component that renders its children with the `value` prop assigned to the context. The value can be accessed by calling the `context` function while the `children` function is running. + +`` is syntax sugar for `context(value, () => children)`. + +> [!NOTE] +> The context function must be called within the top-level of a component. Calling it within an effect or on a new thread may return the default value. + +```tsx +const theme = context("light"); + + + {() => { + const value = theme(); + return ; + }} +; +``` diff --git a/dev.project.json b/dev.project.json new file mode 100644 index 0000000..bc2a194 --- /dev/null +++ b/dev.project.json @@ -0,0 +1,4 @@ +{ + "name": "vide", + "tree": { "$path": "out" } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..c7752ab --- /dev/null +++ b/package.json @@ -0,0 +1,46 @@ +{ + "name": "@rbxts/vide", + "version": "0.5.7", + "description": "A reactive Luau library for creating UI", + "main": "src/init.lua", + "types": "src/index.d.ts", + "scripts": { + "build": "rbxtsc --rojo dev.project.json" + }, + "keywords": [ + "roblox-ts", + "ui" + ], + "author": "centau", + "contributors": [ + "littensy" + ], + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/littensy/vide" + }, + "bugs": { + "url": "https://github.com/littensy/vide/issues" + }, + "homepage": "https://github.com/littensy/vide", + "files": [ + "src" + ], + "publishConfig": { + "access": "public" + }, + "devDependencies": { + "@rbxts/compiler-types": "2.3.0-types.1", + "@rbxts/types": "^1.0.799", + "@typescript-eslint/eslint-plugin": "^7.18.0", + "@typescript-eslint/parser": "^7.18.0", + "eslint": "^8.57.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.2.1", + "eslint-plugin-roblox-ts": "^0.0.36", + "prettier": "^3.3.3", + "roblox-ts": "2.3.0-dev-539482e", + "typescript": "^5.5.4" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..6146e41 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,1675 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + '@rbxts/compiler-types': + specifier: 2.3.0-types.1 + version: 2.3.0-types.1 + '@rbxts/types': + specifier: ^1.0.799 + version: 1.0.799 + '@typescript-eslint/eslint-plugin': + specifier: ^7.18.0 + version: 7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0)(typescript@5.5.4) + '@typescript-eslint/parser': + specifier: ^7.18.0 + version: 7.18.0(eslint@8.57.0)(typescript@5.5.4) + eslint: + specifier: ^8.57.0 + version: 8.57.0 + eslint-config-prettier: + specifier: ^9.1.0 + version: 9.1.0(eslint@8.57.0) + eslint-plugin-prettier: + specifier: ^5.2.1 + version: 5.2.1(eslint-config-prettier@9.1.0(eslint@8.57.0))(eslint@8.57.0)(prettier@3.3.3) + eslint-plugin-roblox-ts: + specifier: ^0.0.36 + version: 0.0.36(eslint@8.57.0) + prettier: + specifier: ^3.3.3 + version: 3.3.3 + roblox-ts: + specifier: 2.3.0-dev-539482e + version: 2.3.0-dev-539482e + typescript: + specifier: ^5.5.4 + version: 5.5.4 + +packages: + + '@eslint-community/eslint-utils@4.4.0': + resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.11.0': + resolution: {integrity: sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/eslintrc@2.1.4': + resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@eslint/js@8.57.0': + resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@humanwhocodes/config-array@0.11.14': + resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} + engines: {node: '>=10.10.0'} + deprecated: Use @eslint/config-array instead + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/object-schema@2.0.3': + resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} + deprecated: Use @eslint/object-schema instead + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@pkgr/core@0.1.1': + resolution: {integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + + '@rbxts/compiler-types@2.3.0-types.1': + resolution: {integrity: sha512-NZWNo+fC4icfJte+NiDSDdWJo1KwzD0MDQ2iBi70YhNYlkA7+Xc+B1udbVqxLJuBur2JxG5bbKrpMAfHqfCtbw==} + + '@rbxts/types@1.0.799': + resolution: {integrity: sha512-R/4lHaPpCEZOmnqgIIMcpV/TufhSlvW8m+Uud88xd1qedOt9PX1Y40QGXtbuMKwjwgtKYrgQhph/lOjqKU5meQ==} + + '@roblox-ts/luau-ast@1.0.11': + resolution: {integrity: sha512-+maoLYpqY0HK8ugLFHS3qz0phMyDaN3i21jjW75T2ZaqJg84heKDUo98iXClvnx3mUDhW10IxqH+cYJ2iftYhQ==} + + '@roblox-ts/path-translator@1.0.0': + resolution: {integrity: sha512-Lp6qVUqjmXIrICy2KPKRiX8IkJ+lNqn6RqoUplLiTr+4JehIN+mJv0tTnE72XRyIfcx0VWl5nKrRwUuqcOj1yg==} + + '@roblox-ts/rojo-resolver@1.0.7': + resolution: {integrity: sha512-LG0RM191h06bmCYf0TTGKyLuKBR1464I2IwZxYqP434XD5ALRDg0xbtyQQqww++cPoTDBR6aPYH/dGdn6Wle8g==} + + '@types/fs-extra@11.0.4': + resolution: {integrity: sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/jsonfile@6.1.4': + resolution: {integrity: sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==} + + '@types/mocha@10.0.7': + resolution: {integrity: sha512-GN8yJ1mNTcFcah/wKEFIJckJx9iJLoMSzWcfRRuxz/Jk+U6KQNnml+etbtxFK8lPjzOw3zp4Ha/kjSst9fsHYw==} + + '@types/node@20.16.0': + resolution: {integrity: sha512-vDxceJcoZhIVh67S568bm1UGZO0DX0hpplJZxzeXMKwIPLn190ec5RRxQ69BKhX44SUGIxxgMdDY557lGLKprQ==} + + '@types/prompts@2.4.9': + resolution: {integrity: sha512-qTxFi6Buiu8+50/+3DGIWLHM6QuWsEKugJnnP6iv2Mc4ncxE4A/OJkjuVOA+5X0X1S/nq5VJRa8Lu+nwcvbrKA==} + + '@types/resolve@1.20.6': + resolution: {integrity: sha512-A4STmOXPhMUtHH+S6ymgE2GiBSMqf4oTvcQZMcHzokuTLVYzXTB8ttjcgxOVaAp2lGwEdzZ0J+cRbbeevQj1UQ==} + + '@types/semver@7.5.8': + resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} + + '@types/yargs-parser@21.0.3': + resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} + + '@types/yargs@17.0.33': + resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} + + '@typescript-eslint/eslint-plugin@7.18.0': + resolution: {integrity: sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + '@typescript-eslint/parser': ^7.0.0 + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/experimental-utils@5.62.0': + resolution: {integrity: sha512-RTXpeB3eMkpoclG3ZHft6vG/Z30azNHuqY6wKPBHlVMZFuEvrtlEDe8gMqDb+SO+9hjC/pLekeSCryf9vMZlCw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + + '@typescript-eslint/parser@7.18.0': + resolution: {integrity: sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/scope-manager@5.62.0': + resolution: {integrity: sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@typescript-eslint/scope-manager@7.18.0': + resolution: {integrity: sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==} + engines: {node: ^18.18.0 || >=20.0.0} + + '@typescript-eslint/type-utils@7.18.0': + resolution: {integrity: sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/types@5.62.0': + resolution: {integrity: sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@typescript-eslint/types@7.18.0': + resolution: {integrity: sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==} + engines: {node: ^18.18.0 || >=20.0.0} + + '@typescript-eslint/typescript-estree@5.62.0': + resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/typescript-estree@7.18.0': + resolution: {integrity: sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/utils@5.62.0': + resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + + '@typescript-eslint/utils@7.18.0': + resolution: {integrity: sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + eslint: ^8.56.0 + + '@typescript-eslint/visitor-keys@5.62.0': + resolution: {integrity: sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@typescript-eslint/visitor-keys@7.18.0': + resolution: {integrity: sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==} + engines: {node: ^18.18.0 || >=20.0.0} + + '@ungap/structured-clone@1.2.0': + resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.12.1: + resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ajv@8.17.1: + resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + + brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + + debug@4.3.6: + resolution: {integrity: sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + escalade@3.1.2: + resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} + engines: {node: '>=6'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-config-prettier@9.1.0: + resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + + eslint-plugin-prettier@5.2.1: + resolution: {integrity: sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + '@types/eslint': '>=8.0.0' + eslint: '>=8.0.0' + eslint-config-prettier: '*' + prettier: '>=3.0.0' + peerDependenciesMeta: + '@types/eslint': + optional: true + eslint-config-prettier: + optional: true + + eslint-plugin-roblox-ts@0.0.36: + resolution: {integrity: sha512-vlbgfHY1heWSyDunVX3gIOamCy28KqrJlDkmuDOyYVBo8uLqdvbFkbWkdGxn59Vky1m6pFcvZGU6TObfmwm5Pg==} + engines: {node: '>=0.10.0'} + + eslint-scope@5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + + eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint@8.57.0: + resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + + espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-diff@1.3.0: + resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} + + fast-glob@3.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fast-uri@3.0.1: + resolution: {integrity: sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==} + + fastq@1.17.1: + resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + + file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@3.2.0: + resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} + engines: {node: ^10.12.0 || >=12.0.0} + + flatted@3.3.1: + resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} + + fs-extra@11.2.0: + resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==} + engines: {node: '>=14.14'} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported + + globals@13.24.0: + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} + engines: {node: '>=8'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-core-module@2.15.0: + resolution: {integrity: sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + jsonfile@6.1.0: + resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + kleur@3.0.3: + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} + + kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.7: + resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} + engines: {node: '>=8.6'} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier-linter-helpers@1.0.0: + resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} + engines: {node: '>=6.0.0'} + + prettier@3.3.3: + resolution: {integrity: sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==} + engines: {node: '>=14'} + hasBin: true + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true + + reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + roblox-ts@2.3.0-dev-539482e: + resolution: {integrity: sha512-IhyAwu35pX+l/aTlGpV8Ql9HQd8cNFBXfVnXpqa3/21YIqdrg0z+W/FQbdVOYivWWskxgzSZCcKT9rwKOF02ZA==} + hasBin: true + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + semver@7.6.3: + resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} + engines: {node: '>=10'} + hasBin: true + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + synckit@0.9.1: + resolution: {integrity: sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==} + engines: {node: ^14.18.0 || >=16.0.0} + + text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + ts-api-utils@1.3.0: + resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} + engines: {node: '>=16'} + peerDependencies: + typescript: '>=4.2.0' + + ts-expose-internals@5.3.3: + resolution: {integrity: sha512-0lW96u0pa08dq9QLYUBRbgGkvsD+LvPAVGe94kr2tHaRZAxgckwJ+qqc1BSRoR1rpkTZf7AWAa6DKrLGFmctwg==} + + tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + + tslib@2.6.3: + resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} + + tsutils@3.21.0: + resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} + engines: {node: '>= 6'} + peerDependencies: + typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + + typescript@5.4.2: + resolution: {integrity: sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==} + engines: {node: '>=14.17'} + hasBin: true + + typescript@5.5.4: + resolution: {integrity: sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@6.19.6: + resolution: {integrity: sha512-e/vggGopEfTKSvj4ihnOLTsqhrKRN3LeO6qSN/GxohhuRv8qH9bNQ4B8W7e/vFL+0XTnmHPB4/kegunZGA4Org==} + + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + +snapshots: + + '@eslint-community/eslint-utils@4.4.0(eslint@8.57.0)': + dependencies: + eslint: 8.57.0 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.11.0': {} + + '@eslint/eslintrc@2.1.4': + dependencies: + ajv: 6.12.6 + debug: 4.3.6 + espree: 9.6.1 + globals: 13.24.0 + ignore: 5.3.2 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@8.57.0': {} + + '@humanwhocodes/config-array@0.11.14': + dependencies: + '@humanwhocodes/object-schema': 2.0.3 + debug: 4.3.6 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/object-schema@2.0.3': {} + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.17.1 + + '@pkgr/core@0.1.1': {} + + '@rbxts/compiler-types@2.3.0-types.1': {} + + '@rbxts/types@1.0.799': {} + + '@roblox-ts/luau-ast@1.0.11': {} + + '@roblox-ts/path-translator@1.0.0': + dependencies: + ajv: 8.17.1 + fs-extra: 11.2.0 + + '@roblox-ts/rojo-resolver@1.0.7': + dependencies: + ajv: 8.17.1 + fs-extra: 11.2.0 + + '@types/fs-extra@11.0.4': + dependencies: + '@types/jsonfile': 6.1.4 + '@types/node': 20.16.0 + + '@types/json-schema@7.0.15': {} + + '@types/jsonfile@6.1.4': + dependencies: + '@types/node': 20.16.0 + + '@types/mocha@10.0.7': {} + + '@types/node@20.16.0': + dependencies: + undici-types: 6.19.6 + + '@types/prompts@2.4.9': + dependencies: + '@types/node': 20.16.0 + kleur: 3.0.3 + + '@types/resolve@1.20.6': {} + + '@types/semver@7.5.8': {} + + '@types/yargs-parser@21.0.3': {} + + '@types/yargs@17.0.33': + dependencies: + '@types/yargs-parser': 21.0.3 + + '@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0)(typescript@5.5.4)': + dependencies: + '@eslint-community/regexpp': 4.11.0 + '@typescript-eslint/parser': 7.18.0(eslint@8.57.0)(typescript@5.5.4) + '@typescript-eslint/scope-manager': 7.18.0 + '@typescript-eslint/type-utils': 7.18.0(eslint@8.57.0)(typescript@5.5.4) + '@typescript-eslint/utils': 7.18.0(eslint@8.57.0)(typescript@5.5.4) + '@typescript-eslint/visitor-keys': 7.18.0 + eslint: 8.57.0 + graphemer: 1.4.0 + ignore: 5.3.2 + natural-compare: 1.4.0 + ts-api-utils: 1.3.0(typescript@5.5.4) + optionalDependencies: + typescript: 5.5.4 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/experimental-utils@5.62.0(eslint@8.57.0)(typescript@5.5.4)': + dependencies: + '@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.5.4) + eslint: 8.57.0 + transitivePeerDependencies: + - supports-color + - typescript + + '@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4)': + dependencies: + '@typescript-eslint/scope-manager': 7.18.0 + '@typescript-eslint/types': 7.18.0 + '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.5.4) + '@typescript-eslint/visitor-keys': 7.18.0 + debug: 4.3.6 + eslint: 8.57.0 + optionalDependencies: + typescript: 5.5.4 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@5.62.0': + dependencies: + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/visitor-keys': 5.62.0 + + '@typescript-eslint/scope-manager@7.18.0': + dependencies: + '@typescript-eslint/types': 7.18.0 + '@typescript-eslint/visitor-keys': 7.18.0 + + '@typescript-eslint/type-utils@7.18.0(eslint@8.57.0)(typescript@5.5.4)': + dependencies: + '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.5.4) + '@typescript-eslint/utils': 7.18.0(eslint@8.57.0)(typescript@5.5.4) + debug: 4.3.6 + eslint: 8.57.0 + ts-api-utils: 1.3.0(typescript@5.5.4) + optionalDependencies: + typescript: 5.5.4 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@5.62.0': {} + + '@typescript-eslint/types@7.18.0': {} + + '@typescript-eslint/typescript-estree@5.62.0(typescript@5.5.4)': + dependencies: + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/visitor-keys': 5.62.0 + debug: 4.3.6 + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.6.3 + tsutils: 3.21.0(typescript@5.5.4) + optionalDependencies: + typescript: 5.5.4 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/typescript-estree@7.18.0(typescript@5.5.4)': + dependencies: + '@typescript-eslint/types': 7.18.0 + '@typescript-eslint/visitor-keys': 7.18.0 + debug: 4.3.6 + globby: 11.1.0 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.6.3 + ts-api-utils: 1.3.0(typescript@5.5.4) + optionalDependencies: + typescript: 5.5.4 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@5.62.0(eslint@8.57.0)(typescript@5.5.4)': + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) + '@types/json-schema': 7.0.15 + '@types/semver': 7.5.8 + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.5.4) + eslint: 8.57.0 + eslint-scope: 5.1.1 + semver: 7.6.3 + transitivePeerDependencies: + - supports-color + - typescript + + '@typescript-eslint/utils@7.18.0(eslint@8.57.0)(typescript@5.5.4)': + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) + '@typescript-eslint/scope-manager': 7.18.0 + '@typescript-eslint/types': 7.18.0 + '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.5.4) + eslint: 8.57.0 + transitivePeerDependencies: + - supports-color + - typescript + + '@typescript-eslint/visitor-keys@5.62.0': + dependencies: + '@typescript-eslint/types': 5.62.0 + eslint-visitor-keys: 3.4.3 + + '@typescript-eslint/visitor-keys@7.18.0': + dependencies: + '@typescript-eslint/types': 7.18.0 + eslint-visitor-keys: 3.4.3 + + '@ungap/structured-clone@1.2.0': {} + + acorn-jsx@5.3.2(acorn@8.12.1): + dependencies: + acorn: 8.12.1 + + acorn@8.12.1: {} + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ajv@8.17.1: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.0.1 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + argparse@2.0.1: {} + + array-union@2.1.0: {} + + balanced-match@1.0.2: {} + + binary-extensions@2.3.0: {} + + brace-expansion@1.1.11: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.1: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + callsites@3.1.0: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + concat-map@0.0.1: {} + + cross-spawn@7.0.3: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + debug@4.3.6: + dependencies: + ms: 2.1.2 + + deep-is@0.1.4: {} + + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + + doctrine@3.0.0: + dependencies: + esutils: 2.0.3 + + emoji-regex@8.0.0: {} + + escalade@3.1.2: {} + + escape-string-regexp@4.0.0: {} + + eslint-config-prettier@9.1.0(eslint@8.57.0): + dependencies: + eslint: 8.57.0 + + eslint-plugin-prettier@5.2.1(eslint-config-prettier@9.1.0(eslint@8.57.0))(eslint@8.57.0)(prettier@3.3.3): + dependencies: + eslint: 8.57.0 + prettier: 3.3.3 + prettier-linter-helpers: 1.0.0 + synckit: 0.9.1 + optionalDependencies: + eslint-config-prettier: 9.1.0(eslint@8.57.0) + + eslint-plugin-roblox-ts@0.0.36(eslint@8.57.0): + dependencies: + '@types/node': 20.16.0 + '@typescript-eslint/experimental-utils': 5.62.0(eslint@8.57.0)(typescript@5.5.4) + typescript: 5.5.4 + transitivePeerDependencies: + - eslint + - supports-color + + eslint-scope@5.1.1: + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + + eslint-scope@7.2.2: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint@8.57.0: + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) + '@eslint-community/regexpp': 4.11.0 + '@eslint/eslintrc': 2.1.4 + '@eslint/js': 8.57.0 + '@humanwhocodes/config-array': 0.11.14 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + '@ungap/structured-clone': 1.2.0 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.6 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.24.0 + graphemer: 1.4.0 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + + espree@9.6.1: + dependencies: + acorn: 8.12.1 + acorn-jsx: 5.3.2(acorn@8.12.1) + eslint-visitor-keys: 3.4.3 + + esquery@1.6.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@4.3.0: {} + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + fast-deep-equal@3.1.3: {} + + fast-diff@1.3.0: {} + + fast-glob@3.3.2: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.7 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fast-uri@3.0.1: {} + + fastq@1.17.1: + dependencies: + reusify: 1.0.4 + + file-entry-cache@6.0.1: + dependencies: + flat-cache: 3.2.0 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@3.2.0: + dependencies: + flatted: 3.3.1 + keyv: 4.5.4 + rimraf: 3.0.2 + + flatted@3.3.1: {} + + fs-extra@11.2.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + + fs.realpath@1.0.0: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + get-caller-file@2.0.5: {} + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + + globals@13.24.0: + dependencies: + type-fest: 0.20.2 + + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.2 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 + + graceful-fs@4.2.11: {} + + graphemer@1.4.0: {} + + has-flag@4.0.0: {} + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + ignore@5.3.2: {} + + import-fresh@3.3.0: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-core-module@2.15.0: + dependencies: + hasown: 2.0.2 + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-number@7.0.0: {} + + is-path-inside@3.0.3: {} + + isexe@2.0.0: {} + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-schema-traverse@1.0.0: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + jsonfile@6.1.0: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + kleur@3.0.3: {} + + kleur@4.1.5: {} + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + + merge2@1.4.1: {} + + micromatch@4.0.7: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.11 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.1 + + ms@2.1.2: {} + + natural-compare@1.4.0: {} + + normalize-path@3.0.0: {} + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + path-exists@4.0.0: {} + + path-is-absolute@1.0.1: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + path-type@4.0.0: {} + + picomatch@2.3.1: {} + + prelude-ls@1.2.1: {} + + prettier-linter-helpers@1.0.0: + dependencies: + fast-diff: 1.3.0 + + prettier@3.3.3: {} + + punycode@2.3.1: {} + + queue-microtask@1.2.3: {} + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + + require-directory@2.1.1: {} + + require-from-string@2.0.2: {} + + resolve-from@4.0.0: {} + + resolve@1.22.8: + dependencies: + is-core-module: 2.15.0 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + reusify@1.0.4: {} + + rimraf@3.0.2: + dependencies: + glob: 7.2.3 + + roblox-ts@2.3.0-dev-539482e: + dependencies: + '@roblox-ts/luau-ast': 1.0.11 + '@roblox-ts/path-translator': 1.0.0 + '@roblox-ts/rojo-resolver': 1.0.7 + '@types/fs-extra': 11.0.4 + '@types/mocha': 10.0.7 + '@types/node': 20.16.0 + '@types/prompts': 2.4.9 + '@types/resolve': 1.20.6 + '@types/ts-expose-internals': ts-expose-internals@5.3.3 + '@types/yargs': 17.0.33 + chokidar: 3.6.0 + fs-extra: 11.2.0 + kleur: 4.1.5 + resolve: 1.22.8 + typescript: 5.4.2 + yargs: 17.7.2 + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + semver@7.6.3: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + slash@3.0.0: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-json-comments@3.1.1: {} + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + synckit@0.9.1: + dependencies: + '@pkgr/core': 0.1.1 + tslib: 2.6.3 + + text-table@0.2.0: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + ts-api-utils@1.3.0(typescript@5.5.4): + dependencies: + typescript: 5.5.4 + + ts-expose-internals@5.3.3: {} + + tslib@1.14.1: {} + + tslib@2.6.3: {} + + tsutils@3.21.0(typescript@5.5.4): + dependencies: + tslib: 1.14.1 + typescript: 5.5.4 + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-fest@0.20.2: {} + + typescript@5.4.2: {} + + typescript@5.5.4: {} + + undici-types@6.19.6: {} + + universalify@2.0.1: {} + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + word-wrap@1.2.5: {} + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrappy@1.0.2: {} + + y18n@5.0.8: {} + + yargs-parser@21.1.1: {} + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.1.2 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yocto-queue@0.1.0: {} diff --git a/src/components.luau b/src/components.luau new file mode 100644 index 0000000..f61a5f1 --- /dev/null +++ b/src/components.luau @@ -0,0 +1,77 @@ +local context = require(script.Parent.context) +local indexes, values = require(script.Parent.maps)() +local show = require(script.Parent.show) +local switch = require(script.Parent.switch) + +type Context = context.Context + +type Case = { + match: any, + children: () -> any, +} + +local function Fragment(props: { children: any }) + return props.children +end + +local function For(props: { + each: () -> { [K]: VI }, + children: (VI, () -> K) -> VO, +}): () -> { VO } + return values(props.each, props.children) +end + +local function Index(props: { + each: () -> { [K]: VI }, + children: (() -> VI, K) -> VO, +}): () -> { VO } + return indexes(props.each, props.children) +end + +local function Switch(props: { + condition: () -> any, + children: { Case }, +}) + local children = props.children :: { [any]: any } + local map = {} + + if children.match then + map[children.match] = children.children + else + for _, node in children do + map[node.match] = node.children + end + end + + return switch(props.condition)(map) +end + +local function Case(props: Case): Case + return props +end + +local function Show(props: { + when: any, + children: () -> any, + fallback: () -> any, +}) + return show(props.when, props.children, props.fallback) +end + +local function Provider(props: { + context: Context, + value: T, + children: () -> any, +}) + return props.context(props.value, props.children) +end + +return { + Fragment = Fragment, + Switch = Switch, + Case = Case, + For = For, + Index = Index, + Show = Show, + Provider = Provider, +} diff --git a/src/index.d.ts b/src/index.d.ts new file mode 100644 index 0000000..a732714 --- /dev/null +++ b/src/index.d.ts @@ -0,0 +1,731 @@ +type Destructor = () => void; +type Key = string | number | symbol; + +export = Vide; +export as namespace Vide; + +declare namespace Vide { + /** + * A reactive state container that can be read and updated. Calling the source + * with no arguments will return the stored value, whereas calling it with an + * argument (including `undefined`) will update the stored value. + * + * @param value The new value to store. + * + * @returns The current value. + */ + type Source = (value?: T) => T; + + /** + * A value that can be either a reactive source property or a static value. + * Useful for defining component props that can be either static or dynamic. + */ + type Derivable = T | (() => T); + + /** + * A function component that renders a Vide node. + * + * @param props Properties passed to the component. + * + * @returns The rendered node. + */ + type Component = (props: Props) => Node; + + /** + * Utility type for a component that accepts `children` as a prop. + */ + type PropsWithChildren = Props & { children?: Node }; + + /** + * An object containing the custom logic to invoke when an instance is + * created. Created using `action()` or using the special `action` prop. + */ + interface VideAction { + priority: number; + callback: (instance: T) => void; + } + + /** + * A value that can be animated by Vide's spring system. + */ + type Animatable = number | CFrame | Color3 | UDim | UDim2 | Vector2 | Vector3 | Rect; + + /** + * Any destructible object that can be passed to `cleanup()`. + */ + type Disposable = + | Destructor + | Instance + | RBXScriptConnection + | { disconnect(): void } + | { destroy(): void } + | { Disconnect(): void } + | { Destroy(): void }; + + /** + * Strict mode is designed to help the development process by adding + * safety checks and identifying improper usage. + * + * As well as additional safety checks, Vide will dedicate extra + * resources to recording and better emitting stack traces where + * errors occur, particularly when binding properties to sources. + * + * It is recommended to develop UI with strict mode and to disable it + * when pushing to production. In Roblox, production code compiles at + * O2 by default, so you don't need to worry about disabling strict + * mode unless you have manually enabled it. + * + * @see https://centau.github.io/vide/api/strict-mode + */ + let strict: boolean; + + /** + * Creates a new stable scope, where creation of effects can be tracked + * and properly disposed of. Returns the result of the given function. + * + * A function to destroy the root is passed into the callback, which will + * run any cleanups and allow derived sources created to garbage collect. + * + * @param fn The function to run in a new stable scope. + * + * @returns The results of the function. + * + * @see https://centau.github.io/vide/api/reactivity-core#root + */ + // overload for a tuple return + function root(fn: (destroy: Destructor) => LuaTuple): LuaTuple<[Destructor, ...T]>; + // overload for a single return + function root(fn: (destroy: Destructor) => T): LuaTuple<[Destructor, T]>; + // overload for no return + function root(fn: (destroy: Destructor) => void): Destructor; + + /** + * Runs a function in a new stable scope and optionally applies its result + * to a target instance. Returns a function that, when called, will destroy + * the stable scope. + * + * The result of the function is applied to a target in the same way + * properties are using `create()`. + * + * @param component The function to run in a new stable scope. + * @param target The target instance to apply the result to. + * + * @returns A function to destroy the stable scope. + * + * @see https://centau.github.io/vide/api/creation#mount + */ + function mount(component: () => T, target?: Instance): Destructor; + + /** + * Creates a new instance and applies any given properties. + * + * If given a `string`, a new instance with the same class name will be + * created. Otherwise, if given an `Instance`, a new instance that is a + * clone of the given instance will be created. + * + * @param className The class name of the instance to create, or an instance to clone. + * @param props The properties to apply to the new instance. + * + * @returns A function that applies properties to the new instance. + * + * @see https://centau.github.io/vide/api/creation#create + */ + function create( + className: K, + ): (props?: LegacyInstanceProps) => CreatableInstances[K]; + // overload where a template instance is passed + function create(instance: T): (props?: LegacyInstanceProps) => T; + + /** + * Craetes a reactive state container that can be read and updated. Calling + * the source with no arguments will return the stored value, whereas calling + * it with an argument (including `undefined`) will update the stored value. + * + * Sources can be passed directly to `create()` to create a reactive instance + * property. + * + * @param initialValue The initial value to store. + * + * @returns A reactive state container. + * + * @see https://centau.github.io/vide/api/reactivity-core#source + */ + function source(initialValue: T): Source; + // convenience overload for when the initial value is optional + function source(): Source; + + /** + * Runs a side-effect in a new reactive scope when any of its dependencies + * change. Any time a source referenced in the callback is updated, the + * callback will be reran. The callback is ran once immediately. + * + * Optionally, the callback can return a value that will be passed during + * the next rerun. This can be useful for comparing values between runs. + * + * @param callback The side-effect to run when dependencies change. + * @param initialValue Optional initial argument to pass to the callback. + * + * @see https://centau.github.io/vide/api/reactivity-core#effect + */ + function effect(callback: () => void): void; + // overload where callback stores a value between runs + function effect(callback: (value: T) => T, initialValue: T): void; + + /** + * Derives a new source in a new reactive scope from existing sources. The + * derived source will have its value recalculated and cached when any source + * it derives from is updated. + * + * @param source The source to derive from. + * + * @returns A new derived source. + * + * @see https://centau.github.io/vide/api/reactivity-core#derive + */ + function derive(source: () => T): () => T; + + /** + * Shows one of a set of components depending on an input source and a + * mapping table. Returns a source holding an instance of the currently + * rendered component. + * + * When the input source changes, the new value will be used to lookup a + * given mapping table to get a component. During the next change, the + * stable scope the component was created in will be destroyed, and a new + * component created under a new stable scope. + * + * @param source The source to match against. + * @param map The mapping table of components. + * + * @returns A source holding the currently rendered component. + * + * @see https://centau.github.io/vide/api/reactivity-flow#switch + */ + // switch is a reserved keyword + function match(source: () => T): (map: { [P in T]?: () => U }) => () => U | undefined; + + /** + * Shows one of two components depending on an input source. Returns a source + * holding an instance of the currently rendered component. + * + * When the input source changes from a falsey to a truthy value, the + * component will be reran under a new stable scope. If it changes from + * a truthy to falsey value, the stable scope the component was created + * in will be destroyed, and the returned source will output `nil`, or + * a fallback component if given. + * + * @param source The source to match against. + * @param component The component to render when the source is truthy. + * @param fallback The component to render when the source is falsey. + * + * @returns A source holding the currently rendered component. + * + * @see https://centau.github.io/vide/api/reactivity-flow#show + */ + function show(source: () => any, component: () => T, fallback?: () => U): () => T | U; + + /** + * Maps each _key_ in a table source to a component. Returns a source holding + * an array of the rendered components. + * + * When the input source changes, each key in the new table is compared with + * the last input table. + * - For any new key, the transform function is ran under a new stable scope + * to produce a new instance. + * - For any removed key, the stable scope for that key is destroyed. + * - Keys whose values have changed will be untouched. + * + * @param input The table source to map over. + * @param transform The function to transform each value in the table. + * + * @returns A source holding an array of the rendered components. + * + * @see https://centau.github.io/vide/api/reactivity-flow#indexes + */ + // overload for an array input + function indexes(input: () => readonly VI[], transform: (value: () => VI, index: number) => VO): () => VO[]; + // overload for a map input + function indexes( + input: () => Map | ReadonlyMap, + transform: (value: () => VI, key: K) => VO, + ): () => VO[]; + // overload for an object input + function indexes( + input: () => { readonly [P in K]: VI }, + transform: (value: () => VI, key: K) => VO, + ): () => VO[]; + + /** + * Maps each _value_ in a table source to a component. Returns a source + * holding an array of the rendered components. + * + * When the input source changes, each value in the new table is compared + * with the last input table. + * - For any new value, the transform function is ran under a new stable + * scope to produce a new instance. + * - For any removed value, the stable scope for that value is destroyed. + * - Values whose keys have changed will be untouched. + * + * **CAUTION:** Having primitive values in the input source table can cause + * unexpected behavior, as duplicate values can result in multiple tranforms + * being ran for a single value. It is recommended to use a table with unique + * values to avoid this issue. + * + * @param input The table source to map over. + * @param transform The function to transform each value in the table. + * + * @returns A source holding an array of the rendered components. + * + * @see https://centau.github.io/vide/api/reactivity-flow#values + */ + // overload for an array input + function values(input: () => readonly VI[], transform: (value: VI, index: () => number) => VO): () => VO[]; + // overload for a map input + function values( + input: () => Map | ReadonlyMap, + transform: (value: VI, key: () => K) => VO, + ): () => VO[]; + // overload for an object input + function values( + input: () => { readonly [P in K]: VI }, + transform: (value: VI, key: () => K) => VO, + ): () => VO[]; + + /** + * Runs the callback function when the scope reruns or is destroyed. Should + * be used to clean up instances and connections created in the scope. + * + * @param value The disposable object to clean up. + * + * @see https://centau.github.io/vide/api/reactivity-utility#cleanup + */ + function cleanup(value: Disposable): void; + + /** + * Runs a function without tracking any reactive sources. Can be used inside + * a reactive scope to read from sources you do not want tracked by the scope. + * + * @param source The source to read from. + * + * @returns The value of the source. + * + * @see https://centau.github.io/vide/api/reactivity-utility#untrack + */ + function untrack(source: Source): T; + + /** + * Reads the source and returns its value. Non-source values are returned + * as-is, making this useful for component props that can be either static + * or dynamic. + * + * @param source The source to read from. + * + * @returns The value of the source. + * + * @see https://centau.github.io/vide/api/reactivity-utility#read + */ + function read(source: Derivable): T; + + /** + * Runs a given function where any source updates made within the function + * do not trigger effects until after the function finishes running. + * + * @param setter The function to run. + * + * @see https://centau.github.io/vide/api/reactivity-utility#batch + */ + function batch(setter: () => void): void; + + /** + * Returns a new source with a value always moving towards the input source + * value. The spring will oscillate around the input value until it reaches + * equilibrium. + * + * @param period The time in seconds it takes for the spring to complete one + * full cycle if undamped. + * @param dampingRatio The amount of resistance applied to the spring. + * + * @returns A new source that moves towards the input source value. + * + * @see https://centau.github.io/vide/api/animation#spring + */ + function spring(source: () => T, period?: number, dampingRatio?: number): Source; + + /** + * Creates a callback that can be passed to `create()` to invoke custom + * actions on instances. The callback will be invoked when the instance is + * created, before any properties are applied. + * + * @param callback The function to run when the instance is created. + * @param priority The priority of the action. Higher priorities run first. + * + * @returns An action object. + * + * @see https://centau.github.io/vide/api/creation#action + */ + function action(callback: (instance: T) => void, priority?: number): VideAction; + + /** + * Creates an action that runs the callback when a property changes on an + * instance. The callback will be invoked once initially and every time the + * property changes. + * + * @param key The property to watch for changes. + * @param callback The function to run when the property changes. + * + * @returns An action object. + * + * @see https://centau.github.io/vide/api/creation#changed + */ + function changed>( + key: K, + callback: (value: WritableInstanceProperties[K]) => void, + ): VideAction; + + /** + * Applies properties to an existing instance. Similarly to `create()`, the + * properties can be either static values or reactive sources. + * + * @param instance The instance to apply properties to. + * @param props The properties to apply to the instance. + * + * @returns The instance with the properties applied. + */ + function apply(instance: T): (props: LegacyInstanceProps) => T; + + /** + * By default, springs run at 120 Hz in the `Heartbeat` event. Calling this + * function can change when the solver runs, which will advance the simulation + * time by `deltaTime` seconds. + * + * Once called, the internal solver will disconnect to allow the user to + * advance the simulation time manually. + * + * @param deltaTime The time in seconds to advance the simulation. + * + * @see https://centau.github.io/vide/api/animation#spring + */ + function step(deltaTime: number): void; + + // Components + + /** + * A blank element that can be used to group multiple elements together. + * Identical to passing an array of instances to `create()`. + */ + function Fragment(props: T): T; + + /** + * Maps each _value_ in a table source to a component. Returns a source + * holding an array of the rendered components. + * + * When the input source changes, each value in the new table is compared + * with the last input table. + * - For any new value, the function is ran under a new stable scope to + * produce a new instance. + * - For any removed value, the stable scope for that value is destroyed. + * - Values whose keys have changed will be untouched. + * + * @example + * ```tsx + * ["a", "b", "c"]}> + * {(id: string, index: () => number) => } + * + * ``` + * + * @param each The table source to map over. + * @param children The function to transform each value in the table. + * + * @returns A function that returns an array of the rendered components. + * + * @see https://centau.github.io/vide/api/reactivity-flow#values + */ + // overload for an array input + function For(props: { + each: () => readonly VI[]; + children: (item: VI, index: () => number) => VO; + }): () => VO[]; + // overload for a map input + function For(props: { + each: () => Map | ReadonlyMap; + children: (value: VI, key: () => K) => VO; + }): () => VO[]; + // overload for an object input + function For(props: { + each: () => { readonly [P in K]: VI }; + children: (value: VI, key: () => K) => VO; + }): () => VO[]; + + /** + * Maps each _key_ in a table source to a component. Returns a source holding + * an array of the rendered components. + * + * When the input source changes, each key in the new table is compared with + * the last input table. + * - For any new key, the function is ran under a new stable scope to produce + * a new instance. + * - For any removed key, the stable scope for that key is destroyed. + * - Keys whose values have changed will be untouched. + * + * @example + * ```tsx + * new Map()}> + * {(data: () => Data, id: string) => } + * + * ``` + * + * @param each The table source to map over. + * @param children The function to transform each value in the table. + * + * @returns A function that returns an array of the rendered components. + * + * @see https://centau.github.io/vide/api/reactivity-flow#indexes + */ + // overload for an array input + function Index(props: { + each: () => readonly VI[]; + children: (item: () => VI, index: number) => VO; + }): () => VO[]; + // overload for a map input + function Index(props: { + each: () => Map | ReadonlyMap; + children: (value: () => VI, key: K) => VO; + }): () => VO[]; + // overload for an object input + function Index(props: { + each: () => { readonly [P in K]: VI }; + children: (value: () => VI, key: K) => VO; + }): () => VO[]; + + /** + * Shows one of a set of components depending on an input source and a + * set of `Case` components. Renders the `Case` component that matches + * the input source. + * + * @example + * ```tsx + * + * match="loading">{() => + * match="error">{() => } + * match="success">{() => + * + * ``` + * + * @param condition The source to match against. + * @param children The `Case` components to render. + * + * @returns The currently rendered component. + * + * @see https://centau.github.io/vide/api/reactivity-flow#switch + */ + function Switch(props: { condition: () => any; children: Node }): () => Node; + + /** + * Declares a component that will be rendered when the `condition` prop + * of the `Switch` component matches this component's `match` prop. + * + * @template T The type of the value to match against. + * + * @param match The value to match against. + * @param children The component to render when the value matches. + * + * @returns The rendered component. + * + * @see Switch + */ + function Case(props: { match: T; children: () => Node | void }): Node; + + /** + * Shows one of two components depending on an input source. Renders the + * first component when the source is truthy, and the second component + * when the source is falsey. + * + * @example + * ```tsx + * }> + * {() => } + * + * ``` + * + * @param when The source to match against. + * @param children The component to render when the source is truthy. + * @param fallback The component to render when the source is falsey. + * + * @returns The rendered component. + * + * @see https://centau.github.io/vide/api/reactivity-flow#show + */ + function Show(props: { when: () => any; children: () => Node | void; fallback?: () => Node | void }): () => Node; + + // Context + + interface Context { + (): T; + (value: T, component: () => U): U; + } + + /** + * Creates a new context that can be used to share state between components. + * + * @example + * ```tsx + * const theme = context("light"); + * + * + * {theme("dark", () => { + * return ; + * })} + * + * ``` + * + * @template T The type of value to store in the context. + * + * @param defaultValue The default value to store in the context. + * + * @returns A new context function. + */ + function context(defaultValue?: T): Context; + + /** + * Renders a component with a new value in the context. The value will be + * passed to any components that read from the context within the component. + * + * **Note:** The context function must be called within the top-level of a + * component. Calling it within an effect or on a new thread will return the + * default value. + * + * @example + * ```tsx + * const theme = context("light"); + * + * + * {() => } + * + * ``` + * + * @param context The context to pass `children` to. + * @param value The new value to store in the context. + * @param children The component to render with the new context value. + * + * @returns The rendered component. + */ + function Provider(props: { context: Context; value: T; children: () => Node | void }): () => Node; + + // Elements + + /** + * A value that can be passed to a JSX element. + */ + type Node = Instance | InstanceAttributes | VideAction | FragmentNode | FunctionNode | undefined; + + /** + * A value that can be passed to the `create()` function. + * + * @template T The type of instance to create. + */ + type LegacyNode = + | Instance + | InstanceAttributes + | VideAction + | FragmentNode + | FunctionNode + | undefined; + + /** + * A value that contains a collection of nodes. Vide unwraps these values + * when rendering, allowing for nested arrays to be passed as children. + */ + type FragmentNode = + | Map + | ReadonlyMap + | readonly Node[] + | { readonly [key: number]: Node }; + + /** + * A function that returns a node. + */ + type FunctionNode = () => Node; + + /** + * Attributes intrinsic to all JSX elements. + */ + interface Attributes { + children?: Node; + } + + /** + * Attributes including the `action` macro. Intrinsic to all JSX instances. + */ + interface ActionAttributes extends Attributes { + action?: (instance: T) => void; + } + + /** + * Infers the names of the enum values from an enum item. Resolves to a union + * of the enum items and their respective names. + */ + type InferEnumNames = T extends EnumItem ? T | T["Name"] : T; + + /** + * Instance properties that can be written to or assigned Vide sources. + */ + type InstancePropertySources = { + [K in keyof WritableInstanceProperties]?: Derivable[K]>>; + }; + + /** + * Instance event properties that can be passed a callback function. + */ + type InstanceEventCallbacks = { + [K in InstanceEventNames]?: T[K] extends RBXScriptSignal<(...args: infer A) => void> + ? (...args: A) => void + : never; + }; + + /** + * Instance property change events that can be passed a callback function. + * Internally, these are resolved to `changed()` actions. + */ + type InstanceChangedCallbacks = { + [K in `${Extract, string>}Changed`]?: K extends `${infer P extends Extract, string>}Changed` + ? (value: T[P]) => void + : never; + }; + + /** + * Instance events and property change events that can be passed a callback + * function. Property change events are a macro for `changed()` actions. + */ + type InstanceEventAttributes = InstanceEventCallbacks & InstanceChangedCallbacks; + + /** + * Instance properties and events that can be used with JSX syntax. + */ + type InstanceAttributes = ActionAttributes & + InstancePropertySources & + InstanceEventAttributes; + + /** + * Instance properties and events that can be used with the + * legacy `create()` function. + */ + type LegacyInstanceProps = { [K in number]?: LegacyNode } & InstancePropertySources & + InstanceEventCallbacks; +} + +declare global { + namespace JSX { + type Element = Vide.Node; + type ElementType = string | ((props: any) => Element | void); + + interface IntrinsicAttributes extends Vide.Attributes {} + + interface ElementChildrenAttribute { + children: {}; + } + + type IntrinsicElements = { + [K in keyof CreatableInstances as Lowercase]: Vide.InstanceAttributes; + } + } +} diff --git a/src/init.luau b/src/init.luau index 3bcc7ad..4e5a1ec 100644 --- a/src/init.luau +++ b/src/init.luau @@ -24,8 +24,10 @@ local indexes, values = require(script.maps)() local spring, update_springs = require(script.spring)() local action = require(script.action)() local changed = require(script.changed) +local components = require(script.components) local throw = require(script.throw) local flags = require(script.flags) +local jsx = require(script.jsx) export type Source = source.Source export type source = Source @@ -97,7 +99,18 @@ local vide = { stepped = nil end step(dt) - end + end, + + -- typescript + jsx = jsx, + match = switch, + Fragment = components.Fragment, + Switch = components.Switch, + Case = components.Case, + For = components.For, + Index = components.Index, + Show = components.Show, + Provider = components.Provider, } setmetatable(vide :: any, { diff --git a/src/jsx.luau b/src/jsx.luau new file mode 100644 index 0000000..962670e --- /dev/null +++ b/src/jsx.luau @@ -0,0 +1,76 @@ +local action = require(script.Parent.action)() +local changed = require(script.Parent.changed) +local create = require(script.Parent.create) +local tags = require(script.Parent.tags) +local untrack = require(script.Parent.untrack) + +type Props = { [any]: any } + +local IGNORED_CHANGE_PROPS = { + AncestryChanged = true, + AttributeChanged = true, + AxisChanged = true, + ButtonChanged = true, + ControlPointChanged = true, + EmotesChanged = true, + HealthChanged = true, + InputChanged = true, + LightingChanged = true, + SelectionChanged = true, + StateChanged = true, + ThemeChanged = true, + ViewportChanged = true, +} + +local function jsx(tag: string | (Props) -> any, props: Props, ...): any + props = props or {} + + if type(tag) == "string" then + tag = tags[tag] or tag + + local actions = {} + + for key, value in props do + if type(key) ~= "string" or IGNORED_CHANGE_PROPS[key] then + continue + end + + local property = string.match(key, "^(.+)Changed$") + + if property then + table.insert(actions, changed(property, value)) + props[key] = nil + end + end + + for _, action in actions do + table.insert(props, action) + end + + if props.action then + table.insert(props, action(props.action)) + props.action = nil + end + + for index = 1, select("#", ...) do + local child = select(index, ...) + table.insert(props, child) + end + + return create(tag)(props) + end + + local count = select("#", ...) + + if count > 1 then + props.children = { ... } + elseif count == 1 then + props.children = ... + end + + return untrack(function() + return tag(props) + end) +end + +return jsx diff --git a/src/tags.luau b/src/tags.luau new file mode 100644 index 0000000..6248cf5 --- /dev/null +++ b/src/tags.luau @@ -0,0 +1,7 @@ +local tags = {} + +for _, class in game:GetService("ReflectionService"):GetClasses() do + tags[string.lower(class.Name)] = class.Name +end + +return tags diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..8b5a2c6 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + // required + "allowSyntheticDefaultImports": true, + "downlevelIteration": true, + "jsx": "react", + "jsxFactory": "Vide.jsx", + "jsxFragmentFactory": "Vide.Fragment", + "module": "commonjs", + "moduleResolution": "Node", + "noLib": true, + "resolveJsonModule": true, + "experimentalDecorators": true, + "forceConsistentCasingInFileNames": true, + "moduleDetection": "force", + "strict": true, + "target": "ESNext", + "typeRoots": ["node_modules/@rbxts"], + + // configurable + "rootDir": "src", + "outDir": "out", + "incremental": true, + "tsBuildInfoFile": "out/tsconfig.tsbuildinfo", + "declaration": true + } +}