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.

8 changes: 8 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -1 +1,9 @@
*
!plugins/
!plugins/**
!src/
!src/**
src/content/**/*.md
src/components/MDX/Sandpack/generatedHooksLint.ts
src/components/MDX/Sandpack/sandpack-rsc/generatedSources.ts
src/components/MDX/Sandpack/sandpack-rsc/sandbox-code/**
53 changes: 22 additions & 31 deletions eslint-local-rules/__tests__/lint-markdown-code-blocks.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,31 +11,30 @@ const path = require('path');
const {ESLint} = require('eslint');
const plugin = require('..');

const FIXTURES_DIR = path.join(
__dirname,
'fixtures',
'src',
'content'
);
const FIXTURES_DIR = path.join(__dirname, 'fixtures', 'src', 'content');
const PARSER_PATH = path.join(__dirname, '..', 'parser.js');

function createESLint({fix = false} = {}) {
return new ESLint({
useEslintrc: false,
overrideConfigFile: true,
fix,
plugins: {
'local-rules': plugin,
},
overrideConfig: {
parser: PARSER_PATH,
plugins: ['local-rules'],
rules: {
'local-rules/lint-markdown-code-blocks': 'error',
overrideConfig: [
{
files: ['**/*.md'],
languageOptions: {
parser: require(PARSER_PATH),
parserOptions: {
sourceType: 'module',
},
},
plugins: {
'local-rules': plugin,
},
rules: {
'local-rules/lint-markdown-code-blocks': 'error',
},
},
parserOptions: {
sourceType: 'module',
},
},
],
});
}

Expand All @@ -53,11 +52,7 @@ async function lintFixture(name, {fix = false} = {}) {

async function run() {
const basicResult = await lintFixture('basic-error.md');
assert.strictEqual(
basicResult.messages.length,
1,
'expected one diagnostic'
);
assert.strictEqual(basicResult.messages.length, 1, 'expected one diagnostic');
assert(
basicResult.messages[0].message.includes('Calling setState during render'),
'expected message to mention setState during render'
Expand Down Expand Up @@ -91,9 +86,7 @@ async function run() {
fix: true,
});
assert(
duplicateFixed.output.includes(
"{expectedErrors: {'react-compiler': [4]}}"
),
duplicateFixed.output.includes("{expectedErrors: {'react-compiler': [4]}}"),
'expected duplicates to be rewritten to a single canonical block'
);
assert(
Expand All @@ -118,14 +111,12 @@ async function run() {
fix: true,
});
assert(
malformedFixed.output.includes(
"{expectedErrors: {'react-compiler': [4]}}"
),
malformedFixed.output.includes("{expectedErrors: {'react-compiler': [4]}}"),
'expected malformed metadata to be replaced with canonical form'
);
}

run().catch(error => {
run().catch((error) => {
console.error(error);
process.exitCode = 1;
});
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 {
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 {
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
76 changes: 76 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import tseslint from 'typescript-eslint';
import reactHooks from 'eslint-plugin-react-hooks';
import nextCoreWebVitals from 'eslint-config-next/core-web-vitals';
import localRules from './eslint-local-rules/index.js';
import mdxParser from './eslint-local-rules/parser.js';

const config = [
{
ignores: [
'.next/**',
'node_modules/**',
'coverage/**',
'dist/**',
'out/**',
'**/.*/**',
'scripts/**',
'plugins/**',
'.claude/**',
'worker-bundle.dist.js',
'src/components/MDX/Sandpack/generatedHooksLint.ts',
'src/components/MDX/Sandpack/sandpack-rsc/generatedSources.ts',
'src/components/MDX/Sandpack/sandpack-rsc/sandbox-code/**',
'next-env.d.ts',
'next.config.js',
],
},
...nextCoreWebVitals,
{
files: ['**/*.{js,jsx,ts,tsx}'],
languageOptions: {
parser: tseslint.parser,
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
ecmaFeatures: {
jsx: true,
},
},
},
plugins: {
'@typescript-eslint': tseslint.plugin,
'react-hooks': reactHooks,
'local-rules': localRules,
},
rules: {
'no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars': ['error', {varsIgnorePattern: '^_'}],
'react-hooks/exhaustive-deps': 'error',
'react/no-unknown-property': ['error', {ignore: ['meta']}],
'local-rules/lint-markdown-code-blocks': 'error',
},
},
{
files: ['src/content/**/*.md'],
languageOptions: {
parser: mdxParser,
parserOptions: {
sourceType: 'module',
},
},
plugins: {
'@typescript-eslint': tseslint.plugin,
'react-hooks': reactHooks,
'local-rules': localRules,
},
rules: {
'no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars': 'off',
'react-hooks/exhaustive-deps': 'off',
'react/no-unknown-property': 'off',
'local-rules/lint-markdown-code-blocks': 'error',
},
},
];

export default config;
4 changes: 3 additions & 1 deletion next-env.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
/// <reference types="next/navigation-types/compat/navigation" />
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.
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
68 changes: 17 additions & 51 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@
const nextConfig = {
pageExtensions: ['jsx', 'js', 'ts', 'tsx', 'mdx', 'md'],
reactStrictMode: true,
reactCompiler: true,
cacheComponents: true,
experimental: {
scrollRestoration: true,
reactCompiler: true,
},
async rewrites() {
return {
Expand All @@ -44,58 +45,23 @@ 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',
})
);
}
...(process.env.ANALYZE
? {
webpack: (config, {isServer}) => {
const {BundleAnalyzerPlugin} = require('webpack-bundle-analyzer');
config.plugins.push(
new BundleAnalyzerPlugin({
analyzerMode: 'static',
reportFilename: 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;
},
})
);

return config;
},
}
: {}),
};

module.exports = nextConfig;
Loading
Loading