feat: add tests [just strict static analysis]

This commit is contained in:
ThaUnknown 2025-06-08 22:43:41 +02:00
parent 1eef1f25fe
commit b985a265b0
No known key found for this signature in database
33 changed files with 296 additions and 115 deletions

39
.github/workflows/check.yml vendored Normal file
View file

@ -0,0 +1,39 @@
name: Check
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
check:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
name: Install pnpm
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 22.9
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Sync
run: pnpm run sync
- name: Lint
run: pnpm run lint
- name: Check GQL
run: pnpm run gql:check
- name: Check Svelte and TypeScript
run: pnpm run check

View file

@ -11,15 +11,6 @@ export default tseslint.config(
tsconfigRootDir: import.meta.dirname,
svelteConfig
}
},
rules: {
"svelte/no-useless-mustaches": [
"error",
{
"ignoreIncludesComment": false,
"ignoreStringEscape": true
}
]
}
}
)

View file

@ -1,4 +1,3 @@
// @ts-expect-error no types for this, that's fine
import { writeFileSync } from 'node:fs'
import { getIntrospectedSchema, minifyIntrospectionQuery } from '@urql/introspection'

View file

@ -1,6 +1,6 @@
{
"name": "ui",
"version": "6.3.50",
"version": "6.3.51",
"license": "BUSL-1.1",
"private": true,
"packageManager": "pnpm@9.14.4",
@ -8,12 +8,13 @@
"dev": "vite dev --open",
"build": "vite build",
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --threshold error --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check -threshold error --tsconfig ./tsconfig.json --watch",
"sync": "svelte-kit sync",
"check": "svelte-check --threshold error --tsconfig ./tsconfig.web.json",
"check:watch": "svelte-check -threshold error --tsconfig ./tsconfig.web.json --watch",
"lint": "eslint --quiet -c eslint.config.js",
"lint:fix": "eslint --quiet -c eslint.config.js --fix",
"gql:turbo": "node ./node_modules/gql.tada/bin/cli.js turbo",
"gql:check": "node ./node_modules/gql.tada/bin/cli.js check",
"gql:turbo": "node ./node_modules/gql.tada/bin/cli.js turbo -c ./tsconfig.web.json",
"gql:check": "node ./node_modules/gql.tada/bin/cli.js check -c ./tsconfig.web.json",
"gql:generate": "node --experimental-strip-types ./generateALIntrospection.ts"
},
"devDependencies": {
@ -27,7 +28,7 @@
"autoprefixer": "^10.4.21",
"bits-ui": "^0.22.0",
"cmdk-sv": "^0.0.19",
"eslint-config-standard-universal": "^1.0.6",
"eslint-config-standard-universal": "^1.0.8",
"gql.tada": "^1.8.10",
"hayase-extensions": "github:hayase-app/extensions",
"jassub": "^1.8.6",
@ -45,7 +46,7 @@
"@cloudflare/speedtest": "^1.4.1",
"@fontsource-variable/nunito": "^5.2.5",
"@prgm/sveltekit-progress-bar": "2.0.0",
"@thaunknown/web-irc": "^1.0.1",
"@thaunknown/web-irc": "^1.0.3",
"@urql/exchange-auth": "^2.2.1",
"@urql/exchange-graphcache": "^7.2.3",
"@urql/exchange-refocus": "^1.1.1",

View file

@ -18,8 +18,8 @@ importers:
specifier: 2.0.0
version: 2.0.0(@sveltejs/kit@2.21.0(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.19(terser@5.39.0)))(svelte@4.2.19)(vite@5.4.19(terser@5.39.0)))(svelte@4.2.19)
'@thaunknown/web-irc':
specifier: ^1.0.1
version: 1.0.1
specifier: ^1.0.3
version: 1.0.3
'@urql/exchange-auth':
specifier: ^2.2.1
version: 2.2.1(@urql/core@5.1.0(graphql@16.10.0))
@ -148,8 +148,8 @@ importers:
specifier: ^0.0.19
version: 0.0.19(svelte@4.2.19)
eslint-config-standard-universal:
specifier: ^1.0.6
version: 1.0.6(@typescript-eslint/parser@8.32.1(eslint@9.27.0(jiti@1.21.6))(typescript@5.8.3))(jiti@1.21.6)
specifier: ^1.0.8
version: 1.0.8(@typescript-eslint/parser@8.32.1(eslint@9.27.0(jiti@1.21.6))(typescript@5.8.3))(jiti@1.21.6)
gql.tada:
specifier: ^1.8.10
version: 1.8.10(@gql.tada/svelte-support@1.0.1(svelte@4.2.19)(typescript@5.8.3))(graphql@16.10.0)(typescript@5.8.3)
@ -656,8 +656,8 @@ packages:
'@thaunknown/simple-websocket@9.1.3':
resolution: {integrity: sha512-pf/FCJsgWtLJiJmIpiSI7acOZVq3bIQCpnNo222UFc8Ph1lOUOTpe6LoYhhiOSKB9GUaWJEVUtZ+sK1/aBgU5Q==}
'@thaunknown/web-irc@1.0.1':
resolution: {integrity: sha512-oP+mrvD2U7gSXHTfT77+A+i2YVT5jp4qCbCXLrNU9aFzXdJ0iRsBbdhdT/AgeB7Nf4O+SC/wVCnwhnAfUoo0Fg==}
'@thaunknown/web-irc@1.0.3':
resolution: {integrity: sha512-gzTDs6+sAfkpuEB1IbVwBfu5btEt2D/0RGP+PwUPITIOZjZOpZLjs3d4ipvzl9eq+76otuudopmIlSD0pUT4Mw==}
'@types/cookie@0.6.0':
resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==}
@ -1207,8 +1207,8 @@ packages:
peerDependencies:
eslint: '>=6.0.0'
eslint-config-standard-universal@1.0.6:
resolution: {integrity: sha512-QNyO5eC69udu7grO+INuGPETVmhodA5vg0u5WXX4tdesh7n6BpdMHUjXJnsW6N6466i6l7jFYrDyCn7nfN83nw==}
eslint-config-standard-universal@1.0.8:
resolution: {integrity: sha512-GYuMkU93hqkoYCsA+sRR0RlLTKCSuB2Yuzois/uWdaRq9Vu9U69gfvNlQLWU7Pkqcaw56Vwll9tTINVWTjl5Jg==}
eslint-import-resolver-node@0.3.9:
resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==}
@ -3012,7 +3012,7 @@ snapshots:
- supports-color
- utf-8-validate
'@thaunknown/web-irc@1.0.1':
'@thaunknown/web-irc@1.0.3':
dependencies:
grapheme-splitter: 1.0.4
streamx: 2.22.0
@ -3695,7 +3695,7 @@ snapshots:
eslint: 9.27.0(jiti@1.21.6)
semver: 7.7.2
eslint-config-standard-universal@1.0.6(@typescript-eslint/parser@8.32.1(eslint@9.27.0(jiti@1.21.6))(typescript@5.8.3))(jiti@1.21.6):
eslint-config-standard-universal@1.0.8(@typescript-eslint/parser@8.32.1(eslint@9.27.0(jiti@1.21.6))(typescript@5.8.3))(jiti@1.21.6):
dependencies:
'@stylistic/eslint-plugin': 4.2.0(eslint@9.27.0(jiti@1.21.6))(typescript@5.8.3)
eslint: 9.27.0(jiti@1.21.6)

View file

@ -10,7 +10,6 @@
import ChevronLeft from 'lucide-svelte/icons/chevron-left'
import ChevronRight from 'lucide-svelte/icons/chevron-right'
import Play from 'lucide-svelte/icons/play'
import { readable } from 'svelte/store'
import Pagination from './Pagination.svelte'
import { Button } from './ui/button'
@ -72,7 +71,7 @@
searchStore.set({ media, episode })
}
export let following = authAggregator.following(media.id) ?? readable(null)
export let following = authAggregator.following(media.id)
$: followerEntries = $following?.data?.Page?.mediaList?.filter(e => e?.user?.id !== authAggregator.id()) ?? []
</script>

View file

@ -1,7 +1,7 @@
import type { Viewer } from '$lib/modules/anilist/queries'
import type { ResultOf } from 'gql.tada'
export type ChatUser = Omit<NonNullable<ResultOf<typeof Viewer>['Viewer']>, 'id'> & { id: string | number }
export type ChatUser = Omit<NonNullable<ResultOf<typeof Viewer>['Viewer']>, 'id' | 'bannerImage' | 'about' | 'isFollowing' | 'isFollower' | 'donatorBadge' | 'options' | 'createdAt' | 'statistics'> & { id: string | number }
export interface ChatMessage {
message: string

View file

@ -8,7 +8,7 @@
type $$Props = CheckboxPrimitive.Props
type $$Events = CheckboxPrimitive.Events
let className: $$Props['class']
let className: $$Props['class'] = undefined
export let checked: $$Props['checked'] = false
export { className as class }
</script>

View file

@ -7,9 +7,9 @@
type $$Props = ContextMenuPrimitive.CheckboxItemProps
type $$Events = ContextMenuPrimitive.CheckboxItemEvents
let className: $$Props['class']
let className: $$Props['class'] = undefined
export { className as class }
export let checked: $$Props['checked']
export let checked: $$Props['checked'] = false
</script>
<ContextMenuPrimitive.CheckboxItem

View file

@ -5,9 +5,9 @@
type $$Props = ContextMenuPrimitive.ContentProps
let className: $$Props['class']
let className: $$Props['class'] = undefined
export let transition: $$Props['transition'] = flyAndScale
export let transitionConfig: $$Props['transitionConfig']
export let transitionConfig: $$Props['transitionConfig'] = undefined
export { className as class }
</script>

View file

@ -8,8 +8,8 @@
}
type $$Events = ContextMenuPrimitive.ItemEvents
let className: $$Props['class']
export let inset: $$Props['inset']
let className: $$Props['class'] = undefined
export let inset: $$Props['inset'] = undefined
export { className as class }
</script>

View file

@ -7,8 +7,8 @@
inset?: boolean
}
let className: $$Props['class']
export let inset: $$Props['inset']
let className: $$Props['class'] = undefined
export let inset: $$Props['inset'] = undefined
export { className as class }
</script>

View file

@ -3,7 +3,7 @@
type $$Props = ContextMenuPrimitive.RadioGroupProps
export let value: $$Props['value']
export let value: $$Props['value'] = undefined
</script>
<ContextMenuPrimitive.RadioGroup {...$$restProps} bind:value>

View file

@ -7,7 +7,7 @@
type $$Props = ContextMenuPrimitive.RadioItemProps
type $$Events = ContextMenuPrimitive.RadioItemEvents
let className: $$Props['class']
let className: $$Props['class'] = undefined
export let value: $$Props['value']
export { className as class }
</script>

View file

@ -5,7 +5,7 @@
type $$Props = ContextMenuPrimitive.SeparatorProps
let className: $$Props['class']
let className: $$Props['class'] = undefined
export { className as class }
</script>

View file

@ -5,7 +5,7 @@
type $$Props = HTMLAttributes<HTMLSpanElement>
let className: $$Props['class']
let className: $$Props['class'] = undefined
export { className as class }
</script>

View file

@ -5,9 +5,9 @@
type $$Props = ContextMenuPrimitive.SubContentProps
let className: $$Props['class']
let className: $$Props['class'] = undefined
export let transition: $$Props['transition'] = flyAndScale
export let transitionConfig: $$Props['transitionConfig']
export let transitionConfig: $$Props['transitionConfig'] = undefined
export { className as class }
</script>

View file

@ -9,8 +9,8 @@
}
type $$Events = ContextMenuPrimitive.SubTriggerEvents
let className: $$Props['class']
export let inset: $$Props['inset']
let className: $$Props['class'] = undefined
export let inset: $$Props['inset'] = undefined
export { className as class }
</script>

View file

@ -10,7 +10,7 @@ import Title from './drawer-title.svelte'
import Root from './drawer.svelte'
const Trigger = DrawerPrimitive.Trigger
const Portal = DrawerPrimitive.Portal
const Portal: typeof DrawerPrimitive.Portal = DrawerPrimitive.Portal
const Close = DrawerPrimitive.Close
export {

View file

@ -30,17 +30,17 @@
{#if options.type === 'string'}
<div class='space-y-2'>
<Label for={id} class='leading-[unset] grow font-bold'>{options.description}</Label>
<Input type='text' {id} placeholder={options.default} bind:value={$exopts[config.id].options[id]} />
<Input type='text' {id} placeholder={options.default} bind:value={$exopts[config.id]?.options[id]} />
</div>
{:else if options.type === 'number'}
<div class='space-y-2'>
<Label for={id} class='leading-[unset] grow font-bold'>{options.description}</Label>
<Input type='number' {id} placeholder={options.default} bind:value={$exopts[config.id].options[id]} />
<Input type='number' {id} placeholder={options.default} bind:value={$exopts[config.id]?.options[id]} />
</div>
{:else if options.type === 'boolean'}
<div class='flex items-center space-x-2'>
<Label for={id} class='leading-[unset] grow font-bold'>{options.description}</Label>
<Switch {id} bind:checked={$exopts[config.id].options[id]} />
<Switch {id} bind:checked={$exopts[config.id]?.options[id]} />
</div>
{/if}
{/each}
@ -55,6 +55,6 @@
</Dialog.Content>
</Dialog.Root>
{#if $exopts[config.id]}
<Switch bind:checked={$exopts[config.id].enabled} hideState={true} />
<Switch bind:checked={$exopts[config.id]?.enabled} hideState={true} />
{/if}
</div>

View file

@ -5,7 +5,7 @@
type $$Props = LabelPrimitive.Props
let className: $$Props['class']
let className: $$Props['class'] = undefined
export { className as class }
</script>

View file

@ -9,6 +9,7 @@
import { cn } from '$lib/utils.js'
// eslint-disable-next-line no-undef
type T = $$Generic<'single' | 'multiple'>
type $$Props = ToggleGroupPrimitive.Props<T> & VariantProps<typeof toggleVariants>

View file

@ -10,8 +10,19 @@ import Worker from './worker?worker'
import type extensionLoader from './worker'
import type { ExtensionConfig } from 'hayase-extensions'
export const saved = persisted<Record<string, ExtensionConfig>>('extensions', {})
export const options = persisted<Record<string, {options: Record<string, string | number | boolean | undefined>, enabled: boolean}>>('extensionoptions', {})
type SavedExtensions = Record<ExtensionConfig['id'], ExtensionConfig>
type ExtensionsOptions = {
[K in keyof SavedExtensions]: {
// this is bad, but w/e
options: Record<string, never>
enabled: boolean
}
}
// Usage:
export const saved = persisted<SavedExtensions>('extensions', {})
export const options = persisted<ExtensionsOptions>('extensionoptions', {})
// `http${string}` | `gh:${string}` | `npm:${string}`
// http[s]://[url] -> http[s]://[url]

View file

@ -51,7 +51,7 @@ type IRCEvents = {
}
function ircUserToChatUser ({ id, pfpid, type, nick }: IRCChatUser): ChatUser {
return { id, avatar: { medium: getPFP({ id, pfpid, type }) }, name: nick, mediaListOptions: null }
return { id, avatar: { large: getPFP({ id, pfpid, type }) }, name: nick, mediaListOptions: null }
}
function ircIdentToChatUser (user: IRCUser): ChatUser {

13
src/types/fs.d.ts vendored Normal file
View file

@ -0,0 +1,13 @@
declare module 'fs' {
export const writeFileSync: (
file: string | URL,
data: string | Uint8Array,
options?: { encoding?: string, mode?: number, flag?: string }
) => void
}
declare module 'node:fs' {
// eslint-disable-next-line @typescript-eslint/no-require-imports
import path = require('fs')
export = path
}

34
src/types/module.d.ts vendored Normal file
View file

@ -0,0 +1,34 @@
/* eslint-disable @typescript-eslint/method-signature-style */
/* eslint-disable @typescript-eslint/naming-convention */
/* eslint-disable no-var */
declare module 'module' {
var __dirname: string
var __filename: string
global {
interface ImportMeta {
dirname: string
filename: string
url: string
resolve(specifier: string, parent?: string | URL): string
}
}
}
declare global {
var __dirname: string
var __filename: string
interface ImportMeta {
dirname: string
filename: string
url: string
resolve(specifier: string, parent?: string | URL): string
}
}
declare module 'node:module' {
// eslint-disable-next-line @typescript-eslint/no-require-imports
import module = require('module')
export = module
}

9
src/types/path.d.ts vendored Normal file
View file

@ -0,0 +1,9 @@
declare module 'path' {
export function resolve(...paths: string[]): string
}
declare module 'node:path' {
// eslint-disable-next-line @typescript-eslint/no-require-imports
import path = require('path')
export = path
}

9
src/types/process.d.ts vendored Normal file
View file

@ -0,0 +1,9 @@
declare module 'process' {
export const env: Record<string, string | undefined>
}
declare module 'node:process' {
// eslint-disable-next-line @typescript-eslint/no-require-imports
import path = require('process')
export = path
}

View file

@ -5,6 +5,7 @@ import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
/** @type {import('@sveltejs/kit').Config} */
const config = {
// @ts-expect-error no typedef for this
onwarn: (warning, handler) => {
if (warning.code === 'a11y_media_has_caption') return
if (warning.code === 'element_invalid_self_closing_tag') return
@ -17,7 +18,7 @@ const config = {
name: process.env.npm_package_version
},
alias: {
"lucide-svelte/dist/Icon.svelte": "./node_modules/lucide-svelte/dist/Icon.svelte"
'lucide-svelte/dist/Icon.svelte': './node_modules/lucide-svelte/dist/Icon.svelte'
}
},
runtime: ''

View file

@ -1,63 +1,22 @@
{
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
"target": "ESNext",
"module": "esnext",
"lib": [
"esnext",
"DOM",
"DOM.Iterable"
],
"allowJs": true,
"checkJs": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"rewriteRelativeImportExtensions": true,
"emitDeclarationOnly": false,
"noImplicitAny": true,
"strictNullChecks": true,
"verbatimModuleSyntax": true,
"isolatedModules": true,
"noEmit": true,
"noUncheckedIndexedAccess": true,
"declaration": true,
"alwaysStrict": true,
"strict": true,
"maxNodeModuleJsDepth": 3,
"allowSyntheticDefaultImports": true,
"plugins": [
{
"name": "gql.tada/ts-plugin",
"schema": "https://graphql.anilist.co",
"tadaOutputLocation": "./src/lib/modules/anilist/graphql-env.d.ts",
"tadaTurboLocation": "./src/lib/modules/anilist/graphql-turbo.d.ts"
}
],
"typeRoots": [
// these overrides are required, because we want a custom typed eventemitter, importing node types in any fashion will fully override the typed event emitter, making life a pain
// disabling type acquisition does NOT prevent type acquisition from working, WE LOVE TYPESCRIPT, INDUSTRY LEADING TECHNOLOGY
"./src/types"
]
},
"typeAcquisition": {
"enable": false
},
"files": [
"src/service-worker/index.ts"
],
"include": [
"src/service-worker/index.ts",
"src/**/*.js",
"src/**/*.cjs",
"src/**/*.ts",
"src/**/*.d.ts",
"src/**/*.svelte"
],
"exclude": [
"../node_modules/**"
"files": [],
"references": [
{
"path": "./tsconfig.node.json"
},
{
"path": "./tsconfig.web.json"
}
]
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
// except $lib which is handled by https://kit.svelte.dev/docs/configuration#files

50
tsconfig.node.json Normal file
View file

@ -0,0 +1,50 @@
{
"compilerOptions": {
"target": "ESNext",
"module": "esnext",
"lib": [
"esnext",
"DOM",
"DOM.Iterable"
],
"allowJs": true,
"checkJs": true,
"composite": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"rewriteRelativeImportExtensions": true,
"emitDeclarationOnly": false,
"noImplicitAny": true,
"strictNullChecks": true,
"verbatimModuleSyntax": true,
"isolatedModules": true,
"declaration": true,
"noUncheckedIndexedAccess": true,
"alwaysStrict": true,
"strict": true,
"outDir": "./dist",
"maxNodeModuleJsDepth": 3,
"allowSyntheticDefaultImports": true
},
"typeAcquisition": {
"enable": false
},
"include": [
"eslint.config.js",
"vite.config.ts",
"svelte.config.js",
"tailwind.config.ts",
"generateALIntrospection.ts",
"src/types/**/*.d.ts",
"postcss.config.js",
"tsconfig.web.json"
],
"exclude": [
"../node_modules/**"
]
}

65
tsconfig.web.json Normal file
View file

@ -0,0 +1,65 @@
{
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
"target": "ESNext",
"module": "esnext",
"lib": [
"esnext",
"DOM",
"DOM.Iterable"
],
"allowJs": true,
"checkJs": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"rewriteRelativeImportExtensions": true,
"emitDeclarationOnly": false,
"noImplicitAny": true,
"strictNullChecks": true,
"verbatimModuleSyntax": true,
"isolatedModules": true,
"noUncheckedIndexedAccess": true,
"declaration": true,
"alwaysStrict": true,
"strict": true,
"noEmit": false,
"outDir": "./dist",
"maxNodeModuleJsDepth": 3,
"allowSyntheticDefaultImports": true,
"composite": true,
"plugins": [
{
"name": "gql.tada/ts-plugin",
"schema": "https://graphql.anilist.co",
"tadaOutputLocation": "./src/lib/modules/anilist/graphql-env.d.ts",
"tadaTurboLocation": "./src/lib/modules/anilist/graphql-turbo.d.ts"
}
],
"typeRoots": [
"./src/types"
]
},
"typeAcquisition": {
"enable": false
},
"files": [
"src/service-worker/index.ts"
],
"include": [
"src/service-worker/index.ts",
"src/**/*.js",
"src/**/*.cjs",
"src/**/*.ts",
"src/**/*.d.ts",
"src/**/*.json",
"src/**/*.svelte"
],
"exclude": [
"../node_modules/**"
]
}

View file

@ -10,7 +10,7 @@ export default defineConfig({
license({
thirdParty: {
allow: '(MIT OR Apache-2.0 OR ISC OR BSD-3-Clause OR BSD-2-Clause)',
output: resolve(__dirname, './build/LICENSE.txt'),
output: resolve(import.meta.dirname, './build/LICENSE.txt'),
includeSelf: true
}
})
@ -18,13 +18,13 @@ export default defineConfig({
resolve: {
alias: {
// thank you bottleneck for importing useless modules
'./RedisConnection': resolve(__dirname, 'src/patches/empty.cjs'),
'./RedisConnection.js': resolve(__dirname, 'src/patches/empty.cjs'),
'./RedisDatastore': resolve(__dirname, 'src/patches/empty.cjs'),
'./IORedisConnection': resolve(__dirname, 'src/patches/empty.cjs'),
'./Scripts': resolve(__dirname, 'src/patches/empty.cjs'),
'./RedisConnection': resolve(import.meta.dirname, 'src/patches/empty.cjs'),
'./RedisConnection.js': resolve(import.meta.dirname, 'src/patches/empty.cjs'),
'./RedisDatastore': resolve(import.meta.dirname, 'src/patches/empty.cjs'),
'./IORedisConnection': resolve(import.meta.dirname, 'src/patches/empty.cjs'),
'./Scripts': resolve(import.meta.dirname, 'src/patches/empty.cjs'),
// no exports :/
'bittorrent-tracker/lib/client/websocket-tracker.js': resolve(__dirname, 'node_modules/bittorrent-tracker/lib/client/websocket-tracker.js'),
'bittorrent-tracker/lib/client/websocket-tracker.js': resolve(import.meta.dirname, 'node_modules/bittorrent-tracker/lib/client/websocket-tracker.js')
}
},
server: { port: 7344 },