Conversation
Replace ESLint (v10) and Prettier (v3.8) with oxlint and oxfmt from the Oxc project for significantly faster linting and formatting (~180ms lint, ~490ms format across 400+ files). - Remove eslint, eslint-config-next, prettier, and prettier plugins - Add oxlint and oxfmt with equivalent rule configurations - Built-in import sorting and Tailwind class sorting (no plugins needed) - Fix all lint warnings: unused imports, missing alt/lang attrs, iframe sandbox - Update package.json scripts, lint-staged, and lefthook pre-commit hook - Update CLAUDE.md documentation references Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Code Review: refactor(lint) — ESLint + Prettier → oxlint + oxfmtThis is a well-motivated migration. The tooling switch is clean, the scope is clearly bounded (formatting-only changes across source files), and the speed gains are real. A few things worth noting for follow-up: Bugs / Issues
"build": "npm run codegen && next build --turbo",
"test:update": "npm test -- --updateSnapshot",CLAUDE.md explicitly states: "This project uses pnpm exclusively. Do not use npm or yarn." These should be Pre-commit Hook Missing Formatter
Suggestion — add an pre-commit:
parallel: true
commands:
oxlint:
glob: "*.{js,ts,jsx,tsx}"
run: pnpm run lint:fix {staged_files}
oxfmt:
glob: "*.{js,ts,tsx,css,md,json}"
run: pnpm run format {staged_files}Dead Code: Husky Config in
|
| Rule | Risk |
|---|---|
react-hooks/exhaustive-deps: "off" |
Stale closures and subtle bugs in hooks |
@typescript-eslint/no-explicit-any: "off" |
Gradual erosion of type safety |
@next/next/no-img-element: "off" |
May miss <img> tags that should use Next.js <Image> |
Not blockers for this PR — but enabling them in a follow-up with a targeted fix pass would improve code quality.
Minor: Local $schema Path in .oxfmtrc.json
The schema path "./node_modules/oxfmt/configuration_schema.json" works locally but silently fails in editors without node_modules installed. An upstream hosted URL would be more portable for IDE schema validation.
Overall: Good cleanup — removing 176 packages and getting sub-200ms lint times is a meaningful win. The main actionable follow-ups are fixing npm run to pnpm run in scripts and adding oxfmt to the lefthook pre-commit hook so formatting is enforced locally.
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request overhauls the project's code quality infrastructure by adopting Oxc's Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
|
|
||
| return ( | ||
| <html | ||
| lang="en" |
There was a problem hiding this comment.
🔴 Hardcoded lang="en" prevents CJK font CSS rules from matching
The PR adds a hardcoded lang="en" to the <html> element, but the app supports English, Chinese, Japanese, and Korean. The CSS in src/styles/global.css:117-127 contains language-specific font rules (html[lang='zh'], html[lang='ja'], html[lang='ko']) that apply the LxgwWenKai font for CJK languages. With lang hardcoded to "en", these rules will never match, so CJK users won't get the proper font at the page level. The i18n client config at src/i18n/client.ts:43 only uses ['cookie'] detection order (the htmlTag detector is commented out), so i18next will not dynamically update the lang attribute. The lang should be dynamic based on the user's selected language.
Prompt for agents
In src/app/layout.tsx, the lang attribute on the <html> element at line 86 is hardcoded to "en". This needs to be dynamic based on the user's language preference. Since this is a server component, you should read the language cookie (STORAGE_LANG_KEY = 'ck-lang') from next/headers cookies() and use it to set the lang attribute. Fallback to 'en' if no cookie is present. For example:
import { cookies } from 'next/headers'
import { STORAGE_LANG_KEY } from '@/constants/storage'
Then inside the Layout function:
const cookieStore = await cookies()
const lang = cookieStore.get(STORAGE_LANG_KEY)?.value || 'en'
And use lang={lang} on the <html> element instead of lang="en".
This ensures that the CSS font rules in src/styles/global.css (html[lang='zh'], html[lang='ja'], html[lang='ko']) will properly match and apply the LxgwWenKai font for CJK users.
Was this helpful? React with 👍 or 👎 to provide feedback.
| width='1024px' | ||
| title="guide" | ||
| src="//player.bilibili.com/player.html?aid=503430935&bvid=BV1Tg411G7gG&cid=349347987&page=1" | ||
| sandbox="allow-scripts allow-popups" |
There was a problem hiding this comment.
🔴 iframe sandbox without allow-same-origin breaks embedded bilibili video player
The PR adds sandbox="allow-scripts allow-popups" to bilibili video player iframes in two export settings pages. Without allow-same-origin, the sandboxed iframe content is treated as being from a unique opaque origin, preventing it from accessing cookies, localStorage, and making same-origin requests to bilibili's servers. This will cause the video player to fail to load or play content, as video players typically need these capabilities for authentication, content delivery, and playback. The sandbox attribute was not present before this PR.
Affected files
src/app/dash/[userid]/settings/exports/export.email.tsx:82src/app/dash/[userid]/settings/exports/export.notion.tsx:106
| sandbox="allow-scripts allow-popups" | |
| sandbox="allow-scripts allow-popups allow-same-origin" |
Was this helpful? React with 👍 or 👎 to provide feedback.
| )} | ||
| onClick={() => props.onSelecte(book)} | ||
| aria-selected={selected} | ||
| data-selected={selected} |
There was a problem hiding this comment.
🟡 aria-selected replaced with non-semantic data-selected, removing accessibility state
The aria-selected={selected} attribute on the book candidate <li> element was changed to data-selected={selected}. This removes the ARIA state that communicates the selected/deselected status of list items to assistive technologies (screen readers). Users relying on screen readers will no longer be informed which book candidate is currently selected. The data-selected attribute has no accessibility semantics.
| data-selected={selected} | |
| aria-selected={selected} |
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Code Review
This pull request is a great initiative to modernize the linting and formatting setup by migrating from ESLint and Prettier to the much faster Oxc toolchain (oxlint and oxfmt). The changes are extensive and include updating configuration files, package dependencies, and scripts, as well as applying the new formatting across the codebase. The author also took the opportunity to fix several existing lint issues, such as missing lang and sandbox attributes, which improves accessibility and security.
My review focuses on ensuring the new configuration is robust and effective. I've pointed out a few areas for improvement:
- The new oxlint configuration disables several important rules, particularly
react-hooks/exhaustive-depsand some accessibility rules. I've recommended re-enabling these to prevent potential bugs and improve accessibility. - The pre-commit hook configuration in
lefthook.ymlseems to have a bug that would cause it to lint the entire project on every commit, instead of just the staged files. I've suggested a fix for this. - There appears to be a redundant
lint-stagedconfiguration inpackage.jsonwhich could be removed to simplify the setup.
Overall, this is a valuable refactoring. Addressing these configuration points will help fully leverage the benefits of the new tooling.
| "rules": { | ||
| "@typescript-eslint/no-explicit-any": "off", | ||
| "@typescript-eslint/no-unused-vars": "warn", | ||
| "@typescript-eslint/no-non-null-assertion": "off", | ||
| "react/react-in-jsx-scope": "off", | ||
| "react/no-unescaped-entities": "off", | ||
| "react-hooks/exhaustive-deps": "off", | ||
| "@next/next/no-img-element": "off", | ||
| "@next/next/no-html-link-for-pages": "off", | ||
| "prefer-template": "warn", | ||
| "no-useless-escape": "warn", | ||
| "import/no-unassigned-import": "off", | ||
| "import/no-named-as-default": "off", | ||
| "import/no-named-as-default-member": "off", | ||
| "no-shadow": "off", | ||
| "react/no-array-index-key": "off", | ||
| "jsx-a11y/no-static-element-interactions": "off", | ||
| "jsx-a11y/click-events-have-key-events": "off", | ||
| "no-await-in-loop": "off" | ||
| }, |
There was a problem hiding this comment.
Disabling important linting rules like react-hooks/exhaustive-deps and accessibility rules (jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events) can introduce subtle bugs and accessibility issues. While these might have been disabled before, this migration is a good opportunity to enable them (at least as warnings) and address the violations. This would significantly improve the application's correctness and accessibility. For cases where a rule is too noisy, you can disable it for a specific line with an explanatory comment.
| oxlint: | ||
| glob: "*.{js,ts,jsx,tsx}" | ||
| run: npm run lint -- --fix {staged_files} | ||
| run: pnpm run lint:fix {staged_files} |
There was a problem hiding this comment.
The current command pnpm run lint:fix {staged_files} will execute oxlint --fix src/ and ignore the {staged_files} placeholder, because pnpm doesn't pass arguments to scripts without --. This means the pre-commit hook will lint and fix the entire src/ directory, not just the staged files, which is inefficient and likely not the intended behavior. To fix this and lint only staged files, you should call oxlint directly.
run: pnpm oxlint --fix {staged_files}| "lint-staged": { | ||
| "*.{js,ts,tsx}": [ | ||
| "eslint --fix", | ||
| "prettier --write", | ||
| "oxlint --fix", | ||
| "oxfmt", | ||
| "git add" | ||
| ], | ||
| "*.{css,md,json}": [ | ||
| "prettier --write", | ||
| "oxfmt", | ||
| "git add" | ||
| ] | ||
| }, |
There was a problem hiding this comment.
It seems lefthook is being used for pre-commit hooks, with commands defined in lefthook.yml. This lint-staged configuration appears to be redundant and might not be executed. To avoid confusion and keep the configuration clean, consider either removing this lint-staged section or configuring lefthook to run lint-staged.
| width='1024px' | ||
| title="guide" | ||
| src="//player.bilibili.com/player.html?aid=503430935&bvid=BV1Tg411G7gG&cid=349347987&page=1" | ||
| sandbox="allow-scripts allow-popups" |
There was a problem hiding this comment.
Adding the sandbox attribute to iframes is a crucial security measure that restricts the content within the iframe from performing potentially malicious actions. However, the current implementation sandbox="allow-scripts allow-popups" is still quite permissive. For embedding content from a trusted source like Bilibili, this might be acceptable, but it's worth evaluating if more restrictions can be applied. For instance, if the embedded player doesn't need to open popups, you could remove allow-popups. The principle of least privilege should be applied.
| width='1024px' | ||
| title="guide" | ||
| src="//player.bilibili.com/player.html?aid=503430935&bvid=BV1Tg411G7gG&cid=349347987&page=1" | ||
| sandbox="allow-scripts allow-popups" |
There was a problem hiding this comment.
Adding the sandbox attribute to iframes is a crucial security measure that restricts the content within the iframe from performing potentially malicious actions. However, the current implementation sandbox="allow-scripts allow-popups" is still quite permissive. For embedding content from a trusted source like Bilibili, this might be acceptable, but it's worth evaluating if more restrictions can be applied. For instance, if the embedded player doesn't need to open popups, you could remove allow-popups. The principle of least privilege should be applied.
|
|
||
| return ( | ||
| <html | ||
| lang="en" |
There was a problem hiding this comment.
Adding lang="en" to the <html> tag is a great improvement for accessibility and SEO. However, since the application supports multiple languages (as seen from the i18n setup), this should be set dynamically based on the user's selected language. Hardcoding it to en will be incorrect for users browsing in other languages.
Summary
Test plan
pnpm lint— 0 warnings, 0 errorspnpm format:check— all files correctly formattedpnpm test— all 7 tests passpnpm build— verify production build succeeds🤖 Generated with Claude Code