diff --git a/.eslintrc.cjs b/.eslintrc.cjs deleted file mode 100644 index ad835aa9..00000000 --- a/.eslintrc.cjs +++ /dev/null @@ -1,54 +0,0 @@ -/* eslint-disable unicorn/prefer-module */ -module.exports = { - root: true, - parser: "@typescript-eslint/parser", - plugins: [ - "@typescript-eslint", - "import", - "jsx-a11y", - "prettier", - "promise", - "react-hooks", - "react", - "unicorn", - ], - extends: [ - "plugin:@typescript-eslint/recommended", - "plugin:import/errors", - "plugin:import/typescript", - "plugin:import/warnings", - "plugin:jsx-a11y/recommended", - "plugin:prettier/recommended", - "plugin:promise/recommended", - "plugin:react-hooks/recommended", - "plugin:react/recommended", - "plugin:unicorn/recommended", - ], - settings: { - react: { version: "detect" }, - "import/resolver": { typescript: {} }, - }, - rules: { - "@typescript-eslint/explicit-module-boundary-types": "off", - "no-unused-vars": "off", - "no-var": "off", - "prefer-const": "off", - "react/prop-types": "off", - "react/button-has-type": "error", - "react/function-component-definition": [ - "error", - { - namedComponents: "function-declaration", - unnamedComponents: "arrow-function", - }, - ], - "react/jsx-uses-react": "off", - "react/react-in-jsx-scope": "off", - "react-hooks/rules-of-hooks": "error", - "react-hooks/exhaustive-deps": "error", - "unicorn/filename-case": "off", - "unicorn/no-null": "off", - "unicorn/prefer-node-protocol": "off", - "unicorn/prevent-abbreviations": "off", - }, -}; diff --git a/.github/workflows/bump.yml b/.github/workflows/bump.yml index a9c9fdbe..9bd0356e 100644 --- a/.github/workflows/bump.yml +++ b/.github/workflows/bump.yml @@ -4,13 +4,13 @@ on: workflow_dispatch: inputs: version: - description: 'Semver type of new version (major / minor / patch)' + description: "Semver type of new version (major / minor / patch)" required: true type: choice options: - - major - - minor - - patch + - major + - minor + - patch jobs: bump-version: @@ -22,7 +22,7 @@ jobs: ssh-key: ${{ secrets.DEPLOY_KEY }} - uses: oven-sh/setup-bun@v1 - - run: bun install + - run: bun install --frozen-lockfile - uses: actions/setup-node@v4 with: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c6280914..5d278993 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: steps: - uses: actions/checkout@v4 - uses: oven-sh/setup-bun@v1 - - run: bun install + - run: bun install --frozen-lockfile - run: bun run build exports: @@ -22,7 +22,7 @@ jobs: steps: - uses: actions/checkout@v4 - uses: oven-sh/setup-bun@v1 - - run: bun install + - run: bun install --frozen-lockfile - run: bun run test:exports typecheck: @@ -31,23 +31,23 @@ jobs: steps: - uses: actions/checkout@v4 - uses: oven-sh/setup-bun@v1 - - run: bun install + - run: bun install --frozen-lockfile - run: bun run typecheck test: - name: Unit and Integration Tests + name: Tests runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: oven-sh/setup-bun@v1 - - run: bun install + - run: bun install --frozen-lockfile - run: bun run test - lint: - name: Linter + quality: + name: Code Quality runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: oven-sh/setup-bun@v1 - - run: bun install - - run: bun run lint + - run: bun install --frozen-lockfile + - run: bun run check diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 32aa6016..4ab85671 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -23,7 +23,7 @@ jobs: steps: - uses: actions/checkout@v4 - uses: oven-sh/setup-bun@v1 - - run: bun install ----frozen-lockfile && bunx typedoc + - run: bun install --frozen-lockfile && bunx typedoc - uses: actions/configure-pages@v5 - uses: actions/upload-pages-artifact@v3 with: diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index bd00783d..594f6e13 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -11,7 +11,7 @@ jobs: steps: - uses: actions/checkout@v4 - uses: oven-sh/setup-bun@v1 - - run: bun install + - run: bun install --frozen-lockfile - run: bun run build - run: bun run test:exports diff --git a/.npmignore b/.npmignore index 9a1dfa2b..d1955704 100644 --- a/.npmignore +++ b/.npmignore @@ -6,8 +6,8 @@ .gitignore .npmignore .npmrc +biome.json bun.lockb -prettier.config.js tsconfig.json vitest.config.ts vitest.setup.ts \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..e1216880 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,14 @@ +{ + "recommendations": [ + "bierner.jsdoc-markdown-highlighting", + "biomejs.biome", + "bradlc.vscode-tailwindcss", + "csstools.postcss", + "formulahendry.auto-close-tag", + "formulahendry.auto-rename-tag", + "github.vscode-github-actions", + "vitest.explorer", + "vscode-icons-team.vscode-icons", + "waderyan.gitblame" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..bb5495b7 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,70 @@ +{ + "[javascript]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[javascriptreact]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[json]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[jsonc]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[typescript]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[typescriptreact]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[markdown]": { + "editor.wordWrap": "on" + }, + "editor.codeActionsOnSave": { + "source.organizeImports.biome": "explicit", + "quickfix.biome": "explicit" + }, + "editor.formatOnPaste": true, + "editor.formatOnSave": true, + "editor.inlineSuggest.enabled": true, + "editor.multiCursorModifier": "ctrlCmd", + "editor.renderWhitespace": "all", + "editor.tabSize": 2, + "editor.wordWrap": "wordWrapColumn", + "editor.wordWrapColumn": 80, + "files.associations": { + "*.js": "javascriptreact", + "*.ts": "typescriptreact" + }, + "files.defaultLanguage": "markdown", + "tailwindCSS.classAttributes": ["class", "className", "cn", "tv"], + "workbench.editor.customLabels.enabled": true, + "workbench.editor.customLabels.patterns": { + "**/.github/workflows/*.yml": "${filename} - GitHub Action", + "**/app/**/*.stories.tsx": "${dirname}/${filename} - Story", + "**/app/**/*.test.ts": "${dirname}/${filename} - Unit Test", + "**/app/**/*.test.tsx": "${dirname}/${filename} - Component Test", + "**/app/components/**/*.tsx": "${dirname}/${filename} - Component", + "**/app/entry.client.tsx": "${dirname}/${filename} - Entry Client", + "**/app/entry.server.tsx": "${dirname}/${filename} - Entry Server", + "**/app/helpers/**/*.ts": "${dirname}/${filename} - Helper", + "**/app/helpers/**/*.tsx": "${dirname}/${filename} - Helper", + "**/app/hooks/**/*.ts": "${dirname}/${filename} - Hook", + "**/app/hooks/**/*.tsx": "${dirname}/${filename} - Hook", + "**/app/modules/**/*.ts": "${filename} - Module", + "**/app/routes/*.ts": "${dirname}/${filename} - Resource Route", + "**/app/routes/*.tsx": "${dirname}/${filename} - UI Route", + "**/app/routes/**/route.ts": "${dirname} - Resource Route", + "**/app/routes/**/route.tsx": "${dirname} - UI Route", + "**/app/ui/**/*.tsx": "${dirname}/${filename} - UI Kit", + "**/config/*.ts": "${filename} - Config", + "**/db/migrations/*.sql": "${filename} - Migration", + "**/db/schema.ts": "Database Schema", + "**/e2e/**/*.test.ts": "${filename} - E2E Test", + "**/locales/**/*.ts": "${dirname}/${filename} - Locale", + "**/scripts/**/*.ts": "${filename} - CLI", + "**/server/modules/**/*.ts": "${filename} - Server Module", + "**/types/**/*.d.ts": "${filename} - Type" + }, + "workbench.iconTheme": "vscode-icons" +} diff --git a/biome.json b/biome.json new file mode 100644 index 00000000..d6a3498a --- /dev/null +++ b/biome.json @@ -0,0 +1,55 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.7.1/schema.json", + "organizeImports": { + "enabled": true + }, + "files": { + "ignore": ["src/server/json-hash.ts"] + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "correctness": { + "useHookAtTopLevel": "error" + }, + "performance": { + "noBarrelFile": "error", + "noReExportAll": "error" + }, + "style": { + "noNegationElse": "error", + "useConst": "off", + "useExportType": "off", + "useImportType": "off" + }, + "suspicious": { + "noConsoleLog": "warn", + "noEmptyBlockStatements": "warn", + "noSkippedTests": "error" + }, + "nursery": { + "useSortedClasses": { + "level": "error", + "options": { + "attributes": ["className", "class"], + "functions": ["cn", "tv"] + } + } + } + } + }, + "formatter": { "enabled": true }, + "vcs": { + "enabled": true, + "clientKind": "git", + "defaultBranch": "main", + "useIgnoreFile": true + }, + "overrides": [ + { + "include": ["**/*.md"], + "formatter": { "indentStyle": "tab" } + } + ] +} diff --git a/bun.lockb b/bun.lockb index 8b42aebd..77740751 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 216b66e9..aad37713 100644 --- a/package.json +++ b/package.json @@ -1,180 +1,166 @@ { - "name": "remix-utils", - "version": "7.6.0", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - }, - "type": "module", - "exports": { - "./package.json": "./package.json", - "./promise": "./build/common/promise.js", - "./cache-assets": "./build/client/cache-assets.js", - "./cors": "./build/server/cors.js", - "./get-client-ip-address": "./build/server/get-client-ip-address.js", - "./is-prefetch": "./build/server/is-prefetch.js", - "./json-hash": "./build/server/json-hash.js", - "./named-action": "./build/server/named-action.js", - "./parse-accept-header": "./build/server/parse-accept-header.js", - "./preload-route-assets": "./build/server/preload-route-assets.js", - "./redirect-back": "./build/server/redirect-back.js", - "./respond-to": "./build/server/respond-to.js", - "./responses": "./build/server/responses.js", - "./rolling-cookie": "./build/server/rolling-cookie.js", - "./safe-redirect": "./build/server/safe-redirect.js", - "./typed-cookie": "./build/server/typed-cookie.js", - "./typed-session": "./build/server/typed-session.js", - "./client-only": "./build/react/client-only.js", - "./existing-search-params": "./build/react/existing-search-params.js", - "./external-scripts": "./build/react/external-scripts.js", - "./fetcher-type": "./build/react/fetcher-type.js", - "./server-only": "./build/react/server-only.js", - "./use-debounce-fetcher": "./build/react/use-debounce-fetcher.js", - "./use-debounce-submit": "./build/react/use-debounce-submit.js", - "./use-delegated-anchors": "./build/react/use-delegated-anchors.js", - "./use-global-navigation-state": "./build/react/use-global-navigation-state.js", - "./use-hydrated": "./build/react/use-hydrated.js", - "./use-should-hydrate": "./build/react/use-should-hydrate.js", - "./sse/server": "./build/server/event-stream.js", - "./sse/react": "./build/react/use-event-source.js", - "./locales/server": "./build/server/get-client-locales.js", - "./locales/react": "./build/react/use-locales.js", - "./honeypot/server": "./build/server/honeypot.js", - "./honeypot/react": "./build/react/honeypot.js", - "./csrf/server": "./build/server/csrf.js", - "./csrf/react": "./build/react/authenticity-token.js", - "./sec-fetch": "./build/server/sec-fetch.js", - "./timers": "./build/common/timers.js" - }, - "sideEffects": false, - "scripts": { - "prepare": "npm run build", - "build": "tsc --project tsconfig.json --outDir ./build", - "postbuild": "prettier --write \"build/**/*.js\" \"build/**/*.d.ts\"", - "format": "prettier --write \"src/**/*.ts\" \"src/**/*.tsx\" \"test/**/*.ts\" \"test/**/*.tsx\" \"*.md\" \"package.json\"", - "typecheck": "tsc --project tsconfig.json --noEmit", - "lint": "eslint --ext .ts,.tsx src/", - "test": "vitest --run", - "test:watch": "vitest", - "test:coverage": "vitest --coverage", - "test:exports": "bun scripts/check-pkg-exports.ts" - }, - "author": { - "name": "Sergio Xalambrí", - "url": "https://sergiodxa.com", - "email": "hello@sergiodxa.com" - }, - "repository": { - "type": "git", - "url": "https://github.com/sergiodxa/remix-utils" - }, - "keywords": [ - "remix", - "remix.run", - "react", - "utils", - "request", - "response", - "csrf", - "redirect-back", - "client-only", - "hydrated", - "server-only", - "cors", - "rolling cookie", - "safe redirect", - "typed cookie", - "typed session", - "client IP address", - "client locale", - "json hash", - "prefetch", - "named action" - ], - "peerDependencies": { - "@remix-run/cloudflare": "^2.0.0", - "@remix-run/node": "^2.0.0", - "@remix-run/react": "^2.0.0", - "@remix-run/router": "^1.7.2", - "crypto-js": "^4.1.1", - "intl-parse-accept-language": "^1.0.0", - "is-ip": "^5.0.1", - "react": "^18.0.0", - "zod": "^3.22.4" - }, - "peerDependenciesMeta": { - "@remix-run/cloudflare": { - "optional": true - }, - "@remix-run/node": { - "optional": true - }, - "@remix-run/react": { - "optional": true - }, - "@remix-run/router": { - "optional": true - }, - "crypto-js": { - "optional": true - }, - "intl-parse-accept-language": { - "optional": true - }, - "is-ip": { - "optional": true - }, - "react": { - "optional": true - }, - "zod": { - "optional": true - } - }, - "devDependencies": { - "@arethetypeswrong/cli": "^0.15.0", - "@remix-run/node": "^2.0.0", - "@remix-run/react": "^2.0.0", - "@remix-run/router": "^1.7.2", - "@remix-run/testing": "^2.0.0", - "@testing-library/jest-dom": "^6.1.3", - "@testing-library/react": "^15.0.2", - "@types/crypto-js": "^4.1.2", - "@types/react": "^18.2.78", - "@typescript-eslint/eslint-plugin": "^7.0.2", - "@typescript-eslint/parser": "^7.0.1", - "@vitejs/plugin-react": "^4.2.1", - "@vitest/coverage-v8": "^1.5.0", - "crypto-js": "^4.1.1", - "eslint": "^8.12.0", - "eslint-config-prettier": "^9.0.0", - "eslint-import-resolver-typescript": "^3.6.1", - "eslint-plugin-cypress": "^2.15.1", - "eslint-plugin-import": "^2.28.1", - "eslint-plugin-jest-dom": "^5.1.0", - "eslint-plugin-jsx-a11y": "^6.7.1", - "eslint-plugin-prettier": "^5.0.0", - "eslint-plugin-promise": "^6.1.1", - "eslint-plugin-react": "^7.33.2", - "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-testing-library": "^6.2.2", - "eslint-plugin-unicorn": "^52.0.0", - "happy-dom": "^14.7.1", - "intl-parse-accept-language": "^1.0.0", - "is-ip": "5.0.1", - "msw": "^2.2.13", - "prettier": "^3.0.3", - "react": "^18.0.0", - "react-dom": "^18.0.0", - "ts-node": "^10.9.1", - "typedoc": "^0.25.13", - "typedoc-plugin-mdn-links": "^3.1.23", - "typescript": "^5.2.2", - "vite": "^5.2.8", - "vitest": "^1.4.0", - "zod": "^3.22.4" - }, - "dependencies": { - "type-fest": "^4.18.1" - } + "name": "remix-utils", + "version": "7.6.0", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "type": "module", + "exports": { + "./package.json": "./package.json", + "./promise": "./build/common/promise.js", + "./cache-assets": "./build/client/cache-assets.js", + "./cors": "./build/server/cors.js", + "./get-client-ip-address": "./build/server/get-client-ip-address.js", + "./is-prefetch": "./build/server/is-prefetch.js", + "./json-hash": "./build/server/json-hash.js", + "./named-action": "./build/server/named-action.js", + "./parse-accept-header": "./build/server/parse-accept-header.js", + "./preload-route-assets": "./build/server/preload-route-assets.js", + "./redirect-back": "./build/server/redirect-back.js", + "./respond-to": "./build/server/respond-to.js", + "./responses": "./build/server/responses.js", + "./rolling-cookie": "./build/server/rolling-cookie.js", + "./safe-redirect": "./build/server/safe-redirect.js", + "./typed-cookie": "./build/server/typed-cookie.js", + "./typed-session": "./build/server/typed-session.js", + "./client-only": "./build/react/client-only.js", + "./existing-search-params": "./build/react/existing-search-params.js", + "./external-scripts": "./build/react/external-scripts.js", + "./fetcher-type": "./build/react/fetcher-type.js", + "./server-only": "./build/react/server-only.js", + "./use-debounce-fetcher": "./build/react/use-debounce-fetcher.js", + "./use-debounce-submit": "./build/react/use-debounce-submit.js", + "./use-delegated-anchors": "./build/react/use-delegated-anchors.js", + "./use-global-navigation-state": "./build/react/use-global-navigation-state.js", + "./use-hydrated": "./build/react/use-hydrated.js", + "./use-should-hydrate": "./build/react/use-should-hydrate.js", + "./sse/server": "./build/server/event-stream.js", + "./sse/react": "./build/react/use-event-source.js", + "./locales/server": "./build/server/get-client-locales.js", + "./locales/react": "./build/react/use-locales.js", + "./honeypot/server": "./build/server/honeypot.js", + "./honeypot/react": "./build/react/honeypot.js", + "./csrf/server": "./build/server/csrf.js", + "./csrf/react": "./build/react/authenticity-token.js", + "./sec-fetch": "./build/server/sec-fetch.js", + "./timers": "./build/common/timers.js" + }, + "sideEffects": false, + "scripts": { + "prepare": "npm run build", + "build": "tsc --project tsconfig.json --outDir ./build", + "format": "biome format --write .", + "typecheck": "tsc --project tsconfig.json --noEmit", + "check": "biome check .", + "lint": "biome lint .", + "test": "vitest --run", + "test:watch": "vitest", + "test:coverage": "vitest --coverage", + "test:exports": "bun scripts/check-pkg-exports.ts" + }, + "author": { + "name": "Sergio Xalambrí", + "url": "https://sergiodxa.com", + "email": "hello@sergiodxa.com" + }, + "repository": { + "type": "git", + "url": "https://github.com/sergiodxa/remix-utils" + }, + "keywords": [ + "remix", + "remix.run", + "react", + "utils", + "request", + "response", + "csrf", + "redirect-back", + "client-only", + "hydrated", + "server-only", + "cors", + "rolling cookie", + "safe redirect", + "typed cookie", + "typed session", + "client IP address", + "client locale", + "json hash", + "prefetch", + "named action" + ], + "peerDependencies": { + "@remix-run/cloudflare": "^2.0.0", + "@remix-run/node": "^2.0.0", + "@remix-run/react": "^2.0.0", + "@remix-run/router": "^1.7.2", + "crypto-js": "^4.1.1", + "intl-parse-accept-language": "^1.0.0", + "is-ip": "^5.0.1", + "react": "^18.0.0", + "zod": "^3.22.4" + }, + "peerDependenciesMeta": { + "@remix-run/cloudflare": { + "optional": true + }, + "@remix-run/node": { + "optional": true + }, + "@remix-run/react": { + "optional": true + }, + "@remix-run/router": { + "optional": true + }, + "crypto-js": { + "optional": true + }, + "intl-parse-accept-language": { + "optional": true + }, + "is-ip": { + "optional": true + }, + "react": { + "optional": true + }, + "zod": { + "optional": true + } + }, + "devDependencies": { + "@arethetypeswrong/cli": "^0.15.0", + "@biomejs/biome": "^1.7.2", + "@remix-run/node": "^2.0.0", + "@remix-run/react": "^2.0.0", + "@remix-run/router": "^1.7.2", + "@remix-run/testing": "^2.0.0", + "@testing-library/jest-dom": "^6.1.3", + "@testing-library/react": "^15.0.2", + "@types/bun": "^1.1.1", + "@types/crypto-js": "^4.1.2", + "@types/react": "^18.2.78", + "@vitejs/plugin-react": "^4.2.1", + "@vitest/coverage-v8": "^1.5.0", + "crypto-js": "^4.1.1", + "happy-dom": "^14.7.1", + "intl-parse-accept-language": "^1.0.0", + "is-ip": "5.0.1", + "msw": "^2.2.13", + "react": "^18.0.0", + "react-dom": "^18.0.0", + "ts-node": "^10.9.1", + "typedoc": "^0.25.13", + "typedoc-plugin-mdn-links": "^3.1.23", + "typescript": "^5.2.2", + "vite": "^5.2.8", + "vitest": "^1.4.0", + "zod": "^3.22.4" + }, + "dependencies": { + "type-fest": "^4.18.1" + } } diff --git a/prettier.config.js b/prettier.config.js deleted file mode 100644 index 9953c75d..00000000 --- a/prettier.config.js +++ /dev/null @@ -1,6 +0,0 @@ -/* eslint-disable unicorn/prefer-module */ -/** @type {import("prettier").Config} */ -export default { - trailingComma: "all", - useTabs: true, -}; diff --git a/scripts/check-pkg-exports.ts b/scripts/check-pkg-exports.ts index da596d89..5a3e8249 100644 --- a/scripts/check-pkg-exports.ts +++ b/scripts/check-pkg-exports.ts @@ -1,7 +1,7 @@ -/* eslint-disable unicorn/no-process-exit */ +import { file, spawn } from "bun"; async function main() { - const proc = Bun.spawn([ + let proc = spawn([ "bunx", "attw", "-f", @@ -11,9 +11,9 @@ async function main() { "--pack", ]); - const text = await new Response(proc.stdout).text(); + let text = await new Response(proc.stdout).text(); - const entrypointLines = text + let entrypointLines = text .slice(text.indexOf('"remix-utils/')) .split("\n") .filter(Boolean) @@ -26,9 +26,9 @@ async function main() { .replaceAll(/│$/g, ""), ); - const pkg = await Bun.file("package.json").json(); - const entrypoints = entrypointLines.map((entrypointLine) => { - const [entrypoint, ...resolutionColumns] = entrypointLine.split("│"); + let pkg = await file("package.json").json(); + let entrypoints = entrypointLines.map((entrypointLine) => { + let [entrypoint, ...resolutionColumns] = entrypointLine.split("│"); return { entrypoint: entrypoint.replace(pkg.name, ".").trim(), esm: resolutionColumns[2].trim(), @@ -36,22 +36,13 @@ async function main() { }; }); - const entrypointsWithProblems = entrypoints.filter( + let entrypointsWithProblems = entrypoints.filter( (item) => item.esm.includes("fail") || item.bundler.includes("fail"), ); + if (entrypointsWithProblems.length > 0) { console.error("Entrypoints with problems:"); - console.log( - `---\n${entrypointsWithProblems - .map( - ({ entrypoint, esm, bundler }) => - `entrypoint: ${entrypoint}\nesm: ${esm}\nbundler: ${bundler}`, - ) - .join("\n---\n")}\n---`, - ); process.exit(1); - } else { - console.log("No problems found."); } } diff --git a/scripts/tsconfig.json b/scripts/tsconfig.json deleted file mode 100644 index 69cb6e2c..00000000 --- a/scripts/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "types": ["bun-types"] - }, - "include": ["./*.ts"] -} diff --git a/src/common/promise.ts b/src/common/promise.ts index 5e391a88..38bc0907 100644 --- a/src/common/promise.ts +++ b/src/common/promise.ts @@ -84,34 +84,32 @@ let TIMEOUT = Symbol("TIMEOUT"); * } * } */ -export function timeout( +export async function timeout( promise: Promise, options: { controller?: AbortController; ms: number }, ): Promise { - return new Promise(async (resolve, reject) => { - let timer: NodeJS.Timeout | null = null; + let timer: Timer | null = null; - try { - let result = await Promise.race([ - promise, - new Promise((resolve) => { - timer = setTimeout(() => resolve(TIMEOUT), options.ms); - }), - ]); + try { + let result = await Promise.race([ + promise, + new Promise((resolve) => { + timer = setTimeout(() => resolve(TIMEOUT), options.ms); + }), + ]); - if (timer) clearTimeout(timer); + if (timer) clearTimeout(timer); - if (result === TIMEOUT) { - if (options.controller) options.controller.abort(); - return reject(new TimeoutError(`Timed out after ${options.ms}ms`)); - } - - return resolve(result as Awaited); - } catch (error) { - if (timer) clearTimeout(timer); - reject(error); + if (result === TIMEOUT) { + if (options.controller) options.controller.abort(); + throw new TimeoutError(`Timed out after ${options.ms}ms`); } - }); + + return result as Awaited; + } catch (error) { + if (timer) clearTimeout(timer); + throw error; + } } /** diff --git a/src/react/existing-search-params.tsx b/src/react/existing-search-params.tsx index ec8f7caf..bd621ec8 100644 --- a/src/react/existing-search-params.tsx +++ b/src/react/existing-search-params.tsx @@ -1,5 +1,5 @@ -import * as React from "react"; import { useSearchParams } from "@remix-run/react"; +import * as React from "react"; type Props = { /** diff --git a/src/react/external-scripts.tsx b/src/react/external-scripts.tsx index b07200ed..73e8b480 100644 --- a/src/react/external-scripts.tsx +++ b/src/react/external-scripts.tsx @@ -1,5 +1,5 @@ -import * as React from "react"; import { useLocation, useMatches } from "@remix-run/react"; +import * as React from "react"; import { HandleConventionArguments } from "./handle-conventions.js"; import { useHydrated } from "./use-hydrated.js"; @@ -58,9 +58,9 @@ export type ScriptDescriptor = { type?: ScriptType; }; -export interface ExternalScriptsFunction { - (args: HandleConventionArguments): ScriptDescriptor[]; -} +export type ExternalScriptsFunction = ( + args: HandleConventionArguments, +) => ScriptDescriptor[]; /** * Define the shape of the `handle` export if you want to use `scripts`. Combine diff --git a/src/react/handle-conventions.ts b/src/react/handle-conventions.ts index bec2f032..a67d7c07 100644 --- a/src/react/handle-conventions.ts +++ b/src/react/handle-conventions.ts @@ -1,5 +1,5 @@ -import type { RouterState } from "@remix-run/router"; import type { Location, Params, useMatches } from "@remix-run/react"; +import type { RouterState } from "@remix-run/router"; export type HandleConventionArguments = { id: string; diff --git a/src/react/use-debounce-fetcher.ts b/src/react/use-debounce-fetcher.ts index 95978fe8..e4beb3ff 100644 --- a/src/react/use-debounce-fetcher.ts +++ b/src/react/use-debounce-fetcher.ts @@ -1,7 +1,7 @@ import type { - SubmitOptions, FetcherWithComponents, SubmitFunction, + SubmitOptions, } from "@remix-run/react"; import { useFetcher } from "@remix-run/react"; import { useCallback, useEffect, useRef } from "react"; @@ -35,7 +35,7 @@ type DebouncedFetcher = Omit< > & { submit: DebounceSubmitFunction }; export function useDebounceFetcher() { - let timeoutRef = useRef(); + let timeoutRef = useRef(); useEffect(() => { // no initialize step required since timeoutRef defaults undefined @@ -43,7 +43,7 @@ export function useDebounceFetcher() { return () => { if (timeout) clearTimeout(timeout); }; - }, [timeoutRef]); + }, []); let fetcher = useFetcher() as DebouncedFetcher; diff --git a/src/react/use-debounce-submit.ts b/src/react/use-debounce-submit.ts index 6d4e020d..680b23a5 100644 --- a/src/react/use-debounce-submit.ts +++ b/src/react/use-debounce-submit.ts @@ -1,11 +1,11 @@ -import type { SubmitOptions, SubmitFunction } from "@remix-run/react"; +import type { SubmitFunction, SubmitOptions } from "@remix-run/react"; import { useSubmit } from "@remix-run/react"; import { useCallback, useEffect, useRef } from "react"; type SubmitTarget = Parameters["0"]; export function useDebounceSubmit() { - let timeoutRef = useRef(); + let timeoutRef = useRef(); useEffect(() => { // no initialize step required since timeoutRef defaults undefined @@ -13,7 +13,7 @@ export function useDebounceSubmit() { return () => { if (timeout) clearTimeout(timeout); }; - }, [timeoutRef]); + }, []); // Clone the original submit to avoid a recursive loop const originalSubmit = useSubmit(); diff --git a/src/react/use-delegated-anchors.tsx b/src/react/use-delegated-anchors.tsx index 2896bc7e..6d314a60 100644 --- a/src/react/use-delegated-anchors.tsx +++ b/src/react/use-delegated-anchors.tsx @@ -6,7 +6,7 @@ const context = React.createContext(false); export function isLinkEvent(event: MouseEvent) { if (!(event.target instanceof HTMLElement)) return; let a = event.target.closest("a"); - if (a && a.hasAttribute("href") && a.host === window.location.host) return a; + if (a?.hasAttribute("href") && a.host === window.location.host) return a; return; } diff --git a/src/react/use-event-source.ts b/src/react/use-event-source.ts index 09ee0dcc..30f3a200 100644 --- a/src/react/use-event-source.ts +++ b/src/react/use-event-source.ts @@ -1,4 +1,4 @@ -import { useEffect, useState, createContext, useContext } from "react"; +import { createContext, useContext, useEffect, useState } from "react"; export interface EventSourceOptions { init?: EventSourceInit; diff --git a/src/react/use-global-navigation-state.ts b/src/react/use-global-navigation-state.ts index 6d7d8f0a..dc2b6407 100644 --- a/src/react/use-global-navigation-state.ts +++ b/src/react/use-global-navigation-state.ts @@ -1,4 +1,4 @@ -import { useNavigation, useFetchers, useRevalidator } from "@remix-run/react"; +import { useFetchers, useNavigation, useRevalidator } from "@remix-run/react"; import { useMemo } from "react"; /** diff --git a/src/react/use-hydrated.ts b/src/react/use-hydrated.ts index ef68c1bd..82646ec1 100644 --- a/src/react/use-hydrated.ts +++ b/src/react/use-hydrated.ts @@ -1,6 +1,7 @@ import { useSyncExternalStore } from "react"; function subscribe() { + // biome-ignore lint/suspicious/noEmptyBlockStatements: Mock function return () => {}; } diff --git a/src/server/cors.ts b/src/server/cors.ts index 0e9135a5..5ee4b4d6 100644 --- a/src/server/cors.ts +++ b/src/server/cors.ts @@ -87,7 +87,7 @@ class CORS { } private configureMaxAge(headers: Headers) { - var { maxAge } = this.options; + const { maxAge } = this.options; if (!this.isNumber(maxAge)) return headers; diff --git a/src/server/csrf.ts b/src/server/csrf.ts index 6100a2bd..95b089ab 100644 --- a/src/server/csrf.ts +++ b/src/server/csrf.ts @@ -184,9 +184,9 @@ export class CSRF { } private parseCookie(data: FormData | Request, headers?: Headers) { - if (data instanceof Request) headers = data.headers; - if (!headers) return null; - return this.cookie.parse(headers.get("cookie")); + let _headers = data instanceof Request ? data.headers : headers; + if (!_headers) return null; + return this.cookie.parse(_headers.get("cookie")); } private sign(token: string) { diff --git a/src/server/event-stream.ts b/src/server/event-stream.ts index 34b3a493..0597d67f 100644 --- a/src/server/event-stream.ts +++ b/src/server/event-stream.ts @@ -6,21 +6,16 @@ interface SendFunctionArgs { data: string; } -interface SendFunction { - (args: SendFunctionArgs): void; -} +type SendFunction = (args: SendFunctionArgs) => void; -interface CleanupFunction { - (): void; -} +type CleanupFunction = () => void; -interface AbortFunction { - (): void; -} +type AbortFunction = () => void; -interface InitFunction { - (send: SendFunction, abort: AbortFunction): CleanupFunction; -} +type InitFunction = ( + send: SendFunction, + abort: AbortFunction, +) => CleanupFunction; /** * A response helper to use Server Sent Events server-side diff --git a/src/server/get-client-ip-address.ts b/src/server/get-client-ip-address.ts index d28a83f3..93d7895e 100644 --- a/src/server/get-client-ip-address.ts +++ b/src/server/get-client-ip-address.ts @@ -77,7 +77,6 @@ function parseForwardedHeader(value: string | null): string | null { if (!value) return null; for (let part of value.split(";")) { if (part.startsWith("for=")) return part.slice(4); - continue; } return null; } diff --git a/src/server/honeypot.ts b/src/server/honeypot.ts index 23a83d06..be3ee306 100644 --- a/src/server/honeypot.ts +++ b/src/server/honeypot.ts @@ -55,7 +55,11 @@ const DEFAULT_VALID_FROM_FIELD_NAME = "from__confirm"; export class Honeypot { private generatedEncryptionSeed = this.randomValue(); - constructor(protected config: HoneypotConfig = {}) {} + protected config: HoneypotConfig; + + constructor(config: HoneypotConfig = {}) { + this.config = config; + } /** * Get the HoneypotInputProps to be used in your forms. diff --git a/src/server/named-action.ts b/src/server/named-action.ts index 032f54a9..d06d2201 100644 --- a/src/server/named-action.ts +++ b/src/server/named-action.ts @@ -48,7 +48,7 @@ export async function namedAction( } if (name === null && "default" in actions) { - return actions["default"]() as unknown as TypedResponse< + return actions.default() as unknown as TypedResponse< ResponsesUnion >; } diff --git a/src/server/respond-to.ts b/src/server/respond-to.ts index 2f51a55b..c8bd5cb7 100644 --- a/src/server/respond-to.ts +++ b/src/server/respond-to.ts @@ -67,7 +67,6 @@ export function respondTo( if (handler) return handler(); handler = handlers[type]; if (handler) return handler(); - continue; } return handlers.default(); diff --git a/src/server/safe-redirect.ts b/src/server/safe-redirect.ts index 4735951d..f365aa5f 100644 --- a/src/server/safe-redirect.ts +++ b/src/server/safe-redirect.ts @@ -15,16 +15,16 @@ export function safeRedirect( ) { if (!to || typeof to !== "string") return defaultRedirect; - to = to.trim(); + let trimmedTo = to.trim(); if ( - !to.startsWith("/") || - to.startsWith("//") || - to.startsWith("/\\") || - to.includes("..") + !trimmedTo.startsWith("/") || + trimmedTo.startsWith("//") || + trimmedTo.startsWith("/\\") || + trimmedTo.includes("..") ) { return defaultRedirect; } - return to; + return trimmedTo; } diff --git a/src/server/typed-session.ts b/src/server/typed-session.ts index 8444545f..a99d9837 100644 --- a/src/server/typed-session.ts +++ b/src/server/typed-session.ts @@ -1,9 +1,9 @@ import { CookieParseOptions, CookieSerializeOptions, - isSession, Session, SessionStorage, + isSession, } from "@remix-run/server-runtime"; import { z } from "zod"; diff --git a/test/common/promise.test.ts b/test/common/promise.test.ts index cc5db600..7a6c2187 100644 --- a/test/common/promise.test.ts +++ b/test/common/promise.test.ts @@ -1,5 +1,5 @@ +import { describe, expect, test } from "vitest"; import { promiseHash, timeout } from "../../src/common/promise"; -import { describe, test, expect } from "vitest"; describe(promiseHash, () => { test("should await all promises in a hash and return them with the same name", async () => { diff --git a/test/common/timers.test.ts b/test/common/timers.test.ts index acfd22da..b4359ca3 100644 --- a/test/common/timers.test.ts +++ b/test/common/timers.test.ts @@ -1,5 +1,5 @@ -import { wait, interval, TimersError } from "../../src/common/timers"; -import { describe, test, expect, beforeAll, vi, afterAll } from "vitest"; +import { afterAll, beforeAll, describe, expect, test, vi } from "vitest"; +import { TimersError, interval, wait } from "../../src/common/timers"; describe("Timers", () => { beforeAll(() => { diff --git a/test/react.test.tsx b/test/react.test.tsx index 6a0cbff5..aef4d575 100644 --- a/test/react.test.tsx +++ b/test/react.test.tsx @@ -1,7 +1,7 @@ -// @vitest-environment happy-dom -import { describe, test } from "vitest"; import { render } from "@testing-library/react"; import * as React from "react"; +// @vitest-environment happy-dom +import { describe, test } from "vitest"; import { AuthenticityTokenInput, AuthenticityTokenProvider, diff --git a/test/react/fetcher-type.test.tsx b/test/react/fetcher-type.test.tsx index d853297f..94e98c97 100644 --- a/test/react/fetcher-type.test.tsx +++ b/test/react/fetcher-type.test.tsx @@ -1,11 +1,11 @@ -// @vitest-environment happy-dom -import * as React from "react"; -import { describe, test, expect } from "vitest"; import { unstable_createRemixStub as createRemixStub } from "@remix-run/testing"; import { render, screen } from "@testing-library/react"; +// @vitest-environment happy-dom +import * as React from "react"; +import { describe, expect, test } from "vitest"; -import { getFetcherType, useFetcherType } from "../../src/react/fetcher-type"; import { useFetcher } from "@remix-run/react"; +import { getFetcherType, useFetcherType } from "../../src/react/fetcher-type"; describe(getFetcherType, () => { test("returns done", () => { diff --git a/test/react/use-delegated-anchors.test.tsx b/test/react/use-delegated-anchors.test.tsx index 40e3b3b0..5f2c4424 100644 --- a/test/react/use-delegated-anchors.test.tsx +++ b/test/react/use-delegated-anchors.test.tsx @@ -1,9 +1,9 @@ +import { useNavigate } from "@remix-run/react"; +import { fireEvent, render, screen } from "@testing-library/react"; // @vitest-environment happy-dom import * as React from "react"; -import { vi, beforeAll, afterEach, test, expect, MockedFunction } from "vitest"; +import { MockedFunction, afterEach, beforeAll, expect, test, vi } from "vitest"; import { PrefetchPageAnchors } from "../../src/react/use-delegated-anchors"; -import { render, screen, fireEvent } from "@testing-library/react"; -import { useNavigate } from "@remix-run/react"; // vi.mock("@remix-run/react"); @@ -134,7 +134,7 @@ describe.skip(PrefetchPageAnchors.name, () => { test("ignores links with target=_blank", () => { render( - + A link , diff --git a/test/react/use-locales.test.tsx b/test/react/use-locales.test.tsx index 8d517d35..0e1e4526 100644 --- a/test/react/use-locales.test.tsx +++ b/test/react/use-locales.test.tsx @@ -1,9 +1,9 @@ // @vitest-environment happy-dom import { RouteData } from "@remix-run/router/dist/utils"; +import { afterEach, describe, expect, test, vi } from "vitest"; import { useLocales } from "../../src/react/use-locales"; import { fakeMatch } from "../helpers/fake-match"; import { mockMatches } from "../helpers/mock-match"; -import { vi, describe, test, expect, afterEach } from "vitest"; describe.skip(useLocales, () => { afterEach(() => { @@ -56,7 +56,7 @@ describe.skip(useLocales, () => { expect(useLocales()).toBeUndefined(); }); - test.only("String", () => { + test("String", () => { mockMatches.mockReturnValue([fakeMatch({ locales: "en" })]); // mockMatches([fakeMatch({ locales: "en" })]); expect(useLocales()).toBe("en"); diff --git a/test/server/cors.test.ts b/test/server/cors.test.ts index 6de64a78..0b23679b 100644 --- a/test/server/cors.test.ts +++ b/test/server/cors.test.ts @@ -1,4 +1,4 @@ -import { describe, test, expect } from "vitest"; +import { describe, expect, test } from "vitest"; import { cors } from "../../src/server/cors"; diff --git a/test/server/csrf.test.ts b/test/server/csrf.test.ts index 8705a996..c7844751 100644 --- a/test/server/csrf.test.ts +++ b/test/server/csrf.test.ts @@ -1,5 +1,5 @@ -import { describe, test, expect } from "vitest"; import { createCookie } from "@remix-run/node"; +import { describe, expect, test } from "vitest"; import { CSRF, CSRFError } from "../../src/server/csrf"; describe("CSRF", () => { diff --git a/test/server/event-stream.test.ts b/test/server/event-stream.test.ts index 0a2b3ad2..6990cb82 100644 --- a/test/server/event-stream.test.ts +++ b/test/server/event-stream.test.ts @@ -1,11 +1,12 @@ /* eslint-disable @typescript-eslint/no-empty-function */ -import { vi, describe, test, expect } from "vitest"; +import { describe, expect, test, vi } from "vitest"; import { eventStream } from "../../src/server/event-stream"; describe(eventStream, () => { test("returns a response", () => { let controller = new AbortController(); let response = eventStream(controller.signal, (_, __) => { + // biome-ignore lint/suspicious/noEmptyBlockStatements: Test return () => {}; }); controller.abort(); @@ -15,6 +16,7 @@ describe(eventStream, () => { test("response is a readable stream", async () => { let controller = new AbortController(); let response = eventStream(controller.signal, (_, __) => { + // biome-ignore lint/suspicious/noEmptyBlockStatements: Test return () => {}; }); controller.abort(); @@ -28,6 +30,7 @@ describe(eventStream, () => { let controller = new AbortController(); let response = eventStream(controller.signal, (send, _) => { send({ data: "hello" }); + // biome-ignore lint/suspicious/noEmptyBlockStatements: Test return () => {}; }); @@ -49,6 +52,7 @@ describe(eventStream, () => { describe("Headers Overrides", () => { test("overrrides Content-Type header", () => { + // biome-ignore lint/suspicious/noEmptyBlockStatements: Test let spy = vi.spyOn(console, "warn").mockImplementation(() => {}); let response = eventStream( @@ -67,6 +71,7 @@ describe(eventStream, () => { }); test("overrides Cache-Control", () => { + // biome-ignore lint/suspicious/noEmptyBlockStatements: Test let spy = vi.spyOn(console, "warn").mockImplementation(() => {}); let response = eventStream( @@ -85,6 +90,7 @@ describe(eventStream, () => { }); test("overrides Connection", () => { + // biome-ignore lint/suspicious/noEmptyBlockStatements: Test let spy = vi.spyOn(console, "warn").mockImplementation(() => {}); let response = eventStream( diff --git a/test/server/get-client-id-address.test.ts b/test/server/get-client-id-address.test.ts index f7130da7..3662e2e3 100644 --- a/test/server/get-client-id-address.test.ts +++ b/test/server/get-client-id-address.test.ts @@ -1,4 +1,4 @@ -import { describe, test, expect } from "vitest"; +import { describe, expect, test } from "vitest"; import { getClientIPAddress } from "../../src/server/get-client-ip-address"; const VALID_IP = "192.168.0.1"; diff --git a/test/server/get-client-locales.test.ts b/test/server/get-client-locales.test.ts index 20afb7a2..c96660f9 100644 --- a/test/server/get-client-locales.test.ts +++ b/test/server/get-client-locales.test.ts @@ -1,4 +1,4 @@ -import { describe, test, expect } from "vitest"; +import { describe, expect, test } from "vitest"; import { getClientLocales } from "../../src/server/get-client-locales"; describe(getClientLocales, () => { diff --git a/test/server/honeypot.test.ts b/test/server/honeypot.test.ts index 36c5069a..07d5f370 100644 --- a/test/server/honeypot.test.ts +++ b/test/server/honeypot.test.ts @@ -1,8 +1,9 @@ -import { describe } from "vitest"; import CryptoJS from "crypto-js"; +import { describe, expect, test } from "vitest"; import { Honeypot, SpamError } from "../../src/server/honeypot"; +// biome-ignore lint/suspicious/noExplicitAny: Test function invariant(condition: any, message: string): asserts condition { if (!condition) throw new Error(message); } diff --git a/test/server/is-prefetch.test.ts b/test/server/is-prefetch.test.ts index b3349e74..ddb6814a 100644 --- a/test/server/is-prefetch.test.ts +++ b/test/server/is-prefetch.test.ts @@ -1,4 +1,4 @@ -import { describe, test, expect } from "vitest"; +import { describe, expect, test } from "vitest"; import { isPrefetch } from "../../src/server/is-prefetch"; describe(isPrefetch, () => { diff --git a/test/server/json-hash.test.ts b/test/server/json-hash.test.ts index 101ded8f..3bb87365 100644 --- a/test/server/json-hash.test.ts +++ b/test/server/json-hash.test.ts @@ -1,4 +1,4 @@ -import { describe, test, expect } from "vitest"; +import { describe, expect, test } from "vitest"; import { jsonHash } from "../../src/server/json-hash"; describe(jsonHash.name, () => { diff --git a/test/server/named-action.test.ts b/test/server/named-action.test.ts index 6a198716..07d0df25 100644 --- a/test/server/named-action.test.ts +++ b/test/server/named-action.test.ts @@ -1,6 +1,6 @@ -/* eslint-disable unicorn/consistent-function-scoping */ -import { describe, test, expect } from "vitest"; import { ActionFunctionArgs, json } from "@remix-run/server-runtime"; +/* eslint-disable unicorn/consistent-function-scoping */ +import { describe, expect, test } from "vitest"; import { namedAction } from "../../src/server/named-action"; describe(namedAction.name, () => { diff --git a/test/server/parse-accept-header.test.ts b/test/server/parse-accept-header.test.ts index 5b194b6e..6340f7b7 100644 --- a/test/server/parse-accept-header.test.ts +++ b/test/server/parse-accept-header.test.ts @@ -1,4 +1,4 @@ -import { describe, test, expect } from "vitest"; +import { describe, expect, test } from "vitest"; import { parseAcceptHeader } from "../../src/server/parse-accept-header"; describe(parseAcceptHeader, () => { diff --git a/test/server/preload-route-assets.test.ts b/test/server/preload-route-assets.test.ts index 058a3ae5..fe9c1736 100644 --- a/test/server/preload-route-assets.test.ts +++ b/test/server/preload-route-assets.test.ts @@ -1,7 +1,7 @@ -import { describe, test, expect } from "vitest"; import type { EntryContext } from "@remix-run/server-runtime"; -import { preloadRouteAssets } from "../../src/server/preload-route-assets"; import { SerializedError } from "@remix-run/server-runtime/dist/errors"; +import { describe, expect, test } from "vitest"; +import { preloadRouteAssets } from "../../src/server/preload-route-assets"; describe(preloadRouteAssets.name, () => { let context: EntryContext = { diff --git a/test/server/redirect-back.test.ts b/test/server/redirect-back.test.ts index 41422f94..a31cdfab 100644 --- a/test/server/redirect-back.test.ts +++ b/test/server/redirect-back.test.ts @@ -1,4 +1,4 @@ -import { describe, test, expect } from "vitest"; +import { describe, expect, test } from "vitest"; import { redirectBack } from "../../src/server/redirect-back"; describe(redirectBack, () => { diff --git a/test/server/respond-to.test.ts b/test/server/respond-to.test.ts index 522d0971..cc05622a 100644 --- a/test/server/respond-to.test.ts +++ b/test/server/respond-to.test.ts @@ -1,4 +1,4 @@ -import { describe, test, expect } from "vitest"; +import { describe, expect, test } from "vitest"; import { respondTo } from "../../src/server/respond-to"; describe(respondTo, () => { diff --git a/test/server/responses.test.ts b/test/server/responses.test.ts index ecbc2f37..695b68c6 100644 --- a/test/server/responses.test.ts +++ b/test/server/responses.test.ts @@ -1,14 +1,14 @@ -import { describe, test, expect } from "vitest"; +import { describe, expect, test } from "vitest"; import { + ImageType, html, - xml, - txt, image, - ImageType, javascript, notModified, pdf, stylesheet, + txt, + xml, } from "../../src/server/responses"; let jsonContentType = "application/json; charset=utf-8"; diff --git a/test/server/rolling-cookie.test.ts b/test/server/rolling-cookie.test.ts index 7ca4bb53..4aff4296 100644 --- a/test/server/rolling-cookie.test.ts +++ b/test/server/rolling-cookie.test.ts @@ -1,5 +1,5 @@ -import { describe, test, expect } from "vitest"; import { createCookie } from "@remix-run/node"; +import { describe, expect, test } from "vitest"; import { z } from "zod"; import { rollingCookie } from "../../src/server/rolling-cookie"; import { createTypedCookie } from "../../src/server/typed-cookie"; diff --git a/test/server/safe-redirect.test.ts b/test/server/safe-redirect.test.ts index da55f25e..417d6a11 100644 --- a/test/server/safe-redirect.test.ts +++ b/test/server/safe-redirect.test.ts @@ -1,4 +1,4 @@ -import { describe, test, expect } from "vitest"; +import { describe, expect, test } from "vitest"; import { safeRedirect } from "../../src/server/safe-redirect"; describe(safeRedirect.name, () => { diff --git a/test/server/typed-cookie.test.ts b/test/server/typed-cookie.test.ts index 63093f69..d3b87dd8 100644 --- a/test/server/typed-cookie.test.ts +++ b/test/server/typed-cookie.test.ts @@ -1,10 +1,10 @@ -import { describe, test, expect } from "vitest"; import { createCookie, createCookieSessionStorage, isCookie, } from "@remix-run/node"; -import { z, ZodError } from "zod"; +import { describe, expect, test } from "vitest"; +import { ZodError, z } from "zod"; import { createTypedCookie, isTypedCookie, diff --git a/test/server/typed-session.test.ts b/test/server/typed-session.test.ts index a7b51a4b..29cb5c75 100644 --- a/test/server/typed-session.test.ts +++ b/test/server/typed-session.test.ts @@ -1,19 +1,19 @@ -import { describe, test, expect } from "vitest"; import { ActionFunctionArgs, + LoaderFunctionArgs, createCookie, createCookieSessionStorage, isSession, json, - LoaderFunctionArgs, } from "@remix-run/node"; +import { describe, expect, test } from "vitest"; import { z } from "zod"; +import { createTypedCookie } from "../../src/server/typed-cookie"; import { + TypedSessionStorage, createTypedSessionStorage, isTypedSession, - TypedSessionStorage, } from "../../src/server/typed-session"; -import { createTypedCookie } from "../../src/server/typed-cookie"; let cookie = createCookie("session", { secrets: ["secret"] }); let schema = z.object({ diff --git a/vitest.config.ts b/vitest.config.ts index c333e367..95939ca4 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -5,14 +5,14 @@ import react from "@vitejs/plugin-react"; import { defineConfig } from "vite"; export default defineConfig({ - plugins: [react()], - test: { - globals: true, - environment: "node", - setupFiles: ["./vitest.setup.ts"], - coverage: { - all: true, - include: ["src/**/*"], - }, - }, + plugins: [react()], + test: { + globals: true, + environment: "node", + setupFiles: ["./vitest.setup.ts"], + coverage: { + all: true, + include: ["src/**/*"], + }, + }, }); diff --git a/vitest.setup.ts b/vitest.setup.ts index ae5150ed..30c74398 100644 --- a/vitest.setup.ts +++ b/vitest.setup.ts @@ -1,140 +1,140 @@ -import { expect } from "vitest"; import { installGlobals } from "@remix-run/node"; +import { expect } from "vitest"; import "@testing-library/jest-dom"; installGlobals(); function validateIsResponse(response: Response) { - if (response instanceof Response === false) { - return { - pass: false, - message() { - return "The value to assert must be an instance of Response."; - }, - }; - } - return; + if (response instanceof Response === false) { + return { + pass: false, + message() { + return "The value to assert must be an instance of Response."; + }, + }; + } + return; } function toHaveStatus(response: Response, status: number) { - let isNotResponse = validateIsResponse(response); - if (isNotResponse) return isNotResponse; - - let pass = response.status === status; - - return { - pass, - message() { - if (pass) { - return `The status code of the Response should not be ${status}.`; - } - return `The status code of the Response should be ${status}, it was ${response.status}.`; - }, - }; + let isNotResponse = validateIsResponse(response); + if (isNotResponse) return isNotResponse; + + let pass = response.status === status; + + return { + pass, + message() { + if (pass) { + return `The status code of the Response should not be ${status}.`; + } + return `The status code of the Response should be ${status}, it was ${response.status}.`; + }, + }; } function toRedirect(response: Response | string, path?: string) { - if (typeof response === "string") { - return { - pass: response === path, - message() { - if (response === path) { - return `The Response should not redirect to ${path}`; - } - return `The response should redirect to ${path}`; - }, - }; - } - - let isNotResponse = validateIsResponse(response); - if (isNotResponse) return isNotResponse; - - let header = response.headers.get("Location"); - let status = response.status; - - let pass = status === 302 && path ? header === path : Boolean(header); - - return { - pass, - message() { - if (pass) { - return `The Response should not redirect to ${path}`; - } - return `The response should redirect to ${path}`; - }, - }; + if (typeof response === "string") { + return { + pass: response === path, + message() { + if (response === path) { + return `The Response should not redirect to ${path}`; + } + return `The response should redirect to ${path}`; + }, + }; + } + + let isNotResponse = validateIsResponse(response); + if (isNotResponse) return isNotResponse; + + let header = response.headers.get("Location"); + let status = response.status; + + let pass = status === 302 && path ? header === path : Boolean(header); + + return { + pass, + message() { + if (pass) { + return `The Response should not redirect to ${path}`; + } + return `The response should redirect to ${path}`; + }, + }; } function toSetACookie(response: Response) { - let isNotResponse = validateIsResponse(response); - if (isNotResponse) return isNotResponse; - - let hasSetCookie = response.headers.has("Set-Cookie"); - - return { - pass: hasSetCookie, - message() { - if (hasSetCookie) return "Expected the response to not set a cookie."; - return "Expected the response to set a cookie."; - }, - }; + let isNotResponse = validateIsResponse(response); + if (isNotResponse) return isNotResponse; + + let hasSetCookie = response.headers.has("Set-Cookie"); + + return { + pass: hasSetCookie, + message() { + if (hasSetCookie) return "Expected the response to not set a cookie."; + return "Expected the response to set a cookie."; + }, + }; } function toBeOk(response: Response) { - let isNotResponse = validateIsResponse(response); - if (isNotResponse) return isNotResponse; - - let pass = response.ok; - - return { - pass, - message() { - if (pass) return "It should not to be ok."; - return "It should be ok."; - }, - }; + let isNotResponse = validateIsResponse(response); + if (isNotResponse) return isNotResponse; + + let pass = response.ok; + + return { + pass, + message() { + if (pass) return "It should not to be ok."; + return "It should be ok."; + }, + }; } function toHaveHeader(response: Response, name: string, value?: string) { - let isNotResponse = validateIsResponse(response); - if (isNotResponse) return isNotResponse; - - let pass = response.headers.has(name); - - if (!Boolean(value)) { - return { - pass, - message() { - if (pass) return `It should not have the header ${name}`; - return `It should have the header ${name}`; - }, - }; - } - - if (Boolean(value)) { - pass = response.headers.get(name) === value; - } - - return { - pass, - message() { - if (pass) { - return `It should not have the header ${name} with value ${value}, it was ${response.headers.get( - name - )}`; - } - return `It should have the header ${name} with value ${value}, it was ${response.headers.get( - name - )}`; - }, - }; + let isNotResponse = validateIsResponse(response); + if (isNotResponse) return isNotResponse; + + let pass = response.headers.has(name); + + if (!value) { + return { + pass, + message() { + if (pass) return `It should not have the header ${name}`; + return `It should have the header ${name}`; + }, + }; + } + + if (value) { + pass = response.headers.get(name) === value; + } + + return { + pass, + message() { + if (pass) { + return `It should not have the header ${name} with value ${value}, it was ${response.headers.get( + name, + )}`; + } + return `It should have the header ${name} with value ${value}, it was ${response.headers.get( + name, + )}`; + }, + }; } expect.extend({ - toRedirect, - toSetACookie, - toHaveStatus, - toBeOk, - toHaveHeader, + toRedirect, + toSetACookie, + toHaveStatus, + toBeOk, + toHaveHeader, });