From 00320c11ab1f734f5f9e800e5870b2a23ad19828 Mon Sep 17 00:00:00 2001 From: skdhg <46562212+skdhg@users.noreply.github.com> Date: Fri, 26 Jan 2024 20:11:52 +0545 Subject: [PATCH 1/3] feat(command-parser): basic impl --- packages/commandkit/helpers/command-parser.ts | 160 +++++++ packages/commandkit/package.json | 8 +- .../commandkit/spec/command-parser.spec.ts | 34 ++ .../spec/sample/(general)/ping/command.ts | 0 .../spec/sample/(general)/ping/validator.ts | 0 .../sample/(general)/pong/test/command.ts | 0 .../spec/sample/(general)/pong/validator.ts | 0 .../commandkit/spec/sample/_test/command.ts | 0 packages/commandkit/tsconfig.json | 2 +- packages/commandkit/vitest.config.ts | 9 + pnpm-lock.yaml | 406 +++++++++++++++++- 11 files changed, 611 insertions(+), 8 deletions(-) create mode 100644 packages/commandkit/helpers/command-parser.ts create mode 100644 packages/commandkit/spec/command-parser.spec.ts create mode 100644 packages/commandkit/spec/sample/(general)/ping/command.ts create mode 100644 packages/commandkit/spec/sample/(general)/ping/validator.ts create mode 100644 packages/commandkit/spec/sample/(general)/pong/test/command.ts create mode 100644 packages/commandkit/spec/sample/(general)/pong/validator.ts create mode 100644 packages/commandkit/spec/sample/_test/command.ts create mode 100644 packages/commandkit/vitest.config.ts diff --git a/packages/commandkit/helpers/command-parser.ts b/packages/commandkit/helpers/command-parser.ts new file mode 100644 index 0000000..d4fd417 --- /dev/null +++ b/packages/commandkit/helpers/command-parser.ts @@ -0,0 +1,160 @@ +import { dirname, join } from 'node:path'; +import { readdir } from 'node:fs/promises'; + +export interface CommandParserOptions { + src: string; + filter: (name: string) => boolean; + maxDepth: number; + extensions: RegExp; +} + +export const enum CommandEntityKind { + Command, + ContextMenuCommand, + Category, + Subcommand, + DynamicCommand, + Validator, +} + +const IGNORE_PATTERN = /^_/; +const CATEGORY_PATTERN = /^\([a-z0-9-_]{1,}\)$/; +const DYNAMIC_PATTERN = /^\[[a-z0-9-_]{1,}\]$/; + +export interface CommandEntity { + name: string; + kind: CommandEntityKind; + path: string; + children: CommandEntity[]; + category: string | null; +} + +export class CommandParser { + #data: CommandEntity[] = []; + + public constructor(public readonly options: CommandParserOptions) {} + + public clear() { + this.#data = []; + } + + public getCommands() { + return this.#data; + } + + public async scan() { + const { src, maxDepth } = this.options; + + const files = await this.#scanDir(src, maxDepth); + + this.#data.push(...files); + + return files; + } + + public map(): Map { + const map = new Map(); + + for (const command of this.#data) { + map.set(command.name, command); + } + + return map; + } + + async #scanDir(dir: string, depth: number) { + const files: CommandEntity[] = []; + + for await (const dirent of await readdir(dir, { withFileTypes: true })) { + if (!this.options.filter(dirent.name)) continue; + + if (dirent.isDirectory()) { + if (depth > 0) { + if (CATEGORY_PATTERN.test(dirent.name)) { + const name = dirent.name.replace(/\(|\)/g, ''); + const fullPath = join(dir, dirent.name); + + files.push({ + name, + path: fullPath, + kind: CommandEntityKind.Category, + children: (await this.#scanDir(fullPath, depth - 1)).map((e) => ({ + ...e, + category: name, + })), + category: null, + }); + } else { + const path = join(dir, dirent.name); + + const nearestCategory = + path + .split('/') + .reverse() + .find((e) => CATEGORY_PATTERN.test(e)) ?? null; + + // ignoring category pattern, if path is nested more than once, it is a subcommand + const isSubcommand = + path + .replace(this.options.src, '') + .split('/') + .filter((e) => !IGNORE_PATTERN.test(e)).length > 1; + + const isDynamic = DYNAMIC_PATTERN.test(dirent.name); + + files.push({ + name: dirent.name, + kind: isSubcommand + ? CommandEntityKind.Subcommand + : isDynamic + ? CommandEntityKind.DynamicCommand + : CommandEntityKind.Command, + path, + children: [...(await this.#scanDir(path, depth - 1))], + category: nearestCategory, + }); + } + } + } else { + if (!this.options.extensions.test(dirent.name)) continue; + + const fullPath = join(dir, dirent.name); + const name = dirent.name.replace(this.options.extensions, ''); + + let kind: CommandEntityKind | null = null; + + switch (name) { + case 'command': + kind = CommandEntityKind.Command; + break; + case 'validator': + kind = CommandEntityKind.Validator; + break; + case 'context': + kind = CommandEntityKind.ContextMenuCommand; + break; + default: + break; + } + + if (kind === null) continue; + + const nearestCategory = + fullPath + .split('/') + .reverse() + .find((e) => CATEGORY_PATTERN.test(e)) ?? null; + + files.push({ + name: dirname(fullPath), + path: fullPath, + kind, + children: [], + category: nearestCategory, + }); + } + } + + return files; + } +} diff --git a/packages/commandkit/package.json b/packages/commandkit/package.json index c06ffd5..706758d 100644 --- a/packages/commandkit/package.json +++ b/packages/commandkit/package.json @@ -24,7 +24,8 @@ "build": "tsup", "deploy": "npm publish", "deploy-dev": "npm publish --access public --tag dev", - "test": "cd ./tests && node ../bin/index.mjs dev", + "test": "vitest", + "test:dev": "cd ./tests && node ../bin/index.mjs dev", "test:build": "cd ./tests && node ../bin/index.mjs build", "test:prod": "cd ./tests && node ../bin/index.mjs start" }, @@ -52,9 +53,10 @@ "discord.js": "^14.14.1", "tsconfig": "workspace:*", "tsx": "^4.7.0", - "typescript": "^5.3.3" + "typescript": "^5.3.3", + "vitest": "^1.2.1" }, "peerDependencies": { "discord.js": "^14" } -} +} \ No newline at end of file diff --git a/packages/commandkit/spec/command-parser.spec.ts b/packages/commandkit/spec/command-parser.spec.ts new file mode 100644 index 0000000..eb93dfb --- /dev/null +++ b/packages/commandkit/spec/command-parser.spec.ts @@ -0,0 +1,34 @@ +import { describe, beforeAll, test, expect } from 'vitest'; +import { CommandEntityKind, CommandParser } from '../helpers/command-parser'; + +describe('CommandParser', () => { + const parser = new CommandParser({ + src: `${__dirname}/sample`, + filter: (name) => !name.startsWith('_'), + maxDepth: 5, + extensions: /\.(c|m)?(j|t)sx?$/, + }); + + beforeAll(async () => { + await parser.scan(); + }); + + test('should scan all files', () => { + expect(parser.getCommands().length).toBeGreaterThan(0); + }); + + test('should contain general category', () => { + const cmds = parser.getCommands(); + const cat = cmds.find((c) => c.kind === CommandEntityKind.Category && c.name === 'general'); + + expect(cat?.name).toBe('general'); + }); + + test('general should contain commands', () => { + const cmds = parser.getCommands(); + const cat = cmds.find((c) => c.kind === CommandEntityKind.Category && c.name === 'general'); + + expect(cat?.children.length).toBeGreaterThan(0); + expect(cat?.children.map((m) => m.name)).toEqual(['ping', 'pong']); + }); +}); diff --git a/packages/commandkit/spec/sample/(general)/ping/command.ts b/packages/commandkit/spec/sample/(general)/ping/command.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/commandkit/spec/sample/(general)/ping/validator.ts b/packages/commandkit/spec/sample/(general)/ping/validator.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/commandkit/spec/sample/(general)/pong/test/command.ts b/packages/commandkit/spec/sample/(general)/pong/test/command.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/commandkit/spec/sample/(general)/pong/validator.ts b/packages/commandkit/spec/sample/(general)/pong/validator.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/commandkit/spec/sample/_test/command.ts b/packages/commandkit/spec/sample/_test/command.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/commandkit/tsconfig.json b/packages/commandkit/tsconfig.json index 60bff6f..b687e43 100644 --- a/packages/commandkit/tsconfig.json +++ b/packages/commandkit/tsconfig.json @@ -3,6 +3,6 @@ "compilerOptions": { "outDir": "dist" }, - "include": ["src/**/*.ts"], + "include": ["src/**/*.ts", "helpers/**/*.ts", "spec"], "exclude": ["node_modules"] } diff --git a/packages/commandkit/vitest.config.ts b/packages/commandkit/vitest.config.ts new file mode 100644 index 0000000..6503d25 --- /dev/null +++ b/packages/commandkit/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + include: ['./spec/**/*.{test,spec}.?(c|m)[jt]s?(x)'], + watch: false, + dangerouslyIgnoreUnhandledErrors: true, + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9452916..559d5e5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -90,6 +90,9 @@ importers: typescript: specifier: ^5.3.3 version: 5.3.3 + vitest: + specifier: ^1.2.1 + version: 1.2.1(@types/node@20.11.6) packages/create-commandkit: dependencies: @@ -443,6 +446,13 @@ packages: wrap-ansi-cjs: /wrap-ansi@7.0.0 dev: false + /@jest/schemas@29.6.3: + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@sinclair/typebox': 0.27.8 + dev: true + /@jridgewell/gen-mapping@0.3.3: resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} engines: {node: '>=6.0.0'} @@ -841,6 +851,10 @@ packages: engines: {node: '>=v14.0.0', npm: '>=7.0.0'} dev: true + /@sinclair/typebox@0.27.8: + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + dev: true + /@swc/helpers@0.5.2: resolution: {integrity: sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==} dependencies: @@ -1008,6 +1022,45 @@ packages: resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} dev: false + /@vitest/expect@1.2.1: + resolution: {integrity: sha512-/bqGXcHfyKgFWYwIgFr1QYDaR9e64pRKxgBNWNXPefPFRhgm+K3+a/dS0cUGEreWngets3dlr8w8SBRw2fCfFQ==} + dependencies: + '@vitest/spy': 1.2.1 + '@vitest/utils': 1.2.1 + chai: 4.4.1 + dev: true + + /@vitest/runner@1.2.1: + resolution: {integrity: sha512-zc2dP5LQpzNzbpaBt7OeYAvmIsRS1KpZQw4G3WM/yqSV1cQKNKwLGmnm79GyZZjMhQGlRcSFMImLjZaUQvNVZQ==} + dependencies: + '@vitest/utils': 1.2.1 + p-limit: 5.0.0 + pathe: 1.1.2 + dev: true + + /@vitest/snapshot@1.2.1: + resolution: {integrity: sha512-Tmp/IcYEemKaqAYCS08sh0vORLJkMr0NRV76Gl8sHGxXT5151cITJCET20063wk0Yr/1koQ6dnmP6eEqezmd/Q==} + dependencies: + magic-string: 0.30.5 + pathe: 1.1.2 + pretty-format: 29.7.0 + dev: true + + /@vitest/spy@1.2.1: + resolution: {integrity: sha512-vG3a/b7INKH7L49Lbp0IWrG6sw9j4waWAucwnksPB1r1FTJgV7nkBByd9ufzu6VWya/QTvQW4V9FShZbZIB2UQ==} + dependencies: + tinyspy: 2.2.0 + dev: true + + /@vitest/utils@1.2.1: + resolution: {integrity: sha512-bsH6WVZYe/J2v3+81M5LDU8kW76xWObKIURpPrOXm2pjBniBu2MERI/XP60GpS4PHU3jyK50LUutOwrx4CyHUg==} + dependencies: + diff-sequences: 29.6.3 + estree-walker: 3.0.3 + loupe: 2.3.7 + pretty-format: 29.7.0 + dev: true + /@vladfrangu/async_event_emitter@2.2.2: resolution: {integrity: sha512-HIzRG7sy88UZjBJamssEczH5q7t5+axva19UbZLO6u0ySbYPrwzWiXBcC0WuHyhKKoeCyneH+FvYzKQq/zTtkQ==} engines: {node: '>=v14.0.0', npm: '>=7.0.0'} @@ -1021,11 +1074,21 @@ packages: acorn: 8.11.2 dev: false + /acorn-walk@8.3.2: + resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==} + engines: {node: '>=0.4.0'} + dev: true + /acorn@8.11.2: resolution: {integrity: sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==} engines: {node: '>=0.4.0'} hasBin: true - dev: false + + /acorn@8.11.3: + resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true /ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} @@ -1055,6 +1118,11 @@ packages: color-convert: 2.0.1 dev: false + /ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + dev: true + /ansi-styles@6.2.1: resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} engines: {node: '>=12'} @@ -1096,6 +1164,10 @@ packages: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} + /assertion-error@1.1.0: + resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + dev: true + /astring@1.8.6: resolution: {integrity: sha512-ISvCdHdlTDlH5IpxQJIex7BWBywFWgjJSVdwst+/iQCoEYnyOaQ95+X1JGshuBjGp6nxKUy1jMgE3zPqN7fQdg==} hasBin: true @@ -1190,6 +1262,19 @@ packages: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} dev: false + /chai@4.4.1: + resolution: {integrity: sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==} + engines: {node: '>=4'} + dependencies: + assertion-error: 1.1.0 + check-error: 1.0.3 + deep-eql: 4.1.3 + get-func-name: 2.0.2 + loupe: 2.3.7 + pathval: 1.1.1 + type-detect: 4.0.8 + dev: true + /chalk@2.3.0: resolution: {integrity: sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==} engines: {node: '>=4'} @@ -1228,6 +1313,12 @@ packages: resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} dev: false + /check-error@1.0.3: + resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} + dependencies: + get-func-name: 2.0.2 + dev: true + /chokidar@3.5.3: resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} engines: {node: '>= 8.10.0'} @@ -1690,6 +1781,13 @@ packages: character-entities: 2.0.2 dev: false + /deep-eql@4.1.3: + resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==} + engines: {node: '>=6'} + dependencies: + type-detect: 4.0.8 + dev: true + /delaunator@5.0.0: resolution: {integrity: sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==} dependencies: @@ -1711,6 +1809,11 @@ packages: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} dev: false + /diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + /diff@5.1.0: resolution: {integrity: sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==} engines: {node: '>=0.3.1'} @@ -1886,7 +1989,6 @@ packages: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} dependencies: '@types/estree': 1.0.5 - dev: false /execa@0.8.0: resolution: {integrity: sha512-zDWS+Rb1E8BlqqhALSt9kUhss8Qq4nN3iof3gsOdyINksElaPyNBtKUMTR62qhvgVWR0CqCX7sdnKe4MnUbFEA==} @@ -1915,6 +2017,21 @@ packages: signal-exit: 3.0.7 strip-final-newline: 2.0.0 + /execa@8.0.1: + resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} + engines: {node: '>=16.17'} + dependencies: + cross-spawn: 7.0.3 + get-stream: 8.0.1 + human-signals: 5.0.0 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.2.0 + onetime: 6.0.0 + signal-exit: 4.1.0 + strip-final-newline: 3.0.0 + dev: true + /extend-shallow@2.0.1: resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} engines: {node: '>=0.10.0'} @@ -1999,6 +2116,10 @@ packages: engines: {node: '>=18'} dev: false + /get-func-name@2.0.2: + resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} + dev: true + /get-stream@3.0.0: resolution: {integrity: sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==} engines: {node: '>=4'} @@ -2008,6 +2129,11 @@ packages: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} + /get-stream@8.0.1: + resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} + engines: {node: '>=16'} + dev: true + /get-tsconfig@4.7.2: resolution: {integrity: sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==} dependencies: @@ -2265,6 +2391,11 @@ packages: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} + /human-signals@5.0.0: + resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} + engines: {node: '>=16.17.0'} + dev: true + /iconv-lite@0.6.3: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} @@ -2403,6 +2534,11 @@ packages: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} + /is-stream@3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true + /is-unicode-supported@1.3.0: resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} engines: {node: '>=12'} @@ -2455,7 +2591,6 @@ packages: /jsonc-parser@3.2.0: resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} - dev: false /jsonfile@6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} @@ -2510,6 +2645,14 @@ packages: resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + /local-pkg@0.5.0: + resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==} + engines: {node: '>=14'} + dependencies: + mlly: 1.5.0 + pkg-types: 1.0.3 + dev: true + /lodash-es@4.17.21: resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} dev: false @@ -2547,6 +2690,12 @@ packages: js-tokens: 4.0.0 dev: false + /loupe@2.3.7: + resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} + dependencies: + get-func-name: 2.0.2 + dev: true + /lru-cache@10.0.2: resolution: {integrity: sha512-Yj9mA8fPiVgOUpByoTZO5pNrcl5Yk37FcSHsUINpAsaBIEZIuqcCclDZJCVxqQShDsmYX8QG63svJiTbOATZwg==} engines: {node: 14 || >=16.14} @@ -2572,6 +2721,13 @@ packages: resolution: {integrity: sha512-lyWpfvNGVb5lu8YUAbER0+UMBTdR63w2mcSUlhhBTyVbxJvjgqwyAf3AZD6MprgK0uHuBoWXSDAMWLupX83o3Q==} dev: true + /magic-string@0.30.5: + resolution: {integrity: sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + /markdown-extensions@1.1.1: resolution: {integrity: sha512-WWC0ZuMzCyDHYCasEGs4IPvLyTGftYwh6wIEOULOF0HXcqZlhwRzrK0w2VUlxWA98xnvb/jszw4ZSkJ6ADpM6Q==} engines: {node: '>=0.10.0'} @@ -3213,6 +3369,11 @@ packages: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} + /mimic-fn@4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + dev: true + /minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} dependencies: @@ -3230,6 +3391,15 @@ packages: engines: {node: '>=16 || 14 >=14.17'} dev: false + /mlly@1.5.0: + resolution: {integrity: sha512-NPVQvAY1xr1QoVeG0cy8yUYC7FQcOx6evl/RjT1wL5FvzPnzOysoqB/jmx/DhssT2dYa8nxECLAaFI/+gVLhDQ==} + dependencies: + acorn: 8.11.3 + pathe: 1.1.2 + pkg-types: 1.0.3 + ufo: 1.3.2 + dev: true + /mri@1.2.0: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} @@ -3428,6 +3598,13 @@ packages: dependencies: path-key: 3.1.1 + /npm-run-path@5.2.0: + resolution: {integrity: sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + path-key: 4.0.0 + dev: true + /npm-to-yarn@2.1.0: resolution: {integrity: sha512-2C1IgJLdJngq1bSER7K7CGFszRr9s2rijEwvENPEgI0eK9xlD3tNwDc0UJnRj7FIT2aydWm72jB88uVswAhXHA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -3453,6 +3630,13 @@ packages: dependencies: mimic-fn: 2.1.0 + /onetime@6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + dependencies: + mimic-fn: 4.0.0 + dev: true + /ora@8.0.1: resolution: {integrity: sha512-ANIvzobt1rls2BDny5fWZ3ZVKyD6nscLvfFRpQgfWsythlcsVUC9kL0zq6j2Z5z9wwp1kd7wpsD/T9qNPVLCaQ==} engines: {node: '>=18'} @@ -3480,6 +3664,13 @@ packages: yocto-queue: 0.1.0 dev: false + /p-limit@5.0.0: + resolution: {integrity: sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==} + engines: {node: '>=18'} + dependencies: + yocto-queue: 1.0.0 + dev: true + /parse-entities@4.0.1: resolution: {integrity: sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==} dependencies: @@ -3528,6 +3719,11 @@ packages: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} + /path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + dev: true + /path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} dev: false @@ -3544,6 +3740,14 @@ packages: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} + /pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + dev: true + + /pathval@1.1.1: + resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + dev: true + /periscopic@3.1.0: resolution: {integrity: sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==} dependencies: @@ -3568,6 +3772,14 @@ packages: resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} engines: {node: '>= 6'} + /pkg-types@1.0.3: + resolution: {integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==} + dependencies: + jsonc-parser: 3.2.0 + mlly: 1.5.0 + pathe: 1.1.2 + dev: true + /postcss-import@15.1.0(postcss@8.4.33): resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} engines: {node: '>=14.0.0'} @@ -3651,6 +3863,15 @@ packages: hasBin: true dev: true + /pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/schemas': 29.6.3 + ansi-styles: 5.2.0 + react-is: 18.2.0 + dev: true + /property-information@6.4.0: resolution: {integrity: sha512-9t5qARVofg2xQqKtytzt+lZ4d1Qvj8t5B8fEwXK6qOfgRLgH/b13QlgEyDh033NOS31nXeFbYv7CLUDG1CeifQ==} dev: false @@ -3680,6 +3901,10 @@ packages: scheduler: 0.23.0 dev: false + /react-is@18.2.0: + resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} + dev: true + /react@18.2.0: resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} engines: {node: '>=0.10.0'} @@ -3946,13 +4171,16 @@ packages: vscode-textmate: 8.0.0 dev: false + /siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + dev: true + /signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} /signal-exit@4.1.0: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} - dev: false /sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} @@ -3992,6 +4220,14 @@ packages: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} dev: false + /stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + dev: true + + /std-env@3.7.0: + resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} + dev: true + /stdin-discarder@0.2.2: resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==} engines: {node: '>=18'} @@ -4064,6 +4300,17 @@ packages: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} + /strip-final-newline@3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} + dev: true + + /strip-literal@1.3.0: + resolution: {integrity: sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==} + dependencies: + acorn: 8.11.2 + dev: true + /style-to-object@0.4.4: resolution: {integrity: sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg==} dependencies: @@ -4165,6 +4412,10 @@ packages: dependencies: any-promise: 1.3.0 + /tinybench@2.6.0: + resolution: {integrity: sha512-N8hW3PG/3aOoZAN5V/NSAEDz0ZixDSSt5b/a05iqtpgfLWMSVuCo7w0k2vVvEjdrIoeGqZzweX2WlyioNIHchA==} + dev: true + /tinycolor2@1.6.0: resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==} dev: false @@ -4176,6 +4427,16 @@ packages: tinycolor2: 1.6.0 dev: false + /tinypool@0.8.2: + resolution: {integrity: sha512-SUszKYe5wgsxnNOVlBYO6IC+8VGWdVGZWAqUxp3UErNBtptZvWbwyUOyzNL59zigz2rCA92QiL3wvG+JDSdJdQ==} + engines: {node: '>=14.0.0'} + dev: true + + /tinyspy@2.2.0: + resolution: {integrity: sha512-d2eda04AN/cPOR89F7Xv5bK/jrQEhmcLFe6HFldoeO9AJtps+fqEnh486vnT/8y4bw38pSyxDcTCAq+Ks2aJTg==} + engines: {node: '>=14.0.0'} + dev: true + /title@3.5.3: resolution: {integrity: sha512-20JyowYglSEeCvZv3EZ0nZ046vLarO37prvV0mbtQV7C8DJPGgN967r8SJkqd3XK3K3lD3/Iyfp3avjfil8Q2Q==} hasBin: true @@ -4338,6 +4599,11 @@ packages: turbo-windows-arm64: 1.10.14 dev: true + /type-detect@4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + dev: true + /type-fest@1.4.0: resolution: {integrity: sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==} engines: {node: '>=10'} @@ -4348,6 +4614,10 @@ packages: engines: {node: '>=14.17'} hasBin: true + /ufo@1.3.2: + resolution: {integrity: sha512-o+ORpgGwaYQXgqGDwd+hkS4PuZ3QnmqMMxRuajK/a38L6fTpcE5GPIfrf+L/KemFzfUpeUQc1rRS1iDBozvnFA==} + dev: true + /undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} @@ -4572,6 +4842,120 @@ packages: vfile-message: 4.0.2 dev: false + /vite-node@1.2.1(@types/node@20.11.6): + resolution: {integrity: sha512-fNzHmQUSOY+y30naohBvSW7pPn/xn3Ib/uqm+5wAJQJiqQsU0NBR78XdRJb04l4bOFKjpTWld0XAfkKlrDbySg==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + dependencies: + cac: 6.7.14 + debug: 4.3.4 + pathe: 1.1.2 + picocolors: 1.0.0 + vite: 5.0.12(@types/node@20.11.6) + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + + /vite@5.0.12(@types/node@20.11.6): + resolution: {integrity: sha512-4hsnEkG3q0N4Tzf1+t6NdN9dg/L3BM+q8SWgbSPnJvrgH2kgdyzfVJwbR1ic69/4uMJJ/3dqDZZE5/WwqW8U1w==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 20.11.6 + esbuild: 0.19.12 + postcss: 8.4.33 + rollup: 4.9.6 + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /vitest@1.2.1(@types/node@20.11.6): + resolution: {integrity: sha512-TRph8N8rnSDa5M2wKWJCMnztCZS9cDcgVTQ6tsTFTG/odHJ4l5yNVqvbeDJYJRZ6is3uxaEpFs8LL6QM+YFSdA==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/node': ^18.0.0 || >=20.0.0 + '@vitest/browser': ^1.0.0 + '@vitest/ui': ^1.0.0 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + dependencies: + '@types/node': 20.11.6 + '@vitest/expect': 1.2.1 + '@vitest/runner': 1.2.1 + '@vitest/snapshot': 1.2.1 + '@vitest/spy': 1.2.1 + '@vitest/utils': 1.2.1 + acorn-walk: 8.3.2 + cac: 6.7.14 + chai: 4.4.1 + debug: 4.3.4 + execa: 8.0.1 + local-pkg: 0.5.0 + magic-string: 0.30.5 + pathe: 1.1.2 + picocolors: 1.0.0 + std-env: 3.7.0 + strip-literal: 1.3.0 + tinybench: 2.6.0 + tinypool: 0.8.2 + vite: 5.0.12(@types/node@20.11.6) + vite-node: 1.2.1(@types/node@20.11.6) + why-is-node-running: 2.2.2 + transitivePeerDependencies: + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + /vscode-oniguruma@1.7.0: resolution: {integrity: sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==} dev: false @@ -4612,6 +4996,15 @@ packages: dependencies: isexe: 2.0.0 + /why-is-node-running@2.2.2: + resolution: {integrity: sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==} + engines: {node: '>=8'} + hasBin: true + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + dev: true + /wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -4663,6 +5056,11 @@ packages: engines: {node: '>=10'} dev: false + /yocto-queue@1.0.0: + resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} + engines: {node: '>=12.20'} + dev: true + /zod@3.22.4: resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} dev: false From 2fd890f3e40882bdf4477f3b51dd2ca7a91465f8 Mon Sep 17 00:00:00 2001 From: twlite <46562212+twlite@users.noreply.github.com> Date: Fri, 26 Jan 2024 20:56:03 +0545 Subject: [PATCH 2/3] fix: update --- packages/commandkit/helpers/command-parser.ts | 75 +++++++-------- .../commandkit/spec/command-parser.spec.ts | 2 +- .../sample/(general)/ping/context-menu.ts | 0 .../sample/playlist/[operation]/command.ts | 0 .../commandkit/spec/snapshots/snapshot-1.json | 92 +++++++++++++++++++ 5 files changed, 128 insertions(+), 41 deletions(-) create mode 100644 packages/commandkit/spec/sample/(general)/ping/context-menu.ts create mode 100644 packages/commandkit/spec/sample/playlist/[operation]/command.ts create mode 100644 packages/commandkit/spec/snapshots/snapshot-1.json diff --git a/packages/commandkit/helpers/command-parser.ts b/packages/commandkit/helpers/command-parser.ts index d4fd417..a1af210 100644 --- a/packages/commandkit/helpers/command-parser.ts +++ b/packages/commandkit/helpers/command-parser.ts @@ -1,4 +1,4 @@ -import { dirname, join } from 'node:path'; +import { basename, dirname, join, resolve, sep } from 'node:path'; import { readdir } from 'node:fs/promises'; export interface CommandParserOptions { @@ -9,15 +9,15 @@ export interface CommandParserOptions { } export const enum CommandEntityKind { - Command, - ContextMenuCommand, - Category, - Subcommand, - DynamicCommand, - Validator, + Command = 'COMMAND', + CommandFile = 'COMMAND_FILE', + ContextMenuCommand = 'CONTEXT_MENU_COMMAND', + Category = 'CATEGORY', + Subcommand = 'SUBCOMMAND', + DynamicCommand = 'DYNAMIC_COMMAND', + Validator = 'VALIDATOR', } -const IGNORE_PATTERN = /^_/; const CATEGORY_PATTERN = /^\([a-z0-9-_]{1,}\)$/; const DYNAMIC_PATTERN = /^\[[a-z0-9-_]{1,}\]$/; @@ -52,16 +52,6 @@ export class CommandParser { return files; } - public map(): Map { - const map = new Map(); - - for (const command of this.#data) { - map.set(command.name, command); - } - - return map; - } - async #scanDir(dir: string, depth: number) { const files: CommandEntity[] = []; @@ -86,32 +76,35 @@ export class CommandParser { }); } else { const path = join(dir, dirent.name); - - const nearestCategory = - path - .split('/') - .reverse() - .find((e) => CATEGORY_PATTERN.test(e)) ?? null; + const pattern = /\([a-z0-9-_]{1,}\)/; + const nearestCategory = pattern.test(path) + ? path + .split('/') + .reverse() + .find((e) => pattern.test(e)) ?? null + : null; // ignoring category pattern, if path is nested more than once, it is a subcommand const isSubcommand = path - .replace(this.options.src, '') - .split('/') - .filter((e) => !IGNORE_PATTERN.test(e)).length > 1; + .replace(resolve(this.options.src), '') + .split(sep) + .filter((e) => e && !e.startsWith('_') && !e.startsWith('(')) + .length > 1; const isDynamic = DYNAMIC_PATTERN.test(dirent.name); files.push({ name: dirent.name, - kind: isSubcommand - ? CommandEntityKind.Subcommand - : isDynamic + kind: isDynamic ? CommandEntityKind.DynamicCommand + : isSubcommand + ? CommandEntityKind.Subcommand : CommandEntityKind.Command, path, children: [...(await this.#scanDir(path, depth - 1))], - category: nearestCategory, + category: + nearestCategory?.match(pattern)?.[0].replace(/\(|\)/g, '') ?? null, }); } } @@ -125,12 +118,12 @@ export class CommandParser { switch (name) { case 'command': - kind = CommandEntityKind.Command; + kind = CommandEntityKind.CommandFile; break; case 'validator': kind = CommandEntityKind.Validator; break; - case 'context': + case 'context-menu': kind = CommandEntityKind.ContextMenuCommand; break; default: @@ -139,18 +132,20 @@ export class CommandParser { if (kind === null) continue; - const nearestCategory = - fullPath - .split('/') - .reverse() - .find((e) => CATEGORY_PATTERN.test(e)) ?? null; + const pattern = /\([a-z0-9-_]{1,}\)/; + const nearestCategory = pattern.test(fullPath) + ? fullPath + .split('/') + .reverse() + .find((e) => pattern.test(e)) ?? null + : null; files.push({ - name: dirname(fullPath), + name: basename(dirname(fullPath)), path: fullPath, kind, children: [], - category: nearestCategory, + category: nearestCategory?.match(pattern)?.[0].replace(/\(|\)/g, '') ?? null, }); } } diff --git a/packages/commandkit/spec/command-parser.spec.ts b/packages/commandkit/spec/command-parser.spec.ts index eb93dfb..4d78f83 100644 --- a/packages/commandkit/spec/command-parser.spec.ts +++ b/packages/commandkit/spec/command-parser.spec.ts @@ -13,7 +13,7 @@ describe('CommandParser', () => { await parser.scan(); }); - test('should scan all files', () => { + test('should scan all files', async () => { expect(parser.getCommands().length).toBeGreaterThan(0); }); diff --git a/packages/commandkit/spec/sample/(general)/ping/context-menu.ts b/packages/commandkit/spec/sample/(general)/ping/context-menu.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/commandkit/spec/sample/playlist/[operation]/command.ts b/packages/commandkit/spec/sample/playlist/[operation]/command.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/commandkit/spec/snapshots/snapshot-1.json b/packages/commandkit/spec/snapshots/snapshot-1.json new file mode 100644 index 0000000..9ef99eb --- /dev/null +++ b/packages/commandkit/spec/snapshots/snapshot-1.json @@ -0,0 +1,92 @@ +[ + { + "name": "general", + "path": "~//commandkit/packages/commandkit/spec/sample/(general)", + "kind": "CATEGORY", + "children": [ + { + "name": "ping", + "kind": "COMMAND", + "path": "~//commandkit/packages/commandkit/spec/sample/(general)/ping", + "children": [ + { + "name": "ping", + "path": "~//commandkit/packages/commandkit/spec/sample/(general)/ping/command.ts", + "kind": "COMMAND_FILE", + "children": [], + "category": "general" + }, + { + "name": "ping", + "path": "~//commandkit/packages/commandkit/spec/sample/(general)/ping/context-menu.ts", + "kind": "CONTEXT_MENU_COMMAND", + "children": [], + "category": "general" + }, + { + "name": "ping", + "path": "~//commandkit/packages/commandkit/spec/sample/(general)/ping/validator.ts", + "kind": "VALIDATOR", + "children": [], + "category": "general" + } + ], + "category": "general" + }, + { + "name": "pong", + "kind": "COMMAND", + "path": "~//commandkit/packages/commandkit/spec/sample/(general)/pong", + "children": [ + { + "name": "test", + "kind": "SUBCOMMAND", + "path": "~//commandkit/packages/commandkit/spec/sample/(general)/pong/test", + "children": [ + { + "name": "test", + "path": "~//commandkit/packages/commandkit/spec/sample/(general)/pong/test/command.ts", + "kind": "COMMAND_FILE", + "children": [], + "category": "general" + } + ], + "category": "general" + }, + { + "name": "pong", + "path": "~//commandkit/packages/commandkit/spec/sample/(general)/pong/validator.ts", + "kind": "VALIDATOR", + "children": [], + "category": "general" + } + ], + "category": "general" + } + ], + "category": null + }, + { + "name": "playlist", + "kind": "COMMAND", + "path": "~//commandkit/packages/commandkit/spec/sample/playlist", + "children": [ + { + "name": "[operation]", + "kind": "DYNAMIC_COMMAND", + "path": "~//commandkit/packages/commandkit/spec/sample/playlist/[operation]", + "children": [ + { + "name": "[operation]", + "path": "~//commandkit/packages/commandkit/spec/sample/playlist/[operation]/command.ts", + "kind": "COMMAND_FILE", + "children": [], + "category": null + } + ], + "category": null + } + ], + "category": null + } +] \ No newline at end of file From 789795f55229225f0633b419ecf54d2c8126a00f Mon Sep 17 00:00:00 2001 From: twlite <46562212+twlite@users.noreply.github.com> Date: Wed, 31 Jan 2024 23:23:44 +0545 Subject: [PATCH 3/3] feat: cli v1 wip --- packages/commandkit/.gitignore | 2 + packages/commandkit/bin/common.mjs | 102 ----------- packages/commandkit/bin/development.mjs | 168 ------------------ packages/commandkit/bin/index.cjs | 3 + packages/commandkit/bin/index.mjs | 39 ---- packages/commandkit/bin/parse-env.mjs | 61 ------- packages/commandkit/bin/production.mjs | 83 --------- packages/commandkit/helpers/command-parser.ts | 155 ---------------- packages/commandkit/package.json | 16 +- .../commandkit/spec/command-parser.spec.ts | 34 ---- packages/commandkit/spec/main.spec.ts | 7 + .../spec/sample/(general)/ping/command.ts | 0 .../sample/(general)/ping/context-menu.ts | 0 .../spec/sample/(general)/ping/validator.ts | 0 .../sample/(general)/pong/test/command.ts | 0 .../spec/sample/(general)/pong/validator.ts | 0 .../commandkit/spec/sample/_test/command.ts | 0 .../sample/playlist/[operation]/command.ts | 0 .../commandkit/spec/snapshots/snapshot-1.json | 92 ---------- packages/commandkit/src/bootstrap/client.ts | 32 ++++ packages/commandkit/src/bootstrap/loadEnv.ts | 9 + packages/commandkit/src/config.ts | 13 +- .../src/environment/actions/development.ts | 68 +++++++ .../src/environment/bundler/bundle.ts | 39 ++++ .../environment/bundler/shims.ts} | 63 +------ packages/commandkit/src/environment/cli.ts | 32 ++++ .../src/environment/common/config.ts | 24 +++ .../src/environment/common/logger.ts | 20 +++ packages/commandkit/src/index.ts | 8 + packages/commandkit/tests/commandkit.mjs | 6 +- .../tests/src/{index.ts => client.ts} | 12 +- .../tests/src/commands/misc/count.ts | 4 +- .../tests/src/commands/misc/ping.ts | 2 +- .../tests/src/commands/misc/reload.ts | 2 +- .../tests/src/events/ready/console-log.ts | 2 +- .../tests/src/validations/devOnly.ts | 2 +- packages/commandkit/tsconfig.json | 4 +- packages/commandkit/tsup.config.ts | 5 +- pnpm-lock.yaml | 122 +++++++++++-- 39 files changed, 398 insertions(+), 833 deletions(-) delete mode 100644 packages/commandkit/bin/common.mjs delete mode 100644 packages/commandkit/bin/development.mjs create mode 100644 packages/commandkit/bin/index.cjs delete mode 100755 packages/commandkit/bin/index.mjs delete mode 100644 packages/commandkit/bin/parse-env.mjs delete mode 100644 packages/commandkit/bin/production.mjs delete mode 100644 packages/commandkit/helpers/command-parser.ts delete mode 100644 packages/commandkit/spec/command-parser.spec.ts create mode 100644 packages/commandkit/spec/main.spec.ts delete mode 100644 packages/commandkit/spec/sample/(general)/ping/command.ts delete mode 100644 packages/commandkit/spec/sample/(general)/ping/context-menu.ts delete mode 100644 packages/commandkit/spec/sample/(general)/ping/validator.ts delete mode 100644 packages/commandkit/spec/sample/(general)/pong/test/command.ts delete mode 100644 packages/commandkit/spec/sample/(general)/pong/validator.ts delete mode 100644 packages/commandkit/spec/sample/_test/command.ts delete mode 100644 packages/commandkit/spec/sample/playlist/[operation]/command.ts delete mode 100644 packages/commandkit/spec/snapshots/snapshot-1.json create mode 100644 packages/commandkit/src/bootstrap/client.ts create mode 100644 packages/commandkit/src/bootstrap/loadEnv.ts create mode 100644 packages/commandkit/src/environment/actions/development.ts create mode 100644 packages/commandkit/src/environment/bundler/bundle.ts rename packages/commandkit/{bin/build.mjs => src/environment/bundler/shims.ts} (58%) create mode 100644 packages/commandkit/src/environment/cli.ts create mode 100644 packages/commandkit/src/environment/common/config.ts create mode 100644 packages/commandkit/src/environment/common/logger.ts rename packages/commandkit/tests/src/{index.ts => client.ts} (52%) diff --git a/packages/commandkit/.gitignore b/packages/commandkit/.gitignore index 7332259..c94bca3 100644 --- a/packages/commandkit/.gitignore +++ b/packages/commandkit/.gitignore @@ -2,5 +2,7 @@ node_modules dist .DS_Store .vscode +!.env.example +.env.* .env .commandkit \ No newline at end of file diff --git a/packages/commandkit/bin/common.mjs b/packages/commandkit/bin/common.mjs deleted file mode 100644 index d717fd1..0000000 --- a/packages/commandkit/bin/common.mjs +++ /dev/null @@ -1,102 +0,0 @@ -// @ts-check - -import { rimrafSync } from 'rimraf'; -import { join } from 'node:path'; -import fs from 'node:fs'; - -const resetColor = '\x1b[0m'; - -export const Colors = { - reset: (text) => `${text}${resetColor}`, - bright: (text) => `\x1b[1m${text}${resetColor}`, - dim: (text) => `\x1b[2m${text}${resetColor}`, - underscore: (text) => `\x1b[4m${text}${resetColor}`, - blink: (text) => `\x1b[5m${text}${resetColor}`, - reverse: (text) => `\x1b[7m${text}${resetColor}`, - hidden: (text) => `\x1b[8m${text}${resetColor}`, - - black: (text) => `\x1b[30m${text}${resetColor}`, - red: (text) => `\x1b[31m${text}${resetColor}`, - green: (text) => `\x1b[32m${text}${resetColor}`, - yellow: (text) => `\x1b[33m${text}${resetColor}`, - blue: (text) => `\x1b[34m${text}${resetColor}`, - magenta: (text) => `\x1b[35m${text}${resetColor}`, - cyan: (text) => `\x1b[36m${text}${resetColor}`, - white: (text) => `\x1b[37m${text}${resetColor}`, - - bgBlack: (text) => `\x1b[40m${text}${resetColor}`, - bgRed: (text) => `\x1b[41m${text}${resetColor}`, - bgGreen: (text) => `\x1b[42m${text}${resetColor}`, - bgYellow: (text) => `\x1b[43m${text}${resetColor}`, - bgBlue: (text) => `\x1b[44m${text}${resetColor}`, - bgMagenta: (text) => `\x1b[45m${text}${resetColor}`, - bgCyan: (text) => `\x1b[46m${text}${resetColor}`, - bgWhite: (text) => `\x1b[47m${text}${resetColor}`, -}; - -export function write(message) { - process.stdout.write(message); - process.stdout.write('\n'); -} - -/** - * @returns {never} - */ -export function panic(message) { - write(Colors.red(`Error: ${message}`)); - process.exit(1); -} - -export function findPackageJSON() { - const cwd = process.cwd(); - const target = join(cwd, 'package.json'); - - if (!fs.existsSync(target)) { - panic('Could not find package.json in current directory.'); - } - - return JSON.parse(fs.readFileSync(target, 'utf8')); -} - -const possibleFileNames = [ - 'commandkit.json', - 'commandkit.config.json', - 'commandkit.js', - 'commandkit.config.js', - 'commandkit.mjs', - 'commandkit.config.mjs', - 'commandkit.cjs', - 'commandkit.config.cjs', -]; - -export async function findCommandKitConfig(src) { - const cwd = process.cwd(); - const locations = src ? [join(cwd, src)] : possibleFileNames.map((name) => join(cwd, name)); - - for (const location of locations) { - try { - return await loadConfigInner(location); - } catch (e) { - continue; - } - } - - panic('Could not locate commandkit config.'); -} - -async function loadConfigInner(target) { - const isJSON = target.endsWith('.json'); - - /** - * @type {import('..').CommandKitConfig} - */ - const config = await import(`file://${target}`, { - assert: isJSON ? { type: 'json' } : undefined, - }).then((conf) => conf.default || conf); - - return config; -} - -export function erase(dir) { - rimrafSync(dir); -} diff --git a/packages/commandkit/bin/development.mjs b/packages/commandkit/bin/development.mjs deleted file mode 100644 index 8f3f4be..0000000 --- a/packages/commandkit/bin/development.mjs +++ /dev/null @@ -1,168 +0,0 @@ -// @ts-check -import { config as dotenv } from 'dotenv'; -import { join } from 'node:path'; -import { build } from 'tsup'; -import { Colors, erase, findCommandKitConfig, panic, write } from './common.mjs'; -import { parseEnv } from './parse-env.mjs'; -import child_process from 'node:child_process'; -import ora from 'ora'; -import { injectShims } from './build.mjs'; - -const RESTARTING_MSG_PATTERN = /^Restarting '|".+'|"\n?$/; -const FAILED_RUNNING_PATTERN = /^Failed running '.+'|"\n?$/; - -export async function bootstrapDevelopmentServer(opts) { - const { - src, - main, - watch = Boolean(opts.noWatch), - nodeOptions = [], - envExtra = true, - clearRestartLogs = true, - outDir, - requirePolyfill, - } = await findCommandKitConfig(opts.config); - - if (!src) { - panic('Could not find src in commandkit.json'); - } - - if (!main) { - panic('Could not find main in commandkit.json'); - } - - const watchMode = watch; - const status = ora(Colors.green('Starting a development server...\n')).start(); - const start = performance.now(); - - if (watchMode && !nodeOptions.includes('--watch')) { - nodeOptions.push('--watch'); - } else if (!watchMode && nodeOptions.includes('--watch')) { - nodeOptions.splice(nodeOptions.indexOf('--watch'), 1); - } - - if (!nodeOptions.includes('--enable-source-maps')) { - nodeOptions.push('--enable-source-maps'); - } - - erase('.commandkit'); - - try { - await build({ - clean: true, - format: ['esm'], - dts: false, - skipNodeModulesBundle: true, - minify: false, - shims: true, - sourcemap: 'inline', - keepNames: true, - outDir: '.commandkit', - silent: true, - entry: [src, '!dist', '!.commandkit', `!${outDir}`].filter(Boolean), - watch: watchMode, - async onSuccess() { - return await injectShims('.commandkit', main, false, requirePolyfill); - }, - }); - - status.succeed( - Colors.green(`Dev server started in ${(performance.now() - start).toFixed(2)}ms!\n`), - ); - - if (watchMode) write(Colors.cyan('Watching for file changes...\n')); - - const processEnv = {}; - - const env = dotenv({ - path: join(process.cwd(), '.env'), - // @ts-expect-error - processEnv, - }); - - if (envExtra) { - parseEnv(processEnv); - } - - if (env.error) { - write(Colors.yellow(`[DOTENV] Warning: ${env.error.message}`)); - } - - if (env.parsed) { - write(Colors.blue('[DOTENV] Loaded .env file!')); - } - - /** - * @type {child_process.ChildProcessWithoutNullStreams} - */ - const ps = child_process.spawn( - 'node', - [...nodeOptions, join(process.cwd(), '.commandkit', main)], - { - env: { - ...process.env, - ...processEnv, - NODE_ENV: 'development', - // @ts-expect-error - COMMANDKIT_DEV: true, - // @ts-expect-error - COMMANDKIT_PRODUCTION: false, - }, - cwd: process.cwd(), - }, - ); - - let isLastLogRestarting = false, - hasStarted = false; - - ps.stdout.on('data', (data) => { - const message = data.toString(); - - if (FAILED_RUNNING_PATTERN.test(message)) { - write(Colors.cyan('Failed running the bot, waiting for changes...')); - isLastLogRestarting = false; - if (!hasStarted) hasStarted = true; - return; - } - - if (clearRestartLogs && !RESTARTING_MSG_PATTERN.test(message)) { - write(message); - isLastLogRestarting = false; - } else { - if (isLastLogRestarting || !hasStarted) { - if (!hasStarted) hasStarted = true; - return; - } - write(Colors.cyan('⌀ Restarting the bot...')); - isLastLogRestarting = true; - } - - if (!hasStarted) hasStarted = true; - }); - - ps.stderr.on('data', (data) => { - const message = data.toString(); - - if ( - message.includes( - 'ExperimentalWarning: Watch mode is an experimental feature and might change at any time', - ) - ) - return; - - write(Colors.red(message)); - }); - - ps.on('close', (code) => { - write('\n'); - process.exit(code ?? 0); - }); - - ps.on('error', (err) => { - panic(err); - }); - } catch (e) { - status.fail(`Error occurred after ${(performance.now() - start).toFixed(2)}ms!\n`); - panic(e.stack ?? e); - } -} diff --git a/packages/commandkit/bin/index.cjs b/packages/commandkit/bin/index.cjs new file mode 100644 index 0000000..ff47fee --- /dev/null +++ b/packages/commandkit/bin/index.cjs @@ -0,0 +1,3 @@ +#!/usr/bin/env node +require('source-map-support').install(); +import('commandkit/cli'); diff --git a/packages/commandkit/bin/index.mjs b/packages/commandkit/bin/index.mjs deleted file mode 100755 index 79e7d11..0000000 --- a/packages/commandkit/bin/index.mjs +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env node - -// @ts-check - -import { Command } from 'commander'; -import { bootstrapDevelopmentServer } from './development.mjs'; -import { bootstrapProductionServer } from './production.mjs'; -import { bootstrapProductionBuild } from './build.mjs'; - -const program = new Command('commandkit'); - -program - .command('dev') - .description('Start your bot in development mode.') - .option('-c, --config ', 'Path to your commandkit config file.', './commandkit.js') - .action(() => { - const options = program.opts(); - bootstrapDevelopmentServer(options); - }); - -program - .command('start') - .description('Start your bot in production mode after running the build command.') - .option('-c, --config ', 'Path to your commandkit.json file.', './commandkit.js') - .action(() => { - const options = program.opts(); - bootstrapProductionServer(options.config); - }); - -program - .command('build') - .description('Build your project for production usage.') - .option('-c, --config ', 'Path to your commandkit.json file.', './commandkit.json') - .action(() => { - const options = program.opts(); - bootstrapProductionBuild(options.config); - }); - -program.parse(); diff --git a/packages/commandkit/bin/parse-env.mjs b/packages/commandkit/bin/parse-env.mjs deleted file mode 100644 index 371b7f9..0000000 --- a/packages/commandkit/bin/parse-env.mjs +++ /dev/null @@ -1,61 +0,0 @@ -// @ts-check - -import { randomUUID } from 'node:crypto'; - -const valuesMap = { - true: true, - false: false, - null: null, - undefined: undefined, - __UUIDv4__: () => randomUUID(), -}; - -const VALUE_PREFIXES = { - JSON: 'JSON::', - DATE: 'DATE::', -}; - -function catcher(fn) { - try { - fn(); - return true; - } catch { - return false; - } -} - -export function parseEnv(src) { - for (const key in src) { - const value = src[key]; - - if (typeof value !== 'string') continue; - - if (value.startsWith(VALUE_PREFIXES.JSON)) { - catcher(() => (src[key] = JSON.parse(value.replace(VALUE_PREFIXES.JSON, '')))); - continue; - } - - if (value.startsWith(VALUE_PREFIXES.DATE)) { - src[key] = new Date(value.replace(VALUE_PREFIXES.DATE, '')); - continue; - } - - if (value.includes(',')) { - src[key] = value.split(',').map((v) => v.trim()); - continue; - } - - if (/^[0-9]+n$/.test(value)) { - src[key] = BigInt(value); - continue; - } - - if (value in valuesMap) { - src[key] = - typeof valuesMap[value] === 'function' ? valuesMap[value]() : valuesMap[value]; - continue; - } - } - - return src; -} diff --git a/packages/commandkit/bin/production.mjs b/packages/commandkit/bin/production.mjs deleted file mode 100644 index 2dc2163..0000000 --- a/packages/commandkit/bin/production.mjs +++ /dev/null @@ -1,83 +0,0 @@ -// @ts-check -import { config as dotenv } from 'dotenv'; -import { existsSync } from 'node:fs'; -import { join } from 'node:path'; -import { Colors, findCommandKitConfig, panic, write } from './common.mjs'; -import { parseEnv } from './parse-env.mjs'; -import child_process from 'node:child_process'; - -export async function bootstrapProductionServer(config) { - const { - main, - outDir = 'dist', - envExtra = true, - sourcemap, - } = await findCommandKitConfig(config); - - if (!existsSync(join(process.cwd(), outDir, main))) { - panic('Could not find production build, maybe run `commandkit build` first?'); - } - - try { - const processEnv = {}; - - const env = dotenv({ - path: join(process.cwd(), '.env'), - // @ts-expect-error - processEnv, - }); - - if (envExtra) { - parseEnv(processEnv); - } - - if (env.error) { - write(Colors.yellow(`[DOTENV] Warning: ${env.error.message}`)); - } - - if (env.parsed) { - write(Colors.blue('[DOTENV] Loaded .env file!')); - } - - /** - * @type {child_process.ChildProcessWithoutNullStreams} - */ - const ps = child_process.spawn( - 'node', - [sourcemap ? '--enable-source-maps' : '', join(process.cwd(), outDir, main)].filter( - Boolean, - ), - { - env: { - ...process.env, - ...processEnv, - NODE_ENV: 'production', - // @ts-expect-error - COMMANDKIT_DEV: false, - // @ts-expect-error - COMMANDKIT_PROD: true, - }, - cwd: process.cwd(), - }, - ); - - ps.stdout.on('data', (data) => { - write(data.toString()); - }); - - ps.stderr.on('data', (data) => { - write(Colors.red(data.toString())); - }); - - ps.on('close', (code) => { - write('\n'); - process.exit(code ?? 0); - }); - - ps.on('error', (err) => { - panic(err); - }); - } catch (e) { - panic(e); - } -} diff --git a/packages/commandkit/helpers/command-parser.ts b/packages/commandkit/helpers/command-parser.ts deleted file mode 100644 index a1af210..0000000 --- a/packages/commandkit/helpers/command-parser.ts +++ /dev/null @@ -1,155 +0,0 @@ -import { basename, dirname, join, resolve, sep } from 'node:path'; -import { readdir } from 'node:fs/promises'; - -export interface CommandParserOptions { - src: string; - filter: (name: string) => boolean; - maxDepth: number; - extensions: RegExp; -} - -export const enum CommandEntityKind { - Command = 'COMMAND', - CommandFile = 'COMMAND_FILE', - ContextMenuCommand = 'CONTEXT_MENU_COMMAND', - Category = 'CATEGORY', - Subcommand = 'SUBCOMMAND', - DynamicCommand = 'DYNAMIC_COMMAND', - Validator = 'VALIDATOR', -} - -const CATEGORY_PATTERN = /^\([a-z0-9-_]{1,}\)$/; -const DYNAMIC_PATTERN = /^\[[a-z0-9-_]{1,}\]$/; - -export interface CommandEntity { - name: string; - kind: CommandEntityKind; - path: string; - children: CommandEntity[]; - category: string | null; -} - -export class CommandParser { - #data: CommandEntity[] = []; - - public constructor(public readonly options: CommandParserOptions) {} - - public clear() { - this.#data = []; - } - - public getCommands() { - return this.#data; - } - - public async scan() { - const { src, maxDepth } = this.options; - - const files = await this.#scanDir(src, maxDepth); - - this.#data.push(...files); - - return files; - } - - async #scanDir(dir: string, depth: number) { - const files: CommandEntity[] = []; - - for await (const dirent of await readdir(dir, { withFileTypes: true })) { - if (!this.options.filter(dirent.name)) continue; - - if (dirent.isDirectory()) { - if (depth > 0) { - if (CATEGORY_PATTERN.test(dirent.name)) { - const name = dirent.name.replace(/\(|\)/g, ''); - const fullPath = join(dir, dirent.name); - - files.push({ - name, - path: fullPath, - kind: CommandEntityKind.Category, - children: (await this.#scanDir(fullPath, depth - 1)).map((e) => ({ - ...e, - category: name, - })), - category: null, - }); - } else { - const path = join(dir, dirent.name); - const pattern = /\([a-z0-9-_]{1,}\)/; - const nearestCategory = pattern.test(path) - ? path - .split('/') - .reverse() - .find((e) => pattern.test(e)) ?? null - : null; - - // ignoring category pattern, if path is nested more than once, it is a subcommand - const isSubcommand = - path - .replace(resolve(this.options.src), '') - .split(sep) - .filter((e) => e && !e.startsWith('_') && !e.startsWith('(')) - .length > 1; - - const isDynamic = DYNAMIC_PATTERN.test(dirent.name); - - files.push({ - name: dirent.name, - kind: isDynamic - ? CommandEntityKind.DynamicCommand - : isSubcommand - ? CommandEntityKind.Subcommand - : CommandEntityKind.Command, - path, - children: [...(await this.#scanDir(path, depth - 1))], - category: - nearestCategory?.match(pattern)?.[0].replace(/\(|\)/g, '') ?? null, - }); - } - } - } else { - if (!this.options.extensions.test(dirent.name)) continue; - - const fullPath = join(dir, dirent.name); - const name = dirent.name.replace(this.options.extensions, ''); - - let kind: CommandEntityKind | null = null; - - switch (name) { - case 'command': - kind = CommandEntityKind.CommandFile; - break; - case 'validator': - kind = CommandEntityKind.Validator; - break; - case 'context-menu': - kind = CommandEntityKind.ContextMenuCommand; - break; - default: - break; - } - - if (kind === null) continue; - - const pattern = /\([a-z0-9-_]{1,}\)/; - const nearestCategory = pattern.test(fullPath) - ? fullPath - .split('/') - .reverse() - .find((e) => pattern.test(e)) ?? null - : null; - - files.push({ - name: basename(dirname(fullPath)), - path: fullPath, - kind, - children: [], - category: nearestCategory?.match(pattern)?.[0].replace(/\(|\)/g, '') ?? null, - }); - } - } - - return files; - } -} diff --git a/packages/commandkit/package.json b/packages/commandkit/package.json index 706758d..94eb876 100644 --- a/packages/commandkit/package.json +++ b/packages/commandkit/package.json @@ -12,6 +12,11 @@ "require": "./dist/index.js", "import": "./dist/index.mjs", "types": "./dist/index.d.ts" + }, + "./cli": { + "require": "./dist/environment/cli.js", + "import": "./dist/environment/cli.mjs", + "types": "./dist/environment/cli.d.ts" } }, "files": [ @@ -19,7 +24,7 @@ "bin" ], "scripts": { - "lint": "tsc", + "lint": "tsc --noEmit", "dev": "tsup --watch", "build": "tsup", "deploy": "npm publish", @@ -41,16 +46,19 @@ "event handler" ], "dependencies": { - "commander": "^11.1.0", - "dotenv": "^16.4.1", + "dotenv-cra": "^3.0.3", "ora": "^8.0.1", "rfdc": "^1.3.1", "rimraf": "^5.0.5", - "tsup": "^8.0.1" + "source-map-support": "^0.5.21", + "tsup": "^8.0.1", + "yargs": "^17.7.2" }, "devDependencies": { "@types/node": "^20.11.6", + "@types/yargs": "^17.0.32", "discord.js": "^14.14.1", + "esbuild-plugin-version-injector": "^1.2.1", "tsconfig": "workspace:*", "tsx": "^4.7.0", "typescript": "^5.3.3", diff --git a/packages/commandkit/spec/command-parser.spec.ts b/packages/commandkit/spec/command-parser.spec.ts deleted file mode 100644 index 4d78f83..0000000 --- a/packages/commandkit/spec/command-parser.spec.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { describe, beforeAll, test, expect } from 'vitest'; -import { CommandEntityKind, CommandParser } from '../helpers/command-parser'; - -describe('CommandParser', () => { - const parser = new CommandParser({ - src: `${__dirname}/sample`, - filter: (name) => !name.startsWith('_'), - maxDepth: 5, - extensions: /\.(c|m)?(j|t)sx?$/, - }); - - beforeAll(async () => { - await parser.scan(); - }); - - test('should scan all files', async () => { - expect(parser.getCommands().length).toBeGreaterThan(0); - }); - - test('should contain general category', () => { - const cmds = parser.getCommands(); - const cat = cmds.find((c) => c.kind === CommandEntityKind.Category && c.name === 'general'); - - expect(cat?.name).toBe('general'); - }); - - test('general should contain commands', () => { - const cmds = parser.getCommands(); - const cat = cmds.find((c) => c.kind === CommandEntityKind.Category && c.name === 'general'); - - expect(cat?.children.length).toBeGreaterThan(0); - expect(cat?.children.map((m) => m.name)).toEqual(['ping', 'pong']); - }); -}); diff --git a/packages/commandkit/spec/main.spec.ts b/packages/commandkit/spec/main.spec.ts new file mode 100644 index 0000000..9d84ad4 --- /dev/null +++ b/packages/commandkit/spec/main.spec.ts @@ -0,0 +1,7 @@ +import { describe, test, expect } from 'vitest'; + +describe('commandkit', () => { + test('passes', () => { + expect(2 + 2).toBe(4); + }); +}); diff --git a/packages/commandkit/spec/sample/(general)/ping/command.ts b/packages/commandkit/spec/sample/(general)/ping/command.ts deleted file mode 100644 index e69de29..0000000 diff --git a/packages/commandkit/spec/sample/(general)/ping/context-menu.ts b/packages/commandkit/spec/sample/(general)/ping/context-menu.ts deleted file mode 100644 index e69de29..0000000 diff --git a/packages/commandkit/spec/sample/(general)/ping/validator.ts b/packages/commandkit/spec/sample/(general)/ping/validator.ts deleted file mode 100644 index e69de29..0000000 diff --git a/packages/commandkit/spec/sample/(general)/pong/test/command.ts b/packages/commandkit/spec/sample/(general)/pong/test/command.ts deleted file mode 100644 index e69de29..0000000 diff --git a/packages/commandkit/spec/sample/(general)/pong/validator.ts b/packages/commandkit/spec/sample/(general)/pong/validator.ts deleted file mode 100644 index e69de29..0000000 diff --git a/packages/commandkit/spec/sample/_test/command.ts b/packages/commandkit/spec/sample/_test/command.ts deleted file mode 100644 index e69de29..0000000 diff --git a/packages/commandkit/spec/sample/playlist/[operation]/command.ts b/packages/commandkit/spec/sample/playlist/[operation]/command.ts deleted file mode 100644 index e69de29..0000000 diff --git a/packages/commandkit/spec/snapshots/snapshot-1.json b/packages/commandkit/spec/snapshots/snapshot-1.json deleted file mode 100644 index 9ef99eb..0000000 --- a/packages/commandkit/spec/snapshots/snapshot-1.json +++ /dev/null @@ -1,92 +0,0 @@ -[ - { - "name": "general", - "path": "~//commandkit/packages/commandkit/spec/sample/(general)", - "kind": "CATEGORY", - "children": [ - { - "name": "ping", - "kind": "COMMAND", - "path": "~//commandkit/packages/commandkit/spec/sample/(general)/ping", - "children": [ - { - "name": "ping", - "path": "~//commandkit/packages/commandkit/spec/sample/(general)/ping/command.ts", - "kind": "COMMAND_FILE", - "children": [], - "category": "general" - }, - { - "name": "ping", - "path": "~//commandkit/packages/commandkit/spec/sample/(general)/ping/context-menu.ts", - "kind": "CONTEXT_MENU_COMMAND", - "children": [], - "category": "general" - }, - { - "name": "ping", - "path": "~//commandkit/packages/commandkit/spec/sample/(general)/ping/validator.ts", - "kind": "VALIDATOR", - "children": [], - "category": "general" - } - ], - "category": "general" - }, - { - "name": "pong", - "kind": "COMMAND", - "path": "~//commandkit/packages/commandkit/spec/sample/(general)/pong", - "children": [ - { - "name": "test", - "kind": "SUBCOMMAND", - "path": "~//commandkit/packages/commandkit/spec/sample/(general)/pong/test", - "children": [ - { - "name": "test", - "path": "~//commandkit/packages/commandkit/spec/sample/(general)/pong/test/command.ts", - "kind": "COMMAND_FILE", - "children": [], - "category": "general" - } - ], - "category": "general" - }, - { - "name": "pong", - "path": "~//commandkit/packages/commandkit/spec/sample/(general)/pong/validator.ts", - "kind": "VALIDATOR", - "children": [], - "category": "general" - } - ], - "category": "general" - } - ], - "category": null - }, - { - "name": "playlist", - "kind": "COMMAND", - "path": "~//commandkit/packages/commandkit/spec/sample/playlist", - "children": [ - { - "name": "[operation]", - "kind": "DYNAMIC_COMMAND", - "path": "~//commandkit/packages/commandkit/spec/sample/playlist/[operation]", - "children": [ - { - "name": "[operation]", - "path": "~//commandkit/packages/commandkit/spec/sample/playlist/[operation]/command.ts", - "kind": "COMMAND_FILE", - "children": [], - "category": null - } - ], - "category": null - } - ], - "category": null - } -] \ No newline at end of file diff --git a/packages/commandkit/src/bootstrap/client.ts b/packages/commandkit/src/bootstrap/client.ts new file mode 100644 index 0000000..444e9ac --- /dev/null +++ b/packages/commandkit/src/bootstrap/client.ts @@ -0,0 +1,32 @@ +import { Client } from 'discord.js'; +import { getConfig } from '../config'; + +let discord_client: Client; + +/** + * @internal + */ +export function getClient() { + return discord_client; +} + +/** + * Fetches the client instance. If the client instance is not initialized, an error will be thrown. + */ +export function client() { + if (!discord_client) { + throw new Error( + 'Client was not initialized. Make sure to run "commandkit dev" to bootstrap the client.', + ); + } + + return discord_client as Client; +} + +export function createClient() { + const config = getConfig(); + + discord_client = new Client(config.clientOptions); + + return discord_client; +} diff --git a/packages/commandkit/src/bootstrap/loadEnv.ts b/packages/commandkit/src/bootstrap/loadEnv.ts new file mode 100644 index 0000000..6c3c452 --- /dev/null +++ b/packages/commandkit/src/bootstrap/loadEnv.ts @@ -0,0 +1,9 @@ +import { config } from 'dotenv-cra'; + +export function loadEnv(type: 'development' | 'production') { + process.env.NODE_ENV = type; + + const result = config(); + + return result.error?.message; +} diff --git a/packages/commandkit/src/config.ts b/packages/commandkit/src/config.ts index 48f7287..127ce9d 100644 --- a/packages/commandkit/src/config.ts +++ b/packages/commandkit/src/config.ts @@ -1,12 +1,14 @@ +import type { ClientOptions } from 'discord.js'; + export interface CommandKitConfig { /** - * The source directory of the project. + * The Discord client options. */ - src: string; + clientOptions: ClientOptions; /** - * The main "javascript" file of the project. + * The Discord bot token. Defaults to `process.env.DISCORD_TOKEN`. */ - main: string; + token?: string; /** * Whether or not to use the watch mode. Defaults to `true`. */ @@ -55,13 +57,14 @@ let globalConfig: Partial = { nodeOptions: [], antiCrash: true, requirePolyfill: true, + token: process.env.DISCORD_TOKEN, }; export function getConfig(): CommandKitConfig { return globalConfig as CommandKitConfig; } -const requiredProps = ['src', 'main'] as const; +const requiredProps = ['clientOptions'] as const; type R = (typeof requiredProps)[number]; diff --git a/packages/commandkit/src/environment/actions/development.ts b/packages/commandkit/src/environment/actions/development.ts new file mode 100644 index 0000000..024dbff --- /dev/null +++ b/packages/commandkit/src/environment/actions/development.ts @@ -0,0 +1,68 @@ +import type yargs from 'yargs'; +import { findConfigPath, importConfig } from '../common/config'; +import colors from '../../utils/colors'; +import { Logger } from '../common/logger'; +import { loadEnv } from '../../bootstrap/loadEnv'; +import { createClient, getClient } from '../../bootstrap/client'; +import { bundle } from '../bundler/bundle'; + +const commandkitVersion = '[VI]{{inject}}[/VI]'; + +function printBanner() { + const banner = colors.magenta(`${String.fromCharCode(9670)} CommandKit ${commandkitVersion}`); + Logger.Log(banner); + Logger.Info('Initializing the development environment...'); +} + +export async function initializeDevelopmentEnvironment( + args: yargs.ArgumentsCamelCase<{ + config?: string | undefined; + nodeOptions?: string | undefined; + }>, +) { + printBanner(); + + if (getClient()) { + Logger.Fatal('The development environment is already initialized.'); + return; + } + + const envErr = loadEnv('development'); + + if (envErr) { + Logger.Warning('Failed to load .env', envErr); + } else { + Logger.Debug('Loaded .env'); + } + + const configPath = await findConfigPath(args.config ?? process.cwd()); + if (!configPath) { + const msg = `Could not locate the commandkit config file${ + args.config ? ' at ' + args.config : ' in the current working directory' + }.`; + Logger.Fatal(msg); + return; + } + + try { + var config = await importConfig(configPath); + Logger.Debug('Loaded config from', colors.yellow(configPath)); + } catch (e) { + Logger.Warning('Failed to load config'); + Logger.Fatal(colors.red((e as Error).stack ?? `${e}`)); + return; + } + + const client = createClient(); + + // build the project + const entrypoint = await bundle('development'); + + try { + // load the client entrypoint + await import(`file://${entrypoint}`); + await client.login(config.token); + } catch (e) { + Logger.Fatal('Failed to load the client entrypoint', e); + } +} diff --git a/packages/commandkit/src/environment/bundler/bundle.ts b/packages/commandkit/src/environment/bundler/bundle.ts new file mode 100644 index 0000000..2116e02 --- /dev/null +++ b/packages/commandkit/src/environment/bundler/bundle.ts @@ -0,0 +1,39 @@ +import { build } from 'tsup'; +import { getConfig } from '../../config'; +import { join } from 'path'; + +export function bundle(mode: 'development' | 'production') { + switch (mode) { + case 'development': + return buildDevelopment(); + default: + throw new Error('Not implemented'); + } +} + +function buildDevelopment() { + const { watch } = getConfig(); + + const outDir = join(process.cwd(), '.commandkit'); + + return build({ + clean: true, + format: ['esm'], + bundle: false, + dts: false, + skipNodeModulesBundle: true, + minify: false, + shims: true, + sourcemap: 'inline', + keepNames: true, + outDir: '.commandkit', + silent: true, + entry: ['src'], + watch, + async onSuccess() { + // return await injectShims('.commandkit', main, false, requirePolyfill); + }, + }).then(() => { + return join(outDir, 'client.mjs'); + }); +} diff --git a/packages/commandkit/bin/build.mjs b/packages/commandkit/src/environment/bundler/shims.ts similarity index 58% rename from packages/commandkit/bin/build.mjs rename to packages/commandkit/src/environment/bundler/shims.ts index 7c916c0..363165d 100644 --- a/packages/commandkit/bin/build.mjs +++ b/packages/commandkit/src/environment/bundler/shims.ts @@ -1,63 +1,12 @@ -// @ts-check - import { readFile, writeFile } from 'node:fs/promises'; import { join } from 'node:path'; -import { build } from 'tsup'; -import { Colors, erase, findCommandKitConfig, panic, write } from './common.mjs'; -import ora from 'ora'; - -export async function bootstrapProductionBuild(config) { - const { - sourcemap = false, - minify = false, - outDir = 'dist', - antiCrash = true, - src, - main, - requirePolyfill: polyfillRequire, - } = await findCommandKitConfig(config); - - const status = ora('Creating optimized production build...\n').start(); - const start = performance.now(); - - erase(outDir); - - try { - await build({ - clean: true, - format: ['esm'], - dts: false, - skipNodeModulesBundle: true, - minify, - shims: true, - banner: { - js: '/* Optimized production build generated by CommandKit */', - }, - sourcemap, - keepNames: true, - outDir, - silent: true, - watch: false, - entry: [src, '!dist', '!.commandkit', `!${outDir}`], - }); - - await injectShims(outDir, main, antiCrash, polyfillRequire); - - status.succeed( - Colors.green(`Build completed in ${(performance.now() - start).toFixed(2)}ms!`), - ); - write( - Colors.green( - `\nRun ${Colors.magenta(`commandkit start`)} ${Colors.green('to start your bot.')}`, - ), - ); - } catch (e) { - status.fail(`Build failed after ${(performance.now() - start).toFixed(2)}ms!`); - panic(e); - } -} -export async function injectShims(outDir, main, antiCrash, polyfillRequire) { +export async function injectShims( + outDir: string, + main: string, + antiCrash: boolean, + polyfillRequire: boolean, +) { const path = join(process.cwd(), outDir, main); const head = ['\n\n;await (async()=>{', " 'use strict';"].join('\n'); diff --git a/packages/commandkit/src/environment/cli.ts b/packages/commandkit/src/environment/cli.ts new file mode 100644 index 0000000..03d66fd --- /dev/null +++ b/packages/commandkit/src/environment/cli.ts @@ -0,0 +1,32 @@ +import yargs from 'yargs/yargs'; +import { hideBin } from 'yargs/helpers'; +import { initializeDevelopmentEnvironment } from './actions/development'; + +yargs(hideBin(process.argv)) + .scriptName('commandkit') + .command( + 'dev', + 'Start the bot in development mode', + () => {}, + async (args) => { + await initializeDevelopmentEnvironment(args); + }, + ) + .command('start', 'Start the production build') + .command('build', 'Generate production build of the project') + .options({ + config: { + alias: 'c', + type: 'string', + description: 'Path to the commandkit config file', + }, + nodeOptions: { + alias: 'n', + type: 'string', + description: 'Node options to pass to the process', + }, + }) + .version('[VI]{{inject}}[/VI]') + .help() + .demandCommand() + .parse(); diff --git a/packages/commandkit/src/environment/common/config.ts b/packages/commandkit/src/environment/common/config.ts new file mode 100644 index 0000000..97ba968 --- /dev/null +++ b/packages/commandkit/src/environment/common/config.ts @@ -0,0 +1,24 @@ +import { existsSync } from 'node:fs'; +import { join } from 'node:path'; +import type { CommandKitConfig } from '../../config'; + +const ConfigLookupPaths = [ + // javascript + 'js', + 'cjs', + 'mjs', +]; + +export async function findConfigPath(relative: string) { + for (const extension of ConfigLookupPaths) { + const path = join(relative, `commandkit.${extension}`); + if (existsSync(path)) return path; + } + + return null; +} + +export async function importConfig(path: string): Promise { + const config = await import(`file://${path}`); + return config?.default ?? config; +} diff --git a/packages/commandkit/src/environment/common/logger.ts b/packages/commandkit/src/environment/common/logger.ts new file mode 100644 index 0000000..aad3ec0 --- /dev/null +++ b/packages/commandkit/src/environment/common/logger.ts @@ -0,0 +1,20 @@ +import colors from '../../utils/colors'; + +export const Logger = { + Fatal: (...message: unknown[]) => { + console.log(colors.red('[FATAL ERROR]'), ...message); + process.exit(1); + }, + Warning: (...message: unknown[]) => { + console.log(colors.yellow('[WARNING]'), ...message); + }, + Info: (...message: unknown[]) => { + console.log(colors.green('[INFO]'), ...message); + }, + Debug: (...message: unknown[]) => { + console.log(colors.blue('[DEBUG]'), ...message); + }, + Log: (...message: unknown[]) => { + console.log(...message); + }, +}; diff --git a/packages/commandkit/src/index.ts b/packages/commandkit/src/index.ts index f6b1806..0e95629 100644 --- a/packages/commandkit/src/index.ts +++ b/packages/commandkit/src/index.ts @@ -3,4 +3,12 @@ export * from './components'; export * from './config'; export * from './utils/signal'; export * from './hooks'; +export { client } from './bootstrap/client'; export type * from './types'; + +/** + * The current version of CommandKit. + */ +// Note to developers: This needs to explicitly be `string` so it is not typed as a "const string" that gets injected by esbuild +// eslint-disable-next-line @typescript-eslint/no-inferrable-types +export const version: string = '[VI]{{inject}}[/VI]'; diff --git a/packages/commandkit/tests/commandkit.mjs b/packages/commandkit/tests/commandkit.mjs index c895b61..47744d3 100644 --- a/packages/commandkit/tests/commandkit.mjs +++ b/packages/commandkit/tests/commandkit.mjs @@ -3,6 +3,8 @@ import { defineConfig } from '../dist/index.mjs'; export default defineConfig({ - main: 'index.mjs', - src: 'src', + clientOptions: { + intents: ['Guilds', 'GuildMembers', 'GuildMessages', 'MessageContent'] + }, + token: process.env.DISCORD_TOKEN }); diff --git a/packages/commandkit/tests/src/index.ts b/packages/commandkit/tests/src/client.ts similarity index 52% rename from packages/commandkit/tests/src/index.ts rename to packages/commandkit/tests/src/client.ts index 49bfa7a..2c60bf7 100644 --- a/packages/commandkit/tests/src/index.ts +++ b/packages/commandkit/tests/src/client.ts @@ -1,13 +1,7 @@ -import { CommandKit } from '../../src/index'; -import { Client } from 'discord.js'; -// require('dotenv').config(); - -const client = new Client({ - intents: ['Guilds', 'GuildMembers', 'GuildMessages', 'MessageContent'], -}); +import { client, CommandKit } from '../../dist/index.mjs'; new CommandKit({ - client, + client: client(), commandsPath: `${__dirname}/commands`, eventsPath: `${__dirname}/events`, validationsPath: `${__dirname}/validations`, @@ -15,5 +9,3 @@ new CommandKit({ // devUserIds: process.env.DEV_USER_ID?.split(',') ?? [], bulkRegister: true, }); - -await client.login(process.env.TOKEN); diff --git a/packages/commandkit/tests/src/commands/misc/count.ts b/packages/commandkit/tests/src/commands/misc/count.ts index 0b211a6..f60400c 100644 --- a/packages/commandkit/tests/src/commands/misc/count.ts +++ b/packages/commandkit/tests/src/commands/misc/count.ts @@ -1,5 +1,5 @@ -import type { SlashCommandProps, CommandOptions, CommandData } from '../../../../src'; -import { ButtonKit, createEffect, createSignal } from '../../../../src/index'; +import type { SlashCommandProps, CommandOptions, CommandData } from '../../../../dist'; +import { ButtonKit, createEffect, createSignal } from '../../../../dist/index.mjs'; import { ButtonStyle, ActionRowBuilder, ButtonInteraction } from 'discord.js'; export const data: CommandData = { diff --git a/packages/commandkit/tests/src/commands/misc/ping.ts b/packages/commandkit/tests/src/commands/misc/ping.ts index ca58d67..fe8eaf3 100644 --- a/packages/commandkit/tests/src/commands/misc/ping.ts +++ b/packages/commandkit/tests/src/commands/misc/ping.ts @@ -5,7 +5,7 @@ import { CommandData, ButtonKit, AutocompleteProps, -} from '../../../../src/index'; +} from '../../../../dist/index.mjs'; export const data: CommandData = { name: 'ping', diff --git a/packages/commandkit/tests/src/commands/misc/reload.ts b/packages/commandkit/tests/src/commands/misc/reload.ts index 88446fd..20b1072 100644 --- a/packages/commandkit/tests/src/commands/misc/reload.ts +++ b/packages/commandkit/tests/src/commands/misc/reload.ts @@ -1,4 +1,4 @@ -import { SlashCommandProps, CommandOptions, CommandData } from '../../../../src/index'; +import { SlashCommandProps, CommandOptions, CommandData } from '../../../../dist/index.mjs'; export const data: CommandData = { name: 'reload', diff --git a/packages/commandkit/tests/src/events/ready/console-log.ts b/packages/commandkit/tests/src/events/ready/console-log.ts index dd20cd2..26349e7 100644 --- a/packages/commandkit/tests/src/events/ready/console-log.ts +++ b/packages/commandkit/tests/src/events/ready/console-log.ts @@ -1,5 +1,5 @@ import type { Client } from 'discord.js'; -import type { CommandKit } from '../../../../src'; +import type { CommandKit } from '../../../../dist'; export default function (c: Client, client, handler: CommandKit) { console.log(`${c.user.username} is online.`); diff --git a/packages/commandkit/tests/src/validations/devOnly.ts b/packages/commandkit/tests/src/validations/devOnly.ts index 82f80f6..c94a140 100644 --- a/packages/commandkit/tests/src/validations/devOnly.ts +++ b/packages/commandkit/tests/src/validations/devOnly.ts @@ -1,4 +1,4 @@ -import type { ValidationProps } from '../../../src'; +import type { ValidationProps } from '../../../dist'; export default function ({ interaction, commandObj, handler }: ValidationProps) { if (interaction.isAutocomplete()) return; diff --git a/packages/commandkit/tsconfig.json b/packages/commandkit/tsconfig.json index b687e43..9545a2b 100644 --- a/packages/commandkit/tsconfig.json +++ b/packages/commandkit/tsconfig.json @@ -1,7 +1,9 @@ { "extends": "tsconfig/base.json", "compilerOptions": { - "outDir": "dist" + "outDir": "dist", + "skipLibCheck": true, + "skipDefaultLibCheck": true }, "include": ["src/**/*.ts", "helpers/**/*.ts", "spec"], "exclude": ["node_modules"] diff --git a/packages/commandkit/tsup.config.ts b/packages/commandkit/tsup.config.ts index 4d07a8a..56f41f5 100644 --- a/packages/commandkit/tsup.config.ts +++ b/packages/commandkit/tsup.config.ts @@ -1,8 +1,10 @@ import { defineConfig } from 'tsup'; +import { esbuildPluginVersionInjector } from 'esbuild-plugin-version-injector'; export default defineConfig({ format: ['cjs', 'esm'], - entry: ['./src/index.ts'], + entry: ['./src'], + sourcemap: true, minifyIdentifiers: false, minifySyntax: true, minifyWhitespace: true, @@ -11,4 +13,5 @@ export default defineConfig({ shims: true, skipNodeModulesBundle: true, clean: true, + esbuildPlugins: [esbuildPluginVersionInjector()], }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 559d5e5..84cf293 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -56,12 +56,9 @@ importers: packages/commandkit: dependencies: - commander: - specifier: ^11.1.0 - version: 11.1.0 - dotenv: - specifier: ^16.4.1 - version: 16.4.1 + dotenv-cra: + specifier: ^3.0.3 + version: 3.0.3 ora: specifier: ^8.0.1 version: 8.0.1 @@ -71,16 +68,28 @@ importers: rimraf: specifier: ^5.0.5 version: 5.0.5 + source-map-support: + specifier: ^0.5.21 + version: 0.5.21 tsup: specifier: ^8.0.1 version: 8.0.1(typescript@5.3.3) + yargs: + specifier: ^17.7.2 + version: 17.7.2 devDependencies: '@types/node': specifier: ^20.11.6 version: 20.11.6 + '@types/yargs': + specifier: ^17.0.32 + version: 17.0.32 discord.js: specifier: ^14.14.1 version: 14.14.1 + esbuild-plugin-version-injector: + specifier: ^1.2.1 + version: 1.2.1 tsconfig: specifier: workspace:* version: link:../tsconfig @@ -838,6 +847,11 @@ packages: engines: {node: '>=v14.0.0', npm: '>=7.0.0'} dev: true + /@sapphire/result@2.6.6: + resolution: {integrity: sha512-QCjj7X/QlY0QUCeAaZQmnrsMH/b2BMQYee3F1Y5iF17JagUQqO3KZlG7vfXWQU3SRAJX5OgZZynBjixUH+nNGg==} + engines: {node: '>=v14.0.0', npm: '>=7.0.0'} + dev: true + /@sapphire/shapeshift@3.9.6: resolution: {integrity: sha512-4+Na/fxu2SEepZRb9z0dbsVh59QtwPuBg/UVaDib3av7ZY14b14+z09z6QVn0P6Dv6eOU2NDTsjIi0mbtgP56g==} engines: {node: '>=v18'} @@ -1018,6 +1032,16 @@ packages: '@types/node': 20.11.6 dev: true + /@types/yargs-parser@21.0.3: + resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} + dev: true + + /@types/yargs@17.0.32: + resolution: {integrity: sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==} + dependencies: + '@types/yargs-parser': 21.0.3 + dev: true + /@ungap/structured-clone@1.2.0: resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} dev: false @@ -1229,6 +1253,10 @@ packages: update-browserslist-db: 1.0.13(browserslist@4.22.2) dev: false + /buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + dev: false + /bundle-require@4.0.1(esbuild@0.19.12): resolution: {integrity: sha512-9NQkRHlNdNpDBGmLpngF3EFDcwodhMUuLz9PaWYciVcQF9SE4LFjM2DB/xV1Li5JiuDMv7ZUWuC3rGbqR0MAXQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -1357,6 +1385,15 @@ packages: execa: 0.8.0 dev: false + /cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + dev: false + /clsx@2.0.0: resolution: {integrity: sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==} engines: {node: '>=6'} @@ -1392,11 +1429,6 @@ packages: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} dev: false - /commander@11.1.0: - resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} - engines: {node: '>=16'} - dev: false - /commander@4.1.1: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} @@ -1860,9 +1892,21 @@ packages: resolution: {integrity: sha512-ilkD8YEnnGh1zJ240uJsW7AzE+2qpbOUYjacomn3AvJ6J4JhKGSZ2nh4wUIXPZrEPppaCLx5jFe8T89Rk8tQ7w==} dev: false - /dotenv@16.4.1: - resolution: {integrity: sha512-CjA3y+Dr3FyFDOAMnxZEGtnW9KBR2M0JvvUtXNW+dYJL5ROWxP9DUHCwgFqpMk0OXCc0ljhaNTr2w/kutYIcHQ==} + /dotenv-cra@3.0.3: + resolution: {integrity: sha512-p/EaxmpLem27Oy/z776GdYnWcHuwWJAzq1XJZvjhqqPPKAoFSn8/PuLradZIiGbX8P2Z5GY7PtswvLH1BYJibQ==} engines: {node: '>=12'} + dependencies: + dotenv: 10.0.0 + dotenv-expand: 5.1.0 + dev: false + + /dotenv-expand@5.1.0: + resolution: {integrity: sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==} + dev: false + + /dotenv@10.0.0: + resolution: {integrity: sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==} + engines: {node: '>=10'} dev: false /eastasianwidth@0.2.0: @@ -1894,6 +1938,13 @@ packages: engines: {node: '>=0.12'} dev: false + /esbuild-plugin-version-injector@1.2.1: + resolution: {integrity: sha512-OKrLqniWutFlv36lKm2lIhJS1L5hncmbC4NXWjEt2a4bcMuyZ5TcZ/idUpH1Vju5FVlGX122cYcRhkMxHlq5GA==} + engines: {node: '>=v14.0.0', npm: '>=7.0.0'} + dependencies: + '@sapphire/result': 2.6.6 + dev: true + /esbuild@0.19.12: resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==} engines: {node: '>=12'} @@ -2111,6 +2162,11 @@ packages: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} dev: false + /get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + dev: false + /get-east-asian-width@1.2.0: resolution: {integrity: sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==} engines: {node: '>=18'} @@ -4025,6 +4081,11 @@ packages: resolution: {integrity: sha512-7pXIJqJOq5tFgG1A2Zxti3Ht8jJF337m4sowbuHsW30ZnkQFnDzy9qBNhgzX8ZLW4+UBcXiiR7SwR6pokHsxiA==} dev: false + /require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + dev: false + /resolve-from@5.0.0: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} @@ -4201,6 +4262,18 @@ packages: resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} engines: {node: '>=0.10.0'} + /source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + dev: false + + /source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + dev: false + /source-map@0.7.4: resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} engines: {node: '>= 8'} @@ -5039,6 +5112,11 @@ packages: optional: true dev: true + /y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + dev: false + /yallist@2.1.2: resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} dev: false @@ -5051,6 +5129,24 @@ packages: resolution: {integrity: sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==} engines: {node: '>= 14'} + /yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + dev: false + + /yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + dependencies: + cliui: 8.0.1 + escalade: 3.1.1 + 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 + dev: false + /yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'}