diff --git a/.lefthook.yml b/.lefthook.yml index 8f9d6554c..8510d04c9 100644 --- a/.lefthook.yml +++ b/.lefthook.yml @@ -1,6 +1,14 @@ pre-commit: - parallel: true + parallel: false commands: - xo: + # lint --fix first, then oxfmt last (avoids write races); stage_fixed re-stages edits + oxlint: + priority: 1 glob: '*.{js,mjs,ts,tsx}' - run: pnpm xo {staged_files} + run: pnpm oxlint --fix {staged_files} + stage_fixed: true + oxfmt: + priority: 2 + glob: '*.{js,mjs,ts,tsx}' + run: pnpm oxfmt {staged_files} + stage_fixed: true diff --git a/.oxfmtrc.json b/.oxfmtrc.json new file mode 100644 index 000000000..2e95062ba --- /dev/null +++ b/.oxfmtrc.json @@ -0,0 +1,32 @@ +{ + "$schema": "./node_modules/oxfmt/configuration_schema.json", + "singleQuote": true, + "tabWidth": 2, + "printWidth": 80, + "trailingComma": "es5", + "bracketSpacing": true, + "endOfLine": "lf", + "sortPackageJson": true, + "sortTailwindcss": { + "stylesheet": "packages/ui/src/styles/globals.css", + "functions": ["clsx", "cn", "cva"] + }, + "sortImports": { + "internalPattern": [ + "@/", + "@app/", + "@ui/", + "@public/", + "@background/", + "@helpers/", + "@popup/", + "@store/" + ] + }, + "ignorePatterns": [ + "packages/ui/src", + "**/.output/**", + "**/.next/**", + "**/.wxt/**" + ] +} diff --git a/.oxlintrc.json b/.oxlintrc.json new file mode 100644 index 000000000..2fa9108b8 --- /dev/null +++ b/.oxlintrc.json @@ -0,0 +1,85 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "plugins": ["typescript", "unicorn", "oxc", "react", "import", "promise"], + "jsPlugins": ["oxlint-tailwindcss"], + "categories": { + "correctness": "error" + }, + "settings": { + "next": { + "rootDir": "apps/web/" + }, + "tailwindcss": { + "entryPoint": "packages/ui/src/styles/globals.css" + } + }, + "options": { + "typeAware": true, + "reportUnusedDisableDirectives": "warn" + }, + "rules": { + "react/react-in-jsx-scope": "off", + + "typescript/no-unsafe-assignment": "off", + "typescript/no-unsafe-return": "off", + "typescript/no-unsafe-argument": "off", + "typescript/no-unsafe-call": "off", + "typescript/no-dynamic-delete": "off", + "typescript/triple-slash-reference": "off", + + "typescript/no-floating-promises": "off", + "typescript/no-useless-default-assignment": "off", + "typescript/unbound-method": "off", + + "typescript/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }], + "typescript/consistent-type-definitions": ["error", "interface"], + "typescript/no-confusing-void-expression": [ + "error", + { "ignoreArrowShorthand": true } + ], + "typescript/no-base-to-string": [ + "error", + { "ignoredTypeNames": ["TRPCError", "URL", "URLSearchParams"] } + ], + "typescript/restrict-template-expressions": [ + "error", + { "allow": ["TRPCError", "Date"] } + ], + "typescript/switch-exhaustiveness-check": [ + "error", + { + "considerDefaultExhaustiveForUnions": true, + "requireDefaultForNonUnion": true + } + ], + + "tailwindcss/no-unknown-classes": [ + "error", + { "allowlist": ["max-w-panel"] } + ], + "tailwindcss/no-conflicting-classes": "error", + "tailwindcss/no-duplicate-classes": "error", + "tailwindcss/no-deprecated-classes": "error" + }, + "overrides": [ + { + "files": ["apps/web/**/*.{ts,tsx}"], + "plugins": ["nextjs"] + }, + { + "files": [ + "apps/extension/**/*.{ts,tsx}", + "packages/shared/**/*.{ts,tsx}" + ], + "rules": { + "react/react-compiler": "warn" + } + } + ], + "ignorePatterns": [ + "apps/web/next-env.d.ts", + "apps/extension/.output/**", + "apps/extension/.wxt/**", + "packages/ui/**" + ] +} diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index c1309c8fe..000000000 --- a/.prettierignore +++ /dev/null @@ -1 +0,0 @@ -packages/ui/src diff --git a/.prettierrc.json b/.prettierrc.json deleted file mode 100644 index dc03570c7..000000000 --- a/.prettierrc.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "endOfLine": "auto", - "singleQuote": true, - "tabWidth": 2, - "trailingComma": "es5", - "bracketSpacing": true -} diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 000000000..b7b4c7aec --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["oxc.oxc-vscode", "TypeScriptTeam.native-preview"] +} diff --git a/.vscode/settings.json b/.vscode/settings.json index 512ff90b1..70ad7defe 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,21 @@ { - "js/ts.tsdk.path": "node_modules/typescript/lib" + "js/ts.experimental.useTsgo": true, + "js/ts.tsdk.path": "./node_modules/typescript", + + "oxc.enable": true, + "oxc.enable.oxfmt": true, + "oxc.lint.run": "onSave", + "oxc.typeAware": true, + + "editor.defaultFormatter": "oxc.oxc-vscode", + "editor.formatOnSave": true, + "editor.formatOnSaveMode": "file", + "editor.codeActionsOnSave": { + "source.fixAll.oxc": "explicit" + }, + + "[typescript]": { "editor.defaultFormatter": "oxc.oxc-vscode" }, + "[typescriptreact]": { "editor.defaultFormatter": "oxc.oxc-vscode" }, + "[javascript]": { "editor.defaultFormatter": "oxc.oxc-vscode" }, + "[javascriptreact]": { "editor.defaultFormatter": "oxc.oxc-vscode" } } diff --git a/AGENTS.md b/AGENTS.md index e2e5af43d..41ee14073 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -29,7 +29,10 @@ pnpm build # Build Chrome extension to .output/chrome-mv3 pnpm dev # Chrome dev server with hot reload # Code Quality -pnpm lint # Lint all files (XO linter) +pnpm lint # Lint all files (oxlint, type-aware) with autofix +pnpm lint:ci # Lint without autofix (CI) +pnpm format # Format all files (oxfmt) +pnpm format:check # Check formatting without writing (oxfmt --check) pnpm typecheck # Type check root only pnpm typecheck:all # Type check all workspaces @@ -45,7 +48,7 @@ This is a **Turbo + pnpm monorepo** with the following structure: - **apps/web** - Next.js web interface for downloads and admin - **packages/shared** - Shared React components, types, utilities, and stores (Zustand) - **packages/ui** - Shared UI components using shadcn/ui Base UI + Tailwind CSS -- **packages/configs** - Shared TypeScript, ESLint (XO), and build configs +- **packages/configs** - Shared TypeScript and build configs - **packages/trpc** - tRPC router with Firebase backend (type-safe API) ## Build System @@ -76,7 +79,9 @@ Playwright tests use setup/teardown projects for both web and extension flows: - **API**: tRPC for type-safe client-server communication - **Backend**: Firebase with Admin SDK - **Testing**: Playwright -- **Linting**: XO with custom config +- **Linting**: oxlint (type-aware via oxlint-tsgolint), config in `.oxlintrc.json` +- **Formatting**: oxfmt, config in `.oxfmtrc.json` +- **Tailwind linting**: `oxlint-tailwindcss` (native oxlint plugin) ## Code Patterns @@ -119,6 +124,7 @@ Always after making changes, run the following commands: ```bash pnpm lint +pnpm format:check pnpm typecheck:all pnpm e2e ``` diff --git a/apps/extension/package.json b/apps/extension/package.json index 7f6fc49c2..5c498404d 100644 --- a/apps/extension/package.json +++ b/apps/extension/package.json @@ -1,9 +1,9 @@ { "name": "@bypass/extension", - "type": "module", - "version": "24.12.1", + "version": "24.13.0", "private": true, "description": "Bypass links from various sites to required links, with added functionality of Bookmarks Manager, Tagging Panel.", + "type": "module", "scripts": { "build": "wxt zip", "dev": "wxt", diff --git a/apps/extension/src/apis/trpcApi.ts b/apps/extension/src/apis/trpcApi.ts index 3759ce9a9..ee15a6263 100644 --- a/apps/extension/src/apis/trpcApi.ts +++ b/apps/extension/src/apis/trpcApi.ts @@ -1,5 +1,6 @@ import type { AppRouter } from '@bypass/trpc'; import { createTRPCClient, httpBatchLink, loggerLink } from '@trpc/client'; + import { env } from '@/constants/env'; import useFirebaseStore from '@/store/firebase/useFirebaseStore'; diff --git a/apps/extension/src/apis/wretchApi.ts b/apps/extension/src/apis/wretchApi.ts index 7e4405fd4..a5f380fb9 100644 --- a/apps/extension/src/apis/wretchApi.ts +++ b/apps/extension/src/apis/wretchApi.ts @@ -1,4 +1,5 @@ import wretch from 'wretch'; + import { env } from '@/constants/env'; import useFirebaseStore from '@/store/firebase/useFirebaseStore'; diff --git a/apps/extension/src/entrypoints/background/index.ts b/apps/extension/src/entrypoints/background/index.ts index 3d0e3b9f6..151d1f7ee 100644 --- a/apps/extension/src/entrypoints/background/index.ts +++ b/apps/extension/src/entrypoints/background/index.ts @@ -1,16 +1,18 @@ import { defineBackground } from 'wxt/utils/define-background'; -import turnOffInputSuggestions from './misc/turnOffInputSuggestions'; -import { redirect } from './redirections'; -import { isValidTabUrl, isValidUrl, setExtensionIcon } from './utils'; -import { receiveRuntimeMessage } from './utils/receiveRuntimeMessage'; -import { type RuntimeInput } from '@/utils/sendRuntimeMessage'; -import { getIsExtensionActive } from '@/utils/common'; + import { EExtensionState } from '@/constants'; import { extStateItem, hasPendingBookmarksItem, hasPendingPersonsItem, } from '@/storage/items'; +import { getIsExtensionActive } from '@/utils/common'; +import { type RuntimeInput } from '@/utils/sendRuntimeMessage'; + +import turnOffInputSuggestions from './misc/turnOffInputSuggestions'; +import { redirect } from './redirections'; +import { isValidTabUrl, isValidUrl, setExtensionIcon } from './utils'; +import { receiveRuntimeMessage } from './utils/receiveRuntimeMessage'; export default defineBackground({ type: 'module', diff --git a/apps/extension/src/entrypoints/background/redirections/index.ts b/apps/extension/src/entrypoints/background/redirections/index.ts index 4c8350adb..f691db0b1 100644 --- a/apps/extension/src/entrypoints/background/redirections/index.ts +++ b/apps/extension/src/entrypoints/background/redirections/index.ts @@ -1,7 +1,8 @@ -import { mapRedirections } from './mapper'; -import { startHistoryWatch } from '@/utils/history'; import { trpcApi } from '@/apis/trpcApi'; import { redirectionsItem, mappedRedirectionsItem } from '@/storage/items'; +import { startHistoryWatch } from '@/utils/history'; + +import { mapRedirections } from './mapper'; export const redirect = async (tabId: number, url: URL) => { url.protocol = 'http:'; diff --git a/apps/extension/src/entrypoints/background/redirections/mapper.ts b/apps/extension/src/entrypoints/background/redirections/mapper.ts index 7b0beace5..df28850d9 100644 --- a/apps/extension/src/entrypoints/background/redirections/mapper.ts +++ b/apps/extension/src/entrypoints/background/redirections/mapper.ts @@ -1,4 +1,5 @@ import { type IRedirections } from '@bypass/shared'; + import { type IMappedRedirections } from '../interfaces/redirections'; export const mapRedirections = ( diff --git a/apps/extension/src/entrypoints/background/utils/index.ts b/apps/extension/src/entrypoints/background/utils/index.ts index adc28ba73..215237f55 100644 --- a/apps/extension/src/entrypoints/background/utils/index.ts +++ b/apps/extension/src/entrypoints/background/utils/index.ts @@ -1,6 +1,6 @@ +import { type EExtensionState } from '@/constants'; import { extStateItem } from '@/storage/items'; import { getIsExtensionActive } from '@/utils/common'; -import { type EExtensionState } from '@/constants'; const restrictedProtocols = new Set([ 'chrome:', // Chrome browser internal URLs diff --git a/apps/extension/src/entrypoints/background/utils/receiveRuntimeMessage.ts b/apps/extension/src/entrypoints/background/utils/receiveRuntimeMessage.ts index d7b74736f..334817b30 100644 --- a/apps/extension/src/entrypoints/background/utils/receiveRuntimeMessage.ts +++ b/apps/extension/src/entrypoints/background/utils/receiveRuntimeMessage.ts @@ -1,10 +1,11 @@ -import { getForumPageLinks } from '../misc/forumPageLinks'; import { type RuntimeInput, type RuntimeKeys, type RuntimeOutput, } from '@/utils/sendRuntimeMessage'; +import { getForumPageLinks } from '../misc/forumPageLinks'; + export const receiveRuntimeMessage = ( message: RuntimeInput, sendMessage: (data: RuntimeOutput[T]) => void diff --git a/apps/extension/src/entrypoints/background/websites/storageSync.ts b/apps/extension/src/entrypoints/background/websites/storageSync.ts index 2471e877b..e863640d6 100644 --- a/apps/extension/src/entrypoints/background/websites/storageSync.ts +++ b/apps/extension/src/entrypoints/background/websites/storageSync.ts @@ -1,4 +1,5 @@ import { type IWebsites } from '@bypass/shared'; + import { trpcApi } from '@/apis/trpcApi'; import { websitesItem } from '@/storage/items'; diff --git a/apps/extension/src/entrypoints/popup/components/ContextMenu.tsx b/apps/extension/src/entrypoints/popup/components/ContextMenu.tsx index 3d8328225..e7b02a6ad 100644 --- a/apps/extension/src/entrypoints/popup/components/ContextMenu.tsx +++ b/apps/extension/src/entrypoints/popup/components/ContextMenu.tsx @@ -1,10 +1,10 @@ -import { HugeiconsIcon } from '@hugeicons/react'; import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuTrigger, } from '@bypass/ui'; +import { HugeiconsIcon } from '@hugeicons/react'; import { useRef } from 'react'; export interface IMenuOption { diff --git a/apps/extension/src/entrypoints/popup/components/Global.tsx b/apps/extension/src/entrypoints/popup/components/Global.tsx index ef8cffd4b..e4b8805c6 100644 --- a/apps/extension/src/entrypoints/popup/components/Global.tsx +++ b/apps/extension/src/entrypoints/popup/components/Global.tsx @@ -1,5 +1,6 @@ import { Toaster } from '@bypass/ui'; import { useHotkeys } from '@mantine/hooks'; + import StoreListener from './StoreListener'; function Global() { diff --git a/apps/extension/src/entrypoints/popup/components/PopupRoutes.tsx b/apps/extension/src/entrypoints/popup/components/PopupRoutes.tsx index 611711b1f..08fd419da 100644 --- a/apps/extension/src/entrypoints/popup/components/PopupRoutes.tsx +++ b/apps/extension/src/entrypoints/popup/components/PopupRoutes.tsx @@ -1,5 +1,6 @@ import { Suspense } from 'react'; import { Switch } from 'wouter'; + import { BookmarksPanelRoute } from '../panels/BookmarksPanel/routes'; import { HomePageRoute } from '../panels/HomePopup/routes'; import { PersonsPanelRoute } from '../panels/PersonsPanel/routes'; diff --git a/apps/extension/src/entrypoints/popup/components/StoreListener.tsx b/apps/extension/src/entrypoints/popup/components/StoreListener.tsx index 241a1b88c..dc30aeed2 100644 --- a/apps/extension/src/entrypoints/popup/components/StoreListener.tsx +++ b/apps/extension/src/entrypoints/popup/components/StoreListener.tsx @@ -1,6 +1,7 @@ -import useHistoryStore from '@store/history'; import { useEffect } from 'react'; + import { startHistoryWatch } from '@/utils/history'; +import useHistoryStore from '@store/history'; function StoreListener() { const monitorHistory = useHistoryStore((state) => state.monitorHistory); diff --git a/apps/extension/src/entrypoints/popup/hooks/useCurrentTab.ts b/apps/extension/src/entrypoints/popup/hooks/useCurrentTab.ts index f0ba8eae1..bd0da148b 100644 --- a/apps/extension/src/entrypoints/popup/hooks/useCurrentTab.ts +++ b/apps/extension/src/entrypoints/popup/hooks/useCurrentTab.ts @@ -1,4 +1,5 @@ import { useEffect, useState } from 'react'; + import { getCurrentTab } from '@popup/utils/tabs'; const useCurrentTab = () => { diff --git a/apps/extension/src/entrypoints/popup/index.tsx b/apps/extension/src/entrypoints/popup/index.tsx index fb0330681..314630374 100644 --- a/apps/extension/src/entrypoints/popup/index.tsx +++ b/apps/extension/src/entrypoints/popup/index.tsx @@ -2,6 +2,7 @@ import '@bypass/ui/styles/globals.css'; import { TooltipProvider } from '@bypass/ui'; import { StrictMode } from 'react'; import { createRoot } from 'react-dom/client'; + import './fonts.css'; import Global from './components/Global'; import PopupRoutes from './components/PopupRoutes'; diff --git a/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/components/BookmarkAddEditDialog.tsx b/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/components/BookmarkAddEditDialog.tsx index f05d3bc38..a4c16dd21 100644 --- a/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/components/BookmarkAddEditDialog.tsx +++ b/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/components/BookmarkAddEditDialog.tsx @@ -1,9 +1,11 @@ -import { z } from 'zod/mini'; -import { useForm } from '@tanstack/react-form'; -import { useDisclosure } from '@mantine/hooks'; -import { useCallback, useEffect, useMemo, useRef } from 'react'; -import { useLocation } from 'wouter'; -import { useShallow } from 'zustand/react/shallow'; +import { + EBookmarkOperation, + getBookmarksPanelUrl, + getDecodedFolderList, + getDefaultFolder, + ROOT_FOLDER_ID, + type ITransformedBookmark, +} from '@bypass/shared'; import { Button, Dialog, @@ -21,16 +23,16 @@ import { SelectTrigger, SelectValue, } from '@bypass/ui'; -import { - EBookmarkOperation, - getBookmarksPanelUrl, - getDecodedFolderList, - getDefaultFolder, - ROOT_FOLDER_ID, - type ITransformedBookmark, -} from '@bypass/shared'; -import { getCurrentTab } from '@popup/utils/tabs'; +import { useDisclosure } from '@mantine/hooks'; +import { useForm } from '@tanstack/react-form'; +import { useCallback, useEffect, useMemo, useRef } from 'react'; +import { useLocation } from 'wouter'; +import { z } from 'zod/mini'; +import { useShallow } from 'zustand/react/shallow'; + import { handleEscapeKey } from '@popup/utils/dialog'; +import { getCurrentTab } from '@popup/utils/tabs'; + import useBookmarkRouteStore from '../store/useBookmarkRouteStore'; import useBookmarkStore from '../store/useBookmarkStore'; import PersonSelect from './PersonSelect'; @@ -295,12 +297,7 @@ function BookmarkAddEditDialog({ curFolderId, handleScroll }: Props) { )} - + diff --git a/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/components/BookmarkContextMenu.tsx b/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/components/BookmarkContextMenu.tsx index 3fd0c2470..cef0d8722 100644 --- a/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/components/BookmarkContextMenu.tsx +++ b/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/components/BookmarkContextMenu.tsx @@ -9,9 +9,11 @@ import { import { useHotkeys } from '@mantine/hooks'; import { type PropsWithChildren } from 'react'; import { useShallow } from 'zustand/react/shallow'; + import ContextMenu, { type IMenuOption } from '@popup/components/ContextMenu'; -import useBookmarkStore from '../store/useBookmarkStore'; + import useBookmarkRouteStore from '../store/useBookmarkRouteStore'; +import useBookmarkStore from '../store/useBookmarkStore'; import { getCutCount, getSelectedCount } from '../utils'; import { findBookmarkById } from '../utils/bookmark'; diff --git a/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/components/BookmarkRow.tsx b/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/components/BookmarkRow.tsx index 986e2d40b..65235daf6 100644 --- a/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/components/BookmarkRow.tsx +++ b/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/components/BookmarkRow.tsx @@ -3,6 +3,7 @@ import { getGoogleFaviconUrl, type BookmarkProps, } from '@bypass/shared'; + import useHistoryStore from '@store/history'; function BookmarkRow( diff --git a/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/components/BookmarksHeader.tsx b/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/components/BookmarksHeader.tsx index e34c32dbc..c05fed4c5 100644 --- a/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/components/BookmarksHeader.tsx +++ b/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/components/BookmarksHeader.tsx @@ -1,9 +1,10 @@ import { Header } from '@bypass/shared'; import { Button } from '@bypass/ui'; -import { useDisclosure, useHotkeys } from '@mantine/hooks'; import { BookmarkCheck01Icon, FolderAddIcon } from '@hugeicons/core-free-icons'; import { HugeiconsIcon } from '@hugeicons/react'; +import { useDisclosure, useHotkeys } from '@mantine/hooks'; import { useShallow } from 'zustand/react/shallow'; + import useBookmarkStore from '../store/useBookmarkStore'; import ConfirmationDialog from './ConfirmationDialog'; import { FolderAddEditDialog } from './FolderAddEditDialog'; diff --git a/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/components/BookmarksPanel.tsx b/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/components/BookmarksPanel.tsx index b376869be..c59272a5e 100644 --- a/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/components/BookmarksPanel.tsx +++ b/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/components/BookmarksPanel.tsx @@ -8,17 +8,19 @@ import { shouldRenderBookmarks, } from '@bypass/shared'; import { ScrollArea } from '@bypass/ui'; -import useHistoryStore from '@store/history'; import { useVirtualizer } from '@tanstack/react-virtual'; import { useCallback, useEffect, useRef, useState } from 'react'; import { useShallow } from 'zustand/react/shallow'; + +import { MAX_PANEL_SIZE } from '@/constants'; +import useHistoryStore from '@store/history'; + import useBookmarkRouteStore from '../store/useBookmarkRouteStore'; import useBookmarkStore from '../store/useBookmarkStore'; import BookmarkAddEditDialog from './BookmarkAddEditDialog'; import BookmarkContextMenu from './BookmarkContextMenu'; import BookmarksHeader from './BookmarksHeader'; import VirtualRow from './VirtualRow'; -import { MAX_PANEL_SIZE } from '@/constants'; function BookmarksPanel({ folderId, operation, bmUrl }: BMPanelQueryParams) { const startHistoryMonitor = useHistoryStore( diff --git a/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/components/ConfirmationDialog.tsx b/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/components/ConfirmationDialog.tsx index aea1edbe0..056319c9f 100644 --- a/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/components/ConfirmationDialog.tsx +++ b/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/components/ConfirmationDialog.tsx @@ -1,3 +1,4 @@ +import { noOp } from '@bypass/shared'; import { Dialog, DialogContent, @@ -6,7 +7,7 @@ import { DialogTitle, Button, } from '@bypass/ui'; -import { noOp } from '@bypass/shared'; + import { handleEscapeKey } from '@popup/utils/dialog'; interface Props { diff --git a/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/components/FolderAddEditDialog.tsx b/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/components/FolderAddEditDialog.tsx index c0232b25f..e9b74da94 100644 --- a/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/components/FolderAddEditDialog.tsx +++ b/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/components/FolderAddEditDialog.tsx @@ -1,5 +1,3 @@ -import { z } from 'zod/mini'; -import { useForm } from '@tanstack/react-form'; import { Button, Dialog, @@ -11,6 +9,9 @@ import { FieldLabel, Input, } from '@bypass/ui'; +import { useForm } from '@tanstack/react-form'; +import { z } from 'zod/mini'; + import { handleEscapeKey } from '@popup/utils/dialog'; interface Props { diff --git a/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/components/FolderRow.tsx b/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/components/FolderRow.tsx index 26bbf75a8..c56a1fc1d 100644 --- a/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/components/FolderRow.tsx +++ b/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/components/FolderRow.tsx @@ -1,13 +1,15 @@ import { Folder, type FolderProps } from '@bypass/shared'; -import { useDisclosure } from '@mantine/hooks'; -import { HugeiconsIcon } from '@hugeicons/react'; import { FolderEditIcon, FolderRemoveIcon, StarIcon, StarOffIcon, } from '@hugeicons/core-free-icons'; +import { HugeiconsIcon } from '@hugeicons/react'; +import { useDisclosure } from '@mantine/hooks'; + import ContextMenu, { type IMenuOption } from '@popup/components/ContextMenu'; + import { FolderAddEditDialog } from './FolderAddEditDialog'; interface Props extends FolderProps { @@ -69,12 +71,7 @@ function FolderRow({
{isDefault && ( -
+
)} diff --git a/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/components/PersonSelect.tsx b/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/components/PersonSelect.tsx index a0721e512..0ccfcfe76 100644 --- a/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/components/PersonSelect.tsx +++ b/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/components/PersonSelect.tsx @@ -25,8 +25,8 @@ import { TooltipTrigger, useComboboxAnchor, } from '@bypass/ui'; -import { HugeiconsIcon } from '@hugeicons/react'; import { UserWarning03Icon } from '@hugeicons/core-free-icons'; +import { HugeiconsIcon } from '@hugeicons/react'; import { useCallback, useEffect, useState } from 'react'; interface IOptionData { diff --git a/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/components/VirtualRow.tsx b/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/components/VirtualRow.tsx index 3a6873e7f..bece96311 100644 --- a/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/components/VirtualRow.tsx +++ b/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/components/VirtualRow.tsx @@ -1,6 +1,7 @@ import { type ContextBookmark, isFolderEmpty } from '@bypass/shared'; -import { useShallow } from 'zustand/react/shallow'; import clsx from 'clsx'; +import { useShallow } from 'zustand/react/shallow'; + import useBookmarkStore from '../store/useBookmarkStore'; import BookmarkRow from './BookmarkRow'; import FolderRow from './FolderRow'; @@ -36,15 +37,9 @@ function VirtualRow({ bookmark, pos, isSelected, isCut }: Props) { className={clsx( 'box-border h-full cursor-pointer rounded-md select-none', 'hover:bg-muted', - ` - data-[is-selected=true]:bg-primary - data-[is-selected=true]:text-primary-foreground - `, + `data-[is-selected=true]:bg-primary data-[is-selected=true]:text-primary-foreground`, 'data-[is-selected=true]:hover:bg-primary/90', - ` - data-[is-cut=true]:border data-[is-cut=true]:border-dashed - data-[is-cut=true]:border-border data-[is-cut=true]:opacity-50 - ` + `data-[is-cut=true]:border data-[is-cut=true]:border-dashed data-[is-cut=true]:border-border data-[is-cut=true]:opacity-50` )} // Added to fix context menu style={{ zIndex: bookmark.isDir ? 1 : 'auto' }} diff --git a/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/routes/index.tsx b/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/routes/index.tsx index ffa168e88..e540d1c4d 100644 --- a/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/routes/index.tsx +++ b/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/routes/index.tsx @@ -5,6 +5,7 @@ import { ROUTES, } from '@bypass/shared'; import { Route, useSearch } from 'wouter'; + import BookmarksPanel from '../components/BookmarksPanel'; const getQueryParams = (qs: string): BMPanelQueryParams => { diff --git a/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/store/useBookmarkStore.ts b/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/store/useBookmarkStore.ts index 35edecbdb..86bb0d628 100644 --- a/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/store/useBookmarkStore.ts +++ b/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/store/useBookmarkStore.ts @@ -10,12 +10,14 @@ import { getGoogleFaviconUrl, getEncryptedFolder, } from '@bypass/shared'; -import { create } from 'zustand'; import { toast } from 'sonner'; +import { create } from 'zustand'; + +import { bookmarksItem } from '@/storage/items'; + import { isFolderContainsDir, setBookmarksInStorage } from '../utils'; import { findBookmarkById, findBookmarkByUrl } from '../utils/bookmark'; import { processBookmarksMove } from '../utils/manipulate'; -import { bookmarksItem } from '@/storage/items'; interface State { // State diff --git a/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/utils/bookmark.ts b/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/utils/bookmark.ts index 5017722f9..ebf70284a 100644 --- a/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/utils/bookmark.ts +++ b/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/utils/bookmark.ts @@ -7,7 +7,7 @@ import { type IBookmarksObj, type ITransformedBookmark, } from '@bypass/shared'; -import { SIGN_IN_TOTAL_STEPS } from '../../HomePopup/constants/progress'; + import { trpcApi } from '@/apis/trpcApi'; import { bookmarksItem, @@ -17,6 +17,8 @@ import { } from '@/storage/items'; import useProgressStore from '@/store/progress'; +import { SIGN_IN_TOTAL_STEPS } from '../../HomePopup/constants/progress'; + export const syncBookmarksToStorage = async () => { const bookmarks = await trpcApi.firebaseData.bookmarksGet.query(); await bookmarksItem.setValue(bookmarks); diff --git a/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/utils/index.ts b/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/utils/index.ts index 96b9739b7..f2d995187 100644 --- a/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/utils/index.ts +++ b/apps/extension/src/entrypoints/popup/panels/BookmarksPanel/utils/index.ts @@ -1,4 +1,5 @@ import { type IBookmarksObj, type ISelectedBookmarks } from '@bypass/shared'; + import { bookmarksItem, hasPendingBookmarksItem } from '@/storage/items'; export const isFolderContainsDir = ( diff --git a/apps/extension/src/entrypoints/popup/panels/HomePopup/components/Authenticate.tsx b/apps/extension/src/entrypoints/popup/panels/HomePopup/components/Authenticate.tsx index ae6feca71..e79087b6a 100644 --- a/apps/extension/src/entrypoints/popup/panels/HomePopup/components/Authenticate.tsx +++ b/apps/extension/src/entrypoints/popup/panels/HomePopup/components/Authenticate.tsx @@ -1,12 +1,14 @@ import { Button, Spinner } from '@bypass/ui'; -import { HugeiconsIcon } from '@hugeicons/react'; import { Login02Icon, Logout02Icon } from '@hugeicons/core-free-icons'; -import useExtStore from '@store/extension'; +import { HugeiconsIcon } from '@hugeicons/react'; import { useCallback, useEffect, useState } from 'react'; import { toast } from 'sonner'; -import { signIn, signOut } from '../utils/authentication'; + import useFirebaseStore from '@/store/firebase/useFirebaseStore'; import useProgressStore from '@/store/progress'; +import useExtStore from '@store/extension'; + +import { signIn, signOut } from '../utils/authentication'; function Authenticate() { const isSignedIn = useFirebaseStore((state) => state.isSignedIn); diff --git a/apps/extension/src/entrypoints/popup/panels/HomePopup/components/BookmarksPanelButton.tsx b/apps/extension/src/entrypoints/popup/panels/HomePopup/components/BookmarksPanelButton.tsx index 8e1baec4e..a598d0e2d 100644 --- a/apps/extension/src/entrypoints/popup/panels/HomePopup/components/BookmarksPanelButton.tsx +++ b/apps/extension/src/entrypoints/popup/panels/HomePopup/components/BookmarksPanelButton.tsx @@ -1,8 +1,9 @@ import { getBookmarksPanelUrl } from '@bypass/shared'; import { Button } from '@bypass/ui'; -import { HugeiconsIcon } from '@hugeicons/react'; import { CollectionsBookmarkIcon } from '@hugeicons/core-free-icons'; +import { HugeiconsIcon } from '@hugeicons/react'; import { useLocation } from 'wouter'; + import useFirebaseStore from '@/store/firebase/useFirebaseStore'; function BookmarksPanelButton() { diff --git a/apps/extension/src/entrypoints/popup/panels/HomePopup/components/LastVisitedButton.tsx b/apps/extension/src/entrypoints/popup/panels/HomePopup/components/LastVisitedButton.tsx index d0f26f8bd..8f8f296b4 100644 --- a/apps/extension/src/entrypoints/popup/panels/HomePopup/components/LastVisitedButton.tsx +++ b/apps/extension/src/entrypoints/popup/panels/HomePopup/components/LastVisitedButton.tsx @@ -6,17 +6,18 @@ import { TooltipContent, TooltipTrigger, } from '@bypass/ui'; -import { HugeiconsIcon } from '@hugeicons/react'; import { CalendarAdd01Icon, Appointment01Icon, } from '@hugeicons/core-free-icons'; +import { HugeiconsIcon } from '@hugeicons/react'; import { useCallback, useEffect, useState } from 'react'; -import { getlastVisitedText } from '@popup/utils/lastVisited'; -import useCurrentTab from '@popup/hooks/useCurrentTab'; + import { trpcApi } from '@/apis/trpcApi'; -import useFirebaseStore from '@/store/firebase/useFirebaseStore'; import { lastVisitedItem } from '@/storage/items'; +import useFirebaseStore from '@/store/firebase/useFirebaseStore'; +import useCurrentTab from '@popup/hooks/useCurrentTab'; +import { getlastVisitedText } from '@popup/utils/lastVisited'; function LastVisitedButton() { const isSignedIn = useFirebaseStore((state) => state.isSignedIn); diff --git a/apps/extension/src/entrypoints/popup/panels/HomePopup/components/OpenDefaultsButton.tsx b/apps/extension/src/entrypoints/popup/panels/HomePopup/components/OpenDefaultsButton.tsx index 5340a85f4..aed9876d2 100644 --- a/apps/extension/src/entrypoints/popup/panels/HomePopup/components/OpenDefaultsButton.tsx +++ b/apps/extension/src/entrypoints/popup/panels/HomePopup/components/OpenDefaultsButton.tsx @@ -1,10 +1,11 @@ import { Button, Spinner } from '@bypass/ui'; -import { HugeiconsIcon } from '@hugeicons/react'; import { LinkSquare02Icon } from '@hugeicons/core-free-icons'; -import useHistoryStore from '@store/history'; +import { HugeiconsIcon } from '@hugeicons/react'; import { useState } from 'react'; -import useFirebaseStore from '@/store/firebase/useFirebaseStore'; + import { redirectionsItem } from '@/storage/items'; +import useFirebaseStore from '@/store/firebase/useFirebaseStore'; +import useHistoryStore from '@store/history'; function OpenDefaultsButton() { const startHistoryMonitor = useHistoryStore( diff --git a/apps/extension/src/entrypoints/popup/panels/HomePopup/components/OpenForumLinks/ButtonWithFeedback.tsx b/apps/extension/src/entrypoints/popup/panels/HomePopup/components/OpenForumLinks/ButtonWithFeedback.tsx index 5a1bd45e9..6b84dcc3f 100644 --- a/apps/extension/src/entrypoints/popup/panels/HomePopup/components/OpenForumLinks/ButtonWithFeedback.tsx +++ b/apps/extension/src/entrypoints/popup/panels/HomePopup/components/OpenForumLinks/ButtonWithFeedback.tsx @@ -1,9 +1,10 @@ import { Button, Spinner } from '@bypass/ui'; -import { HugeiconsIcon } from '@hugeicons/react'; import { CheckmarkBadge02Icon, WebDesign01Icon, } from '@hugeicons/core-free-icons'; +import { HugeiconsIcon } from '@hugeicons/react'; + import useFeedbackButton, { EButtonState } from './useFeedbackButton'; interface Props { @@ -17,10 +18,7 @@ function ButtonWithFeedback({ openAllLinks, isForumPage }: Props) { if (isForumPage && buttonState === EButtonState.SUCCESS) { return ( -
+
{redirections?.map((redirection, index) => { const isMatch = isMatchingRule(redirection, searchText); return ( @@ -148,9 +146,7 @@ function ShortcutsPanel() { {isFetching && (
diff --git a/apps/extension/src/entrypoints/popup/panels/ShortcutsPanel/routes/index.tsx b/apps/extension/src/entrypoints/popup/panels/ShortcutsPanel/routes/index.tsx index efb68f62f..8b9b62168 100644 --- a/apps/extension/src/entrypoints/popup/panels/ShortcutsPanel/routes/index.tsx +++ b/apps/extension/src/entrypoints/popup/panels/ShortcutsPanel/routes/index.tsx @@ -1,5 +1,6 @@ import { ROUTES } from '@bypass/shared'; import { Route } from 'wouter'; + import ShortcutsPanel from '../components/ShortcutsPanel'; export const ShortcutsPanelRoute = ( diff --git a/apps/extension/src/entrypoints/popup/panels/ShortcutsPanel/utils/index.ts b/apps/extension/src/entrypoints/popup/panels/ShortcutsPanel/utils/index.ts index 2be15567a..0f1a28e71 100644 --- a/apps/extension/src/entrypoints/popup/panels/ShortcutsPanel/utils/index.ts +++ b/apps/extension/src/entrypoints/popup/panels/ShortcutsPanel/utils/index.ts @@ -1,4 +1,5 @@ import { type IRedirection } from '@bypass/shared'; + import { DEFAULT_RULE_ALIAS } from '../constants'; export const getValidRules = (obj: IRedirection) => diff --git a/apps/extension/src/entrypoints/popup/provider/DynamicProvider.tsx b/apps/extension/src/entrypoints/popup/provider/DynamicProvider.tsx index f75fa40cc..7d29f2c4c 100644 --- a/apps/extension/src/entrypoints/popup/provider/DynamicProvider.tsx +++ b/apps/extension/src/entrypoints/popup/provider/DynamicProvider.tsx @@ -1,6 +1,7 @@ import { DynamicContext } from '@bypass/shared'; import { type PropsWithChildren, useMemo } from 'react'; import { useLocation, useSearch } from 'wouter'; + import { getFromChromeStorage, setToChromeStorage } from './utils'; function DynamicProvider({ children }: PropsWithChildren) { diff --git a/apps/extension/src/entrypoints/popup/utils/lastVisited.ts b/apps/extension/src/entrypoints/popup/utils/lastVisited.ts index 6f1ff2259..9ebf4f92c 100644 --- a/apps/extension/src/entrypoints/popup/utils/lastVisited.ts +++ b/apps/extension/src/entrypoints/popup/utils/lastVisited.ts @@ -1,4 +1,5 @@ import { sha256Hash } from '@bypass/shared'; + import { lastVisitedItem } from '@/storage/items'; export const getlastVisitedText = async (url: string) => { diff --git a/apps/extension/src/storage/items.ts b/apps/extension/src/storage/items.ts index e2500000f..07595f64b 100644 --- a/apps/extension/src/storage/items.ts +++ b/apps/extension/src/storage/items.ts @@ -1,4 +1,3 @@ -import { storage } from 'wxt/utils/storage'; import type { IBookmarksObj, IPersons, @@ -7,8 +6,10 @@ import type { IRedirections, IWebsites, } from '@bypass/shared'; -import type { IMappedRedirections } from '@/entrypoints/background/interfaces/redirections'; +import { storage } from 'wxt/utils/storage'; + import { EExtensionState } from '@/constants'; +import type { IMappedRedirections } from '@/entrypoints/background/interfaces/redirections'; export const bookmarksItem = storage.defineItem( 'local:bookmarks', diff --git a/apps/extension/src/store/firebase/api.ts b/apps/extension/src/store/firebase/api.ts index 23952299d..4cd698cb5 100644 --- a/apps/extension/src/store/firebase/api.ts +++ b/apps/extension/src/store/firebase/api.ts @@ -1,13 +1,15 @@ import { getFirebasePublicConfig } from '@bypass/configs/firebase.config'; import wretch from 'wretch'; import QueryStringAddon from 'wretch/addons/queryString'; -import { getExpiresAtMs } from './utils'; + import { env } from '@/constants/env'; import { type IAuthResponse, type IRefreshTokenResponse, } from '@/interfaces/firebase'; +import { getExpiresAtMs } from './utils'; + const firebaseConfig = getFirebasePublicConfig(import.meta.env.PROD); const identityApi = wretch('https://identitytoolkit.googleapis.com/v1') diff --git a/apps/extension/src/store/firebase/useFirebaseStore.ts b/apps/extension/src/store/firebase/useFirebaseStore.ts index 251d3a086..a58069496 100644 --- a/apps/extension/src/store/firebase/useFirebaseStore.ts +++ b/apps/extension/src/store/firebase/useFirebaseStore.ts @@ -1,5 +1,6 @@ import { create } from 'zustand'; import { persist } from 'zustand/middleware'; + import { TEST_AUTH_DATA_KEY } from '@/constants'; import { type IAuthResponse } from '@/interfaces/firebase'; import { refreshIdToken, signInWithCredential } from '@/store/firebase/api'; diff --git a/apps/extension/tests/auth.setup.ts b/apps/extension/tests/auth.setup.ts index b347c408c..9f7c178aa 100644 --- a/apps/extension/tests/auth.setup.ts +++ b/apps/extension/tests/auth.setup.ts @@ -1,9 +1,14 @@ import fs from 'node:fs'; import process from 'node:process'; -import { chromium, expect, test as setup } from '@playwright/test'; + import { TEST_TIMEOUTS } from '@bypass/shared/tests'; +import { chromium, expect, test as setup } from '@playwright/test'; import wretch from 'wretch'; import QueryStringAddon from 'wretch/addons/queryString'; + +import type { IAuthResponse } from '@/interfaces/firebase'; +import { getExpiresAtMs } from '@/store/firebase/utils'; + import { getFirebasePublicConfig } from '../../../packages/configs/firebase.config'; import { TEST_AUTH_DATA_KEY } from '../src/constants'; import { @@ -12,8 +17,6 @@ import { EXTENSION_STORAGE_PATH, } from './auth-constants'; import { getExtensionPath } from './utils/extension-path'; -import type { IAuthResponse } from '@/interfaces/firebase'; -import { getExpiresAtMs } from '@/store/firebase/utils'; const isCI = Boolean(process.env.PLAYWRIGHT_TEST_BASE_URL); const firebaseConfig = getFirebasePublicConfig(isCI); diff --git a/apps/extension/tests/fixtures/auth-fixture.ts b/apps/extension/tests/fixtures/auth-fixture.ts index 1f8f9a265..9e94e56e2 100644 --- a/apps/extension/tests/fixtures/auth-fixture.ts +++ b/apps/extension/tests/fixtures/auth-fixture.ts @@ -1,6 +1,5 @@ -/* eslint-disable react-hooks/rules-of-hooks */ -import { test as base } from './extension-fixture'; import { loadCachedStorageData } from './base-fixture'; +import { test as base } from './extension-fixture'; export const test = base.extend<{ login: void; diff --git a/apps/extension/tests/fixtures/background-fixture.ts b/apps/extension/tests/fixtures/background-fixture.ts index 5a87da931..d6bf5b082 100644 --- a/apps/extension/tests/fixtures/background-fixture.ts +++ b/apps/extension/tests/fixtures/background-fixture.ts @@ -1,7 +1,8 @@ -/* eslint-disable react-hooks/rules-of-hooks */ import fs from 'node:fs/promises'; import os from 'node:os'; import path from 'node:path'; + +import { TEST_TIMEOUTS } from '@bypass/shared/tests'; import { chromium, type BrowserContext, @@ -9,14 +10,15 @@ import { test as base, type Worker, } from '@playwright/test'; -import { TEST_TIMEOUTS } from '@bypass/shared/tests'; + +import { EExtensionState } from '@/constants'; + import { getExtensionPath } from '../utils/extension-path'; import { createSharedBackgroundSW, createSharedContext, getExtensionId, } from './base-fixture'; -import { EExtensionState } from '@/constants'; interface BaseBackgroundEnv { extensionId: string; diff --git a/apps/extension/tests/fixtures/base-fixture.ts b/apps/extension/tests/fixtures/base-fixture.ts index 5605c39af..32171c5a0 100644 --- a/apps/extension/tests/fixtures/base-fixture.ts +++ b/apps/extension/tests/fixtures/base-fixture.ts @@ -1,13 +1,15 @@ import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; + +import { TEST_TIMEOUTS } from '@bypass/shared/tests'; import { type BrowserContext, type Page, type Worker, chromium, } from '@playwright/test'; -import { TEST_TIMEOUTS } from '@bypass/shared/tests'; + import { CHROME_PROFILE_DIR, EXTENSION_STORAGE_PATH } from '../auth-constants'; import { getExtensionPath } from '../utils/extension-path'; diff --git a/apps/extension/tests/fixtures/bookmark-fixture.ts b/apps/extension/tests/fixtures/bookmark-fixture.ts index 0a15bf15a..0d306265e 100644 --- a/apps/extension/tests/fixtures/bookmark-fixture.ts +++ b/apps/extension/tests/fixtures/bookmark-fixture.ts @@ -1,10 +1,10 @@ -/* eslint-disable react-hooks/rules-of-hooks */ import { type BrowserContext, type Page, type Worker, test as base, } from '@playwright/test'; + import { createSharedBackgroundSW, createSharedContext, diff --git a/apps/extension/tests/fixtures/extension-fixture.ts b/apps/extension/tests/fixtures/extension-fixture.ts index d317bdfb0..8e2aba1ff 100644 --- a/apps/extension/tests/fixtures/extension-fixture.ts +++ b/apps/extension/tests/fixtures/extension-fixture.ts @@ -1,13 +1,14 @@ -/* eslint-disable react-hooks/rules-of-hooks */ import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; + import { type Worker, test as base, chromium, type BrowserContext, } from '@playwright/test'; + import { getExtensionPath } from '../utils/extension-path'; export const test = base.extend<{ diff --git a/apps/extension/tests/fixtures/home-popup-fixture.ts b/apps/extension/tests/fixtures/home-popup-fixture.ts index 9d0b2c0cd..2a50d9033 100644 --- a/apps/extension/tests/fixtures/home-popup-fixture.ts +++ b/apps/extension/tests/fixtures/home-popup-fixture.ts @@ -1,10 +1,10 @@ -/* eslint-disable react-hooks/rules-of-hooks */ import { type BrowserContext, type Page, type Worker, test as base, } from '@playwright/test'; + import { getExtensionPath } from '../utils/extension-path'; import { createSharedBackgroundSW, diff --git a/apps/extension/tests/fixtures/persons-fixture.ts b/apps/extension/tests/fixtures/persons-fixture.ts index 21a9e91ac..42ecd92a6 100644 --- a/apps/extension/tests/fixtures/persons-fixture.ts +++ b/apps/extension/tests/fixtures/persons-fixture.ts @@ -1,10 +1,10 @@ -/* eslint-disable react-hooks/rules-of-hooks */ import { type BrowserContext, type Page, type Worker, test as base, } from '@playwright/test'; + import { createSharedBackgroundSW, createSharedContext, diff --git a/apps/extension/tests/fixtures/shortcuts-fixture.ts b/apps/extension/tests/fixtures/shortcuts-fixture.ts index 2788bdebc..8e270f711 100644 --- a/apps/extension/tests/fixtures/shortcuts-fixture.ts +++ b/apps/extension/tests/fixtures/shortcuts-fixture.ts @@ -1,10 +1,10 @@ -/* eslint-disable react-hooks/rules-of-hooks */ import { type BrowserContext, type Page, type Worker, test as base, } from '@playwright/test'; + import { createSharedBackgroundSW, createSharedContext, diff --git a/apps/extension/tests/global-teardown.ts b/apps/extension/tests/global-teardown.ts index 727b7b3d9..d24bc35c2 100644 --- a/apps/extension/tests/global-teardown.ts +++ b/apps/extension/tests/global-teardown.ts @@ -1,5 +1,7 @@ import fs from 'node:fs'; + import { test as teardown } from '@playwright/test'; + import { AUTH_CACHE_DIR } from './auth-constants'; teardown('clean up cache directory', async () => { diff --git a/apps/extension/tests/specs/background-lifecycle.spec.ts b/apps/extension/tests/specs/background-lifecycle.spec.ts index 6da6613b2..aa7de50da 100644 --- a/apps/extension/tests/specs/background-lifecycle.spec.ts +++ b/apps/extension/tests/specs/background-lifecycle.spec.ts @@ -1,6 +1,7 @@ -import { test, expect } from '../fixtures/background-fixture'; import { EExtensionState } from '@/constants'; +import { test, expect } from '../fixtures/background-fixture'; + test.describe('Background Service Worker Lifecycle', () => { test('onInstalled writes extState=active on first install', async ({ isolatedBackground, diff --git a/apps/extension/tests/specs/background-navigation.spec.ts b/apps/extension/tests/specs/background-navigation.spec.ts index c9ff265c1..2c0669619 100644 --- a/apps/extension/tests/specs/background-navigation.spec.ts +++ b/apps/extension/tests/specs/background-navigation.spec.ts @@ -1,5 +1,6 @@ import { TEST_SHORTCUTS } from '@bypass/shared/tests'; import type { Page } from '@playwright/test'; + import { test, expect } from '../fixtures/background-fixture'; const allInputsAutocompleteOff = async (page: Page) => { diff --git a/apps/extension/tests/specs/bookmarks.spec.ts b/apps/extension/tests/specs/bookmarks.spec.ts index eef982cad..fdd584d61 100644 --- a/apps/extension/tests/specs/bookmarks.spec.ts +++ b/apps/extension/tests/specs/bookmarks.spec.ts @@ -6,6 +6,7 @@ import { fillSearchInput, openNewPageFromAction, } from '@bypass/shared/tests'; + import { test, expect } from '../fixtures/bookmark-fixture'; import { BookmarksPanel } from '../utils/bookmarks-panel'; import { PersonsPanel } from '../utils/persons-panel'; diff --git a/apps/extension/tests/specs/persons.spec.ts b/apps/extension/tests/specs/persons.spec.ts index 74393c7cf..4942e806c 100644 --- a/apps/extension/tests/specs/persons.spec.ts +++ b/apps/extension/tests/specs/persons.spec.ts @@ -5,6 +5,7 @@ import { closeDialog, fillSearchInput, } from '@bypass/shared/tests'; + import { test, expect } from '../fixtures/persons-fixture'; import { PersonsPanel } from '../utils/persons-panel'; diff --git a/apps/extension/tests/specs/shortcuts.spec.ts b/apps/extension/tests/specs/shortcuts.spec.ts index 259705bb9..350d5368a 100644 --- a/apps/extension/tests/specs/shortcuts.spec.ts +++ b/apps/extension/tests/specs/shortcuts.spec.ts @@ -4,6 +4,7 @@ import { fillSearchInput, openNewPageFromAction, } from '@bypass/shared/tests'; + import { test, expect } from '../fixtures/shortcuts-fixture'; import { ShortcutsPanel } from '../utils/shortcuts-panel'; diff --git a/apps/extension/tests/specs/toggle-history.spec.ts b/apps/extension/tests/specs/toggle-history.spec.ts index 85a361968..fce1d8b5b 100644 --- a/apps/extension/tests/specs/toggle-history.spec.ts +++ b/apps/extension/tests/specs/toggle-history.spec.ts @@ -3,10 +3,11 @@ import { TEST_BOOKMARKS, openNewPageFromAction, } from '@bypass/shared/tests'; + import { test, expect } from '../fixtures/home-popup-fixture'; import { BookmarksPanel } from '../utils/bookmarks-panel'; -import { PopupHomePanel } from '../utils/home-panel'; import { getHistoryItems } from '../utils/history-utils'; +import { PopupHomePanel } from '../utils/home-panel'; /** * ToggleHistory E2E Tests diff --git a/apps/extension/tests/utils/bookmarks-panel.ts b/apps/extension/tests/utils/bookmarks-panel.ts index 2c271b25a..df9b55431 100644 --- a/apps/extension/tests/utils/bookmarks-panel.ts +++ b/apps/extension/tests/utils/bookmarks-panel.ts @@ -1,4 +1,5 @@ import { expect, type Page } from '@playwright/test'; + import { clickDropdownPersonAndGetName, clickContextMenuItem as clickContextMenuItemUtil, diff --git a/apps/extension/tests/utils/home-panel.ts b/apps/extension/tests/utils/home-panel.ts index faee798cc..c0fe4218d 100644 --- a/apps/extension/tests/utils/home-panel.ts +++ b/apps/extension/tests/utils/home-panel.ts @@ -1,4 +1,5 @@ import { expect, type Page } from '@playwright/test'; + import { getStorageItem } from './test-utils'; export class PopupHomePanel { diff --git a/apps/extension/tests/utils/persons-panel.ts b/apps/extension/tests/utils/persons-panel.ts index c60645144..51940656b 100644 --- a/apps/extension/tests/utils/persons-panel.ts +++ b/apps/extension/tests/utils/persons-panel.ts @@ -1,4 +1,5 @@ import { expect, type Page } from '@playwright/test'; + import { clickDialogButton, clickContextMenuItem, diff --git a/apps/extension/tests/utils/test-utils.ts b/apps/extension/tests/utils/test-utils.ts index 2fc138d97..085d010cf 100644 --- a/apps/extension/tests/utils/test-utils.ts +++ b/apps/extension/tests/utils/test-utils.ts @@ -1,8 +1,8 @@ -import { expect, type Page } from '@playwright/test'; import { fillSearchInput as fillSearchInputShared, parseBadgeCount, } from '@bypass/shared/tests'; +import { expect, type Page } from '@playwright/test'; // Re-export shared utilities for convenience diff --git a/apps/extension/wxt.config.ts b/apps/extension/wxt.config.ts index 5415be129..fb688e976 100644 --- a/apps/extension/wxt.config.ts +++ b/apps/extension/wxt.config.ts @@ -1,9 +1,11 @@ import path from 'node:path'; import { fileURLToPath } from 'node:url'; + import babel from '@rolldown/plugin-babel'; -import react, { reactCompilerPreset } from '@vitejs/plugin-react'; import tailwindcss from '@tailwindcss/vite'; +import react, { reactCompilerPreset } from '@vitejs/plugin-react'; import { defineConfig } from 'wxt'; + import { devManifest, prodOAuth2 } from './src/constants/manifest'; const envDir = path.resolve( diff --git a/apps/web/next.config.ts b/apps/web/next.config.ts index 70df31445..d1202225d 100644 --- a/apps/web/next.config.ts +++ b/apps/web/next.config.ts @@ -1,5 +1,6 @@ import path from 'node:path'; import { fileURLToPath } from 'node:url'; + import { type NextConfig } from 'next'; if (!process.env.VERCEL) { @@ -28,6 +29,15 @@ const nextConfig: NextConfig = { removeConsole: isDev ? false : { exclude: ['error'] }, }, transpilePackages: ['@bypass/shared', '@bypass/trpc', '@bypass/ui'], + // Next 16.3 auto-generates apps/web/AGENTS.md + CLAUDE.md; the repo maintains a + // single root AGENTS.md, so disable the per-app generation. + agentRules: false, + experimental: { + // TODO: TypeScript 7's native package drops the JS compiler API that Next's + // default backend uses; this runs the local `tsc` (via tsc --showConfig) + // for type info + tsconfig paths instead. Requires Next >= 16.3. + useTypeScriptCli: true, + }, }; export default nextConfig; diff --git a/apps/web/package.json b/apps/web/package.json index 4a6936d5b..21b9676af 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -36,12 +36,12 @@ }, "devDependencies": { "@playwright/test": "catalog:", + "@tailwindcss/postcss": "catalog:", "@types/react": "catalog:", "autoprefixer": "catalog:", "babel-plugin-react-compiler": "catalog:", "postcss": "catalog:", "tailwindcss": "catalog:", - "@tailwindcss/postcss": "catalog:", "typescript": "catalog:" } } diff --git a/apps/web/src/app/api/storage-cleanup/route.ts b/apps/web/src/app/api/storage-cleanup/route.ts index 4ba510f47..9395b62dd 100644 --- a/apps/web/src/app/api/storage-cleanup/route.ts +++ b/apps/web/src/app/api/storage-cleanup/route.ts @@ -1,8 +1,9 @@ -import { verifyInternalToken } from '@app/helpers/verifyInternalToken'; import { cleanupStorage } from '@bypass/trpc'; -import { serverEnv } from '@app/constants/env/server'; import { type NextRequest, NextResponse } from 'next/server'; +import { serverEnv } from '@app/constants/env/server'; +import { verifyInternalToken } from '@app/helpers/verifyInternalToken'; + export async function POST(req: NextRequest) { verifyInternalToken(req); diff --git a/apps/web/src/app/api/upload-file/route.ts b/apps/web/src/app/api/upload-file/route.ts index 4b3343242..748fdd3e7 100644 --- a/apps/web/src/app/api/upload-file/route.ts +++ b/apps/web/src/app/api/upload-file/route.ts @@ -1,6 +1,8 @@ -import { authorizeUser } from '@app/helpers/authorizeUser'; import { uploadImageToFirebase } from '@bypass/trpc/appRouter'; import { type NextRequest, NextResponse } from 'next/server'; + +import { authorizeUser } from '@app/helpers/authorizeUser'; + import { validateAndProccessFile } from './utils'; export async function POST(request: NextRequest) { diff --git a/apps/web/src/app/api/upload-file/utils.ts b/apps/web/src/app/api/upload-file/utils.ts index c8d1eca0b..14a10bd07 100644 --- a/apps/web/src/app/api/upload-file/utils.ts +++ b/apps/web/src/app/api/upload-file/utils.ts @@ -1,4 +1,3 @@ -/* eslint-disable n/prefer-global/buffer */ import { fileTypeFromBuffer } from 'file-type'; import sharp from 'sharp'; diff --git a/apps/web/src/app/bookmark-panel/components/VirtualRow.tsx b/apps/web/src/app/bookmark-panel/components/VirtualRow.tsx index 33d24b6c1..930dfbef9 100644 --- a/apps/web/src/app/bookmark-panel/components/VirtualRow.tsx +++ b/apps/web/src/app/bookmark-panel/components/VirtualRow.tsx @@ -1,4 +1,3 @@ -import { openNewTab } from '@app/utils'; import { Bookmark, type ContextBookmarks, @@ -9,6 +8,8 @@ import { } from '@bypass/shared'; import { memo } from 'react'; +import { openNewTab } from '@app/utils'; + interface Props { index: number; folders: IBookmarksObj['folders']; @@ -23,12 +24,7 @@ const VirtualRow = memo(({ index, folders, contextBookmarks }) => { const ctx = contextBookmarks[index]; return ( -
+
{ctx.isDir ? ( { if (isExistsInLocalStorage(STORAGE_KEYS.bookmarks)) { return; diff --git a/apps/web/src/app/bookmark-panel/page.tsx b/apps/web/src/app/bookmark-panel/page.tsx index dd342af3a..bf5ce6443 100644 --- a/apps/web/src/app/bookmark-panel/page.tsx +++ b/apps/web/src/app/bookmark-panel/page.tsx @@ -1,6 +1,5 @@ 'use client'; -import { getFromLocalStorage, setToLocalStorage } from '@app/utils/storage'; import { BOOKMARK_ROW_HEIGHT, bookmarksMapper, @@ -17,6 +16,9 @@ import { ScrollArea } from '@bypass/ui'; import { useVirtualizer } from '@tanstack/react-virtual'; import { useSearchParams } from 'next/navigation'; import { useCallback, useEffect, useRef, useState } from 'react'; + +import { getFromLocalStorage, setToLocalStorage } from '@app/utils/storage'; + import VirtualRow from './components/VirtualRow'; export default function BookmarksPage() { diff --git a/apps/web/src/app/components/AppHeader.tsx b/apps/web/src/app/components/AppHeader.tsx index 53059cd79..39e77354a 100644 --- a/apps/web/src/app/components/AppHeader.tsx +++ b/apps/web/src/app/components/AppHeader.tsx @@ -1,12 +1,13 @@ 'use client'; -import { ROUTES } from '@app/constants/routes'; import { GithubIcon } from '@hugeicons/core-free-icons'; import { HugeiconsIcon } from '@hugeicons/react'; import Image from 'next/image'; import { useRouter } from 'next/navigation'; import { useEffect, useState } from 'react'; +import { ROUTES } from '@app/constants/routes'; + function AppHeader() { const router = useRouter(); const [clickCount, setClickCount] = useState(0); @@ -18,28 +19,14 @@ function AppHeader() { }, [clickCount, router]); return ( -
-
+
+
diff --git a/apps/web/src/app/provider/AppProviders.tsx b/apps/web/src/app/provider/AppProviders.tsx index 36ac3b8a4..a1a6dd5b3 100644 --- a/apps/web/src/app/provider/AppProviders.tsx +++ b/apps/web/src/app/provider/AppProviders.tsx @@ -2,8 +2,9 @@ import { TooltipProvider } from '@bypass/ui'; import { Analytics } from '@vercel/analytics/react'; -import { StrictMode, Suspense } from 'react'; import { SpeedInsights } from '@vercel/speed-insights/next'; +import { StrictMode, Suspense } from 'react'; + import { AuthProvider } from './AuthProvider'; import DynamicProvider from './DynamicProvider'; diff --git a/apps/web/src/app/provider/AuthProvider.tsx b/apps/web/src/app/provider/AuthProvider.tsx index 4d89e0dbe..77f6dff68 100644 --- a/apps/web/src/app/provider/AuthProvider.tsx +++ b/apps/web/src/app/provider/AuthProvider.tsx @@ -8,6 +8,7 @@ import { useMemo, useState, } from 'react'; + import { ROUTES } from '../constants/routes'; import { onAuthStateChange } from '../helpers/firebase/auth'; diff --git a/apps/web/src/app/provider/DynamicProvider.tsx b/apps/web/src/app/provider/DynamicProvider.tsx index 7a071ae8b..8637a4449 100644 --- a/apps/web/src/app/provider/DynamicProvider.tsx +++ b/apps/web/src/app/provider/DynamicProvider.tsx @@ -1,6 +1,7 @@ import { DynamicContext } from '@bypass/shared'; import { useRouter, useSearchParams } from 'next/navigation'; import { type PropsWithChildren, useMemo } from 'react'; + import { getFromLocalStorage, setToLocalStorage } from '../utils/storage'; function DynamicProvider({ children }: PropsWithChildren) { @@ -15,7 +16,6 @@ function DynamicProvider({ children }: PropsWithChildren) { goBack: () => router.back(), }, storage: { - // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion get: async (key: string) => getFromLocalStorage(key) as any, set: async (key: string, value: any) => setToLocalStorage(key, value), }, diff --git a/apps/web/src/app/utils/api.ts b/apps/web/src/app/utils/api.ts index 0b4b56c10..5be07fc9d 100644 --- a/apps/web/src/app/utils/api.ts +++ b/apps/web/src/app/utils/api.ts @@ -1,7 +1,8 @@ -import { getAuthIdToken } from '@app/helpers/firebase/auth'; import { type AppRouter } from '@bypass/trpc'; import { createTRPCClient, httpBatchLink, loggerLink } from '@trpc/client'; +import { getAuthIdToken } from '@app/helpers/firebase/auth'; + const getBaseUrl = () => { if (typeof window !== 'undefined') { return ''; diff --git a/apps/web/src/app/web-ext/page.tsx b/apps/web/src/app/web-ext/page.tsx index 4dd5d2401..3177218ae 100644 --- a/apps/web/src/app/web-ext/page.tsx +++ b/apps/web/src/app/web-ext/page.tsx @@ -1,14 +1,7 @@ 'use client'; -import { - googleSignIn, - googleSignOut, - emailAndPasswordSignIn, -} from '@app/helpers/firebase/auth'; -import { useUser } from '@app/provider/AuthProvider'; import { Header, ROUTES } from '@bypass/shared'; import { Button, Spinner } from '@bypass/ui'; -import { TEST_CREDENTIALS_KEY } from '@app/constants'; import { Bookmark01Icon, Login02Icon, @@ -18,6 +11,15 @@ import { import { HugeiconsIcon } from '@hugeicons/react'; import { useRouter } from 'next/navigation'; import { useEffect, useState } from 'react'; + +import { TEST_CREDENTIALS_KEY } from '@app/constants'; +import { + googleSignIn, + googleSignOut, + emailAndPasswordSignIn, +} from '@app/helpers/firebase/auth'; +import { useUser } from '@app/provider/AuthProvider'; + import useWebPreload from './hooks/useWebPreload'; export default function Web() { @@ -72,12 +74,7 @@ export default function Web() {
-
+