Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions .eslintignore

This file was deleted.

37 changes: 0 additions & 37 deletions .eslintrc

This file was deleted.

14 changes: 10 additions & 4 deletions eslint-local-rules/rules/metadata.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,19 @@ function parseExpectedErrorsEntries(rawEntries) {

if (parsed && typeof parsed === 'object') {
for (const [key, value] of Object.entries(parsed)) {
entries[key] = normalizeEntryValues(Array.isArray(value) ? value.flat() : value);
entries[key] = normalizeEntryValues(
Array.isArray(value) ? value.flat() : value
);
}
}

return entries;
}

function parseExpectedErrorsToken(tokenText) {
const match = tokenText.match(/^\{\s*expectedErrors\s*:\s*(\{[\s\S]*\})\s*\}$/);
const match = tokenText.match(
/^\{\s*expectedErrors\s*:\s*(\{[\s\S]*\})\s*\}$/
);
if (!match) {
return null;
}
Expand All @@ -103,7 +107,7 @@ function parseExpectedErrorsToken(tokenText) {

try {
entries = parseExpectedErrorsEntries(entriesSource);
} catch (error) {
} catch (_error) {
parseError = true;
entries = {};
}
Expand Down Expand Up @@ -203,7 +207,9 @@ function cloneMetadata(metadata) {
}

function findExpectedErrorsToken(metadata) {
return metadata.tokens.find((token) => token.type === 'expectedErrors') || null;
return (
metadata.tokens.find((token) => token.type === 'expectedErrors') || null
);
}

function getCompilerExpectedLines(metadata) {
Expand Down
16 changes: 9 additions & 7 deletions eslint-local-rules/rules/react-compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ function runReactCompiler(code, filename) {
configFile: false,
babelrc: false,
});
} catch (error) {
} catch (_error) {
return {...result, diagnostics: []};
}

Expand All @@ -98,17 +98,19 @@ function runReactCompiler(code, filename) {
continue;
}

const loc = typeof detail.primaryLocation === 'function'
? detail.primaryLocation()
: null;
const loc =
typeof detail.primaryLocation === 'function'
? detail.primaryLocation()
: null;

if (loc == null || typeof loc === 'symbol') {
continue;
}

const message = typeof detail.printErrorMessage === 'function'
? detail.printErrorMessage(result.sourceCode, {eslint: true})
: detail.description || 'Unknown React Compiler error';
const message =
typeof detail.printErrorMessage === 'function'
? detail.printErrorMessage(result.sourceCode, {eslint: true})
: detail.description || 'Unknown React Compiler error';

diagnostics.push({detail, loc, message});
}
Expand Down
87 changes: 87 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import nextCoreWebVitals from "eslint-config-next/core-web-vitals";
import reactCompiler from "eslint-plugin-react-compiler";
import localRules from "eslint-plugin-local-rules";
import globals from "globals";
import tsParser from "@typescript-eslint/parser";
import parser from "./eslint-local-rules/parser.js";

// Extract plugin instances from next's config to avoid
// "Cannot redefine plugin" errors in flat config.
function getNextPlugin(name) {
return nextCoreWebVitals.find((c) => c.plugins?.[name])?.plugins[name];
}
const tsPlugin = getNextPlugin("@typescript-eslint");
const reactHooksPlugin = getNextPlugin("react-hooks");

export default [

Check warning on line 16 in eslint.config.mjs

View workflow job for this annotation

GitHub Actions / Lint on node 20.x and ubuntu-latest

Assign array to a variable before exporting as module default
{
ignores: [
"**/scripts",
"**/plugins",
"**/next.config.js",
"**/.claude/",
"**/worker-bundle.dist.js",
],
},
...nextCoreWebVitals,
{
plugins: {
"@typescript-eslint": tsPlugin,
"react-hooks": reactHooksPlugin,
"react-compiler": reactCompiler,
"local-rules": localRules,
},

languageOptions: {
globals: Object.fromEntries(
Object.entries({
...globals.node,
...globals.commonjs,
...globals.browser,
}).map(([key, value]) => [key.trim(), value])
),

parser: tsParser,
},

rules: {
"no-unused-vars": "off",

"@typescript-eslint/no-unused-vars": ["error", {
varsIgnorePattern: "^_",
caughtErrorsIgnorePattern: "^_",
}],

"react-hooks/exhaustive-deps": "error",

"react/no-unknown-property": ["error", {
ignore: ["meta"],
}],

// New rules in react-hooks plugin — pre-existing patterns need refactoring
"react-hooks/set-state-in-effect": "warn",
"react-hooks/refs": "warn",

"react-compiler/react-compiler": "error",
"local-rules/lint-markdown-code-blocks": "error",
},
},
{
files: ["src/content/**/*.md"],

languageOptions: {
parser: parser,
ecmaVersion: 5,
sourceType: "module",
},

rules: {
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": "off",
"react-hooks/exhaustive-deps": "off",
"react/no-unknown-property": "off",
"react-compiler/react-compiler": "off",
"local-rules/lint-markdown-code-blocks": "error",
},
},
];
1 change: 1 addition & 0 deletions next-env.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
import './.next/types/routes.d.ts';

// NOTE: This file should not be edited
// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information.
71 changes: 18 additions & 53 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,29 @@
* @type {import('next').NextConfig}
**/
const nextConfig = {
turbopack: {
resolveAlias: {
// Don't bundle the shim unnecessarily.
'use-sync-external-store/shim': 'react',
// ESLint depends on the CommonJS version of esquery,
// but Webpack loads the ESM version by default. This
// alias ensures the correct version is used.
//
// More info:
// https://github.com/reactjs/react.dev/pull/8115
esquery: 'esquery/dist/esquery.min.js',
// Replace transitive dependencies with lightweight shims.
raf: './src/utils/rafShim.js',
process: './src/utils/processShim.js',
},
},
pageExtensions: ['jsx', 'js', 'ts', 'tsx', 'mdx', 'md'],
reactStrictMode: true,
experimental: {
scrollRestoration: true,
reactCompiler: true,
turbopackImportTypeText: true,
},
reactCompiler: true,
async rewrites() {
return {
beforeFiles: [
Expand All @@ -44,58 +61,6 @@ const nextConfig = {
};
},
env: {},
webpack: (config, {dev, isServer, ...options}) => {
if (process.env.ANALYZE) {
const {BundleAnalyzerPlugin} = require('webpack-bundle-analyzer');
config.plugins.push(
new BundleAnalyzerPlugin({
analyzerMode: 'static',
reportFilename: options.isServer
? '../analyze/server.html'
: './analyze/client.html',
})
);
}

// Don't bundle the shim unnecessarily.
config.resolve.alias['use-sync-external-store/shim'] = 'react';

// ESLint depends on the CommonJS version of esquery,
// but Webpack loads the ESM version by default. This
// alias ensures the correct version is used.
//
// More info:
// https://github.com/reactjs/react.dev/pull/8115
config.resolve.alias['esquery'] = 'esquery/dist/esquery.min.js';

const {IgnorePlugin, NormalModuleReplacementPlugin} = require('webpack');
config.plugins.push(
new NormalModuleReplacementPlugin(
/^raf$/,
require.resolve('./src/utils/rafShim.js')
),
new NormalModuleReplacementPlugin(
/^process$/,
require.resolve('./src/utils/processShim.js')
),
new IgnorePlugin({
checkResource(resource, context) {
if (
/\/eslint\/lib\/rules$/.test(context) &&
/\.\/[\w-]+(\.js)?$/.test(resource)
) {
// Skips imports of built-in rules that ESLint
// tries to carry into the bundle by default.
// We only want the engine and the React rules.
return true;
}
return false;
},
})
);

return config;
},
};

module.exports = nextConfig;
39 changes: 18 additions & 21 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@
"private": true,
"license": "CC",
"scripts": {
"analyze": "ANALYZE=true next build",
"analyze": "next experimental-analyze",
"dev": "next-remote-watch ./src/content",
"prebuild:rsc": "node scripts/buildRscWorker.mjs",
"build": "node scripts/buildRscWorker.mjs && next build && node --experimental-modules ./scripts/downloadFonts.mjs",
"lint": "next lint && eslint \"src/content/**/*.md\"",
"lint:fix": "next lint --fix && eslint \"src/content/**/*.md\" --fix",
"lint": "eslint . && eslint \"src/content/**/*.md\"",
"lint:fix": "eslint --fix . && eslint \"src/content/**/*.md\" --fix",
"format:source": "prettier --config .prettierrc --write \"{plugins,src}/**/*.{js,ts,jsx,tsx,css}\"",
"nit:source": "prettier --config .prettierrc --list-different \"{plugins,src}/**/*.{js,ts,jsx,tsx,css}\"",
"prettier": "yarn format:source",
Expand All @@ -36,13 +35,12 @@
"classnames": "^2.2.6",
"debounce": "^1.2.1",
"github-slugger": "^1.3.0",
"next": "15.1.12",
"next": "16.2.0",
"next-remote-watch": "^1.0.0",
"parse-numeric-range": "^1.2.0",
"raw-loader": "^4.0.2",
"react": "^19.0.0",
"react": "19.2.4",
"react-collapsed": "4.0.4",
"react-dom": "^19.0.0",
"react-dom": "19.2.4",
"remark-frontmatter": "^4.0.1",
"remark-gfm": "^3.0.1"
},
Expand All @@ -58,8 +56,9 @@
"@types/mdx-js__react": "^1.5.2",
"@types/node": "^14.6.4",
"@types/parse-numeric-range": "^0.0.1",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"@types/prop-types": "^15.7.15",
"@types/react": "19.2.14",
"@types/react-dom": "19.2.3",
"@typescript-eslint/eslint-plugin": "^5.36.2",
"@typescript-eslint/parser": "^5.36.2",
"asyncro": "^3.0.0",
Expand All @@ -68,8 +67,8 @@
"babel-plugin-react-compiler": "^1.0.0",
"chalk": "4.1.2",
"esbuild": "^0.24.0",
"eslint": "7.x",
"eslint-config-next": "12.0.3",
"eslint": "^9",
"eslint-config-next": "16.2.0",
"eslint-config-react-app": "^5.2.1",
"eslint-plugin-flowtype": "4.x",
"eslint-plugin-import": "2.x",
Expand All @@ -90,7 +89,7 @@
"postcss": "^8.4.5",
"postcss-flexbugs-fixes": "4.2.1",
"postcss-preset-env": "^6.7.0",
"prettier": "^2.5.1",
"prettier": "^3.8.1",
"react-server-dom-webpack": "^19.2.4",
"reading-time": "^1.2.0",
"remark": "^12.0.1",
Expand All @@ -104,20 +103,18 @@
"rss": "^1.2.2",
"tailwindcss": "^3.4.1",
"typescript": "^5.7.2",
"unist-util-visit": "^2.0.3",
"webpack-bundle-analyzer": "^4.5.0"
"unist-util-visit": "^2.0.3"
},
"engines": {
"node": ">=16.8.0"
},
"nextBundleAnalysis": {
"budget": null,
"budgetPercentIncreaseRed": 10,
"showDetails": true
},
"lint-staged": {
"*.{js,ts,jsx,tsx,css}": "yarn prettier",
"src/**/*.md": "yarn fix-headings"
},
"packageManager": "yarn@1.22.22"
"packageManager": "yarn@1.22.22",
"resolutions": {
"@types/react": "19.2.14",
"@types/react-dom": "19.2.3"
}
}
Loading
Loading