refactor: start using turborepo (#63734)

This commit is contained in:
Oliver Eyton-Williams
2025-11-17 11:37:09 +01:00
committed by GitHub
parent 0029c51a9a
commit f64f895ab6
9 changed files with 819 additions and 878 deletions

2
.gitignore vendored
View File

@@ -206,3 +206,5 @@ shared-dist
### Shadow Testing Log Files Folder ### ### Shadow Testing Log Files Folder ###
api/logs/ api/logs/
### Turborepo
.turbo

View File

@@ -9,7 +9,6 @@ import { CSRF_COOKIE, CSRF_HEADER } from './src/plugins/csrf.js';
type FastifyTestInstance = Awaited<ReturnType<typeof build>>; type FastifyTestInstance = Awaited<ReturnType<typeof build>>;
declare global { declare global {
// eslint-disable-next-line no-var
var fastifyTestInstance: FastifyTestInstance; var fastifyTestInstance: FastifyTestInstance;
} }

View File

@@ -1,221 +1,3 @@
import { fileURLToPath } from 'node:url'; import { baseConfig } from '@freecodecamp/eslint-config/base';
import path from 'node:path';
import { fixupConfigRules, fixupPluginRules } from '@eslint/compat'; export default baseConfig;
import noOnlyTests from 'eslint-plugin-no-only-tests';
import filenamesSimple from 'eslint-plugin-filenames-simple';
import globals from 'globals';
import babelParser from '@babel/eslint-parser';
import importPlugin from 'eslint-plugin-import';
import jsxAllyPlugin from 'eslint-plugin-jsx-a11y';
import prettierConfig from 'eslint-config-prettier';
import reactPlugin from 'eslint-plugin-react';
import testingLibraryPlugin from 'eslint-plugin-testing-library';
import vitest from '@vitest/eslint-plugin';
import tsParser from '@typescript-eslint/parser';
import tseslint from 'typescript-eslint';
import jsdoc from 'eslint-plugin-jsdoc';
import js from '@eslint/js';
import { FlatCompat } from '@eslint/eslintrc';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const compat = new FlatCompat({
baseDirectory: __dirname,
recommendedConfig: js.configs.recommended,
allConfig: js.configs.all
});
export default tseslint.config(
{
ignores: [
'client/static/**/*',
'client/.cache/**/*',
'client/public/**/*',
'shared/**/*.js',
'shared/**/*.d.ts',
'docs/**/*.md',
'**/playwright*.config.ts',
'playwright/**/*',
'shared-dist/**/*',
'**/dist/**/*'
]
},
js.configs.recommended,
reactPlugin.configs['flat'].recommended,
jsxAllyPlugin.flatConfigs.recommended,
...fixupConfigRules(
compat.extends(
'plugin:react-hooks/recommended' // Note: at time of testing, upgrading to v5 creates false positives
)
),
importPlugin.flatConfigs.recommended,
// TODO: consider adding eslint-plugin-n ():
// ...nodePlugin.configs["flat/mixed-esm-and-cjs"],
prettierConfig,
{
plugins: {
'no-only-tests': noOnlyTests,
'filenames-simple': fixupPluginRules(filenamesSimple)
},
languageOptions: {
globals: {
...globals.browser,
...globals.mocha,
...globals.node,
Promise: true,
window: true,
$: true,
ga: true,
jQuery: true,
router: true,
globalThis: true
},
parser: babelParser,
parserOptions: {
babelOptions: {
presets: ['@babel/preset-react']
}
}
},
settings: {
react: {
version: '16.4.2'
},
'import/resolver': {
typescript: true,
node: true
}
},
// TODO: audit these rules and remove as many as possible, ideally we want
// to rely on recommended configs.
rules: {
'import/no-unresolved': [
2,
{
commonjs: true
}
],
'import/named': 'error',
'import/no-named-as-default': 'off',
'import/no-named-as-default-member': 'off',
'import/order': 'error',
'import/no-cycle': [
2,
{
maxDepth: 2
}
],
'react/prop-types': 'off',
'react/jsx-no-useless-fragment': 'error',
'no-only-tests/no-only-tests': 'error',
'no-unused-expressions': 'error', // This is so the js rules are more in line with the ts rules
'filenames-simple/naming-convention': ['warn']
}
},
{
files: ['**/*.ts?(x)'],
extends: [
tseslint.configs.recommended,
// TODO: turn on type-aware rules
// tseslint.configs.recommendedTypeChecked,
importPlugin.flatConfigs['typescript']
],
languageOptions: {
parser: tsParser,
parserOptions: {
projectService: true
}
},
rules: {
'import/no-unresolved': 'off',
'@typescript-eslint/naming-convention': 'off',
'@typescript-eslint/no-unused-vars': [
'warn',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
caughtErrorsIgnorePattern: '^_'
}
]
}
},
{
files: [
'client/**/*.ts?(x)',
'api/**/*.ts',
'shared/**/*.ts',
'tools/client-plugins/**/*.ts',
'tools/scripts/**/*.ts',
'tools/challenge-helper-scripts/**/*.ts',
'tools/challenge-auditor/**/*.ts',
'e2e/**/*.ts'
],
extends: [tseslint.configs.recommendedTypeChecked]
},
{
files: ['client/**/*.test.[jt]s?(x)'],
extends: [testingLibraryPlugin.configs['flat/react']]
},
{
files: ['**/*.test.[jt]s?(x)'],
plugins: { vitest },
extends: [vitest.configs.recommended]
},
{
files: ['e2e/*.ts'],
rules: {
'@typescript-eslint/no-unsafe-member-access': 'off',
'@typescript-eslint/no-unsafe-call': 'off',
'@typescript-eslint/no-unsafe-assignment': 'off'
}
},
{
files: ['.lintstagedrc.js', '**/.babelrc.js', '**/404.tsx'],
rules: {
'filenames-simple/naming-convention': 'off'
}
},
{
extends: [
jsdoc.configs['flat/recommended-typescript-error'],
tseslint.configs.recommendedTypeChecked
],
files: ['**/api/src/**/*.ts'],
rules: {
'jsdoc/require-jsdoc': [
'error',
{
require: {
ArrowFunctionExpression: true,
ClassDeclaration: true,
ClassExpression: true,
FunctionDeclaration: true,
FunctionExpression: true,
MethodDefinition: true
},
publicOnly: true
}
],
'jsdoc/require-description-complete-sentence': 'error',
'jsdoc/tag-lines': 'off'
}
}
);

View File

@@ -88,11 +88,7 @@
"dotenv": "16.4.5" "dotenv": "16.4.5"
}, },
"devDependencies": { "devDependencies": {
"@babel/eslint-parser": "7.26.5", "@freecodecamp/eslint-config": "workspace:*",
"@babel/preset-react": "7.26.3",
"@eslint/compat": "^1.2.6",
"@eslint/eslintrc": "^3.2.0",
"@eslint/js": "^9.19.0",
"@playwright/test": "^1.47.1", "@playwright/test": "^1.47.1",
"@testing-library/dom": "10.4.0", "@testing-library/dom": "10.4.0",
"@testing-library/jest-dom": "6.6.3", "@testing-library/jest-dom": "6.6.3",
@@ -103,17 +99,7 @@
"@typescript-eslint/parser": "8.23.0", "@typescript-eslint/parser": "8.23.0",
"@vitest/eslint-plugin": "^1.3.12", "@vitest/eslint-plugin": "^1.3.12",
"debug": "4.3.4", "debug": "4.3.4",
"eslint": "9.19.0", "eslint": "^9.39.1",
"eslint-config-prettier": "10.0.1",
"eslint-import-resolver-typescript": "^3.5.5",
"eslint-plugin-filenames-simple": "0.9.0",
"eslint-plugin-import": "2.31.0",
"eslint-plugin-jsdoc": "48.2.1",
"eslint-plugin-jsx-a11y": "6.10.2",
"eslint-plugin-no-only-tests": "3.1.0",
"eslint-plugin-react": "7.37.4",
"eslint-plugin-react-hooks": "4.6.0",
"eslint-plugin-testing-library": "7.1.1",
"globals": "^15.14.0", "globals": "^15.14.0",
"husky": "9.0.11", "husky": "9.0.11",
"identity-obj-proxy": "^3.0.0", "identity-obj-proxy": "^3.0.0",
@@ -126,8 +112,8 @@
"prismjs": "1.29.0", "prismjs": "1.29.0",
"stylelint": "16.14.1", "stylelint": "16.14.1",
"tsx": "4.19.1", "tsx": "4.19.1",
"turbo": "^2.6.1",
"typescript": "5.7.3", "typescript": "5.7.3",
"typescript-eslint": "^8.23.0",
"webpack-bundle-analyzer": "4.10.1", "webpack-bundle-analyzer": "4.10.1",
"yargs": "17.7.2" "yargs": "17.7.2"
}, },

View File

@@ -0,0 +1,221 @@
import { fileURLToPath } from 'node:url';
import path from 'node:path';
import { fixupConfigRules, fixupPluginRules } from '@eslint/compat';
import noOnlyTests from 'eslint-plugin-no-only-tests';
import filenamesSimple from 'eslint-plugin-filenames-simple';
import globals from 'globals';
import babelParser from '@babel/eslint-parser';
import importPlugin from 'eslint-plugin-import';
import jsxAllyPlugin from 'eslint-plugin-jsx-a11y';
import prettierConfig from 'eslint-config-prettier';
import reactPlugin from 'eslint-plugin-react';
import testingLibraryPlugin from 'eslint-plugin-testing-library';
import vitest from '@vitest/eslint-plugin';
import tsParser from '@typescript-eslint/parser';
import tseslint from 'typescript-eslint';
import jsdoc from 'eslint-plugin-jsdoc';
import js from '@eslint/js';
import { FlatCompat } from '@eslint/eslintrc';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const compat = new FlatCompat({
baseDirectory: __dirname,
recommendedConfig: js.configs.recommended,
allConfig: js.configs.all
});
export const baseConfig = tseslint.config(
{
ignores: [
'client/static/**/*',
'client/.cache/**/*',
'client/public/**/*',
'shared/**/*.js',
'shared/**/*.d.ts',
'docs/**/*.md',
'**/playwright*.config.ts',
'playwright/**/*',
'shared-dist/**/*',
'**/dist/**/*'
]
},
js.configs.recommended,
reactPlugin.configs['flat'].recommended,
jsxAllyPlugin.flatConfigs.recommended,
...fixupConfigRules(
compat.extends(
'plugin:react-hooks/recommended' // Note: at time of testing, upgrading to v5 creates false positives
)
),
importPlugin.flatConfigs.recommended,
// TODO: consider adding eslint-plugin-n ():
// ...nodePlugin.configs["flat/mixed-esm-and-cjs"],
prettierConfig,
{
plugins: {
'no-only-tests': noOnlyTests,
'filenames-simple': fixupPluginRules(filenamesSimple)
},
languageOptions: {
globals: {
...globals.browser,
...globals.mocha,
...globals.node,
Promise: true,
window: true,
$: true,
ga: true,
jQuery: true,
router: true,
globalThis: true
},
parser: babelParser,
parserOptions: {
babelOptions: {
presets: ['@babel/preset-react']
}
}
},
settings: {
react: {
version: '16.4.2'
},
'import/resolver': {
typescript: true,
node: true
}
},
// TODO: audit these rules and remove as many as possible, ideally we want
// to rely on recommended configs.
rules: {
'import/no-unresolved': [
2,
{
commonjs: true
}
],
'import/named': 'error',
'import/no-named-as-default': 'off',
'import/no-named-as-default-member': 'off',
'import/order': 'error',
'import/no-cycle': [
2,
{
maxDepth: 2
}
],
'react/prop-types': 'off',
'react/jsx-no-useless-fragment': 'error',
'no-only-tests/no-only-tests': 'error',
'no-unused-expressions': 'error', // This is so the js rules are more in line with the ts rules
'filenames-simple/naming-convention': ['warn']
}
},
{
files: ['**/*.ts?(x)'],
extends: [
tseslint.configs.recommended,
// TODO: turn on type-aware rules
// tseslint.configs.recommendedTypeChecked,
importPlugin.flatConfigs['typescript']
],
languageOptions: {
parser: tsParser,
parserOptions: {
projectService: true
}
},
rules: {
'import/no-unresolved': 'off',
'@typescript-eslint/naming-convention': 'off',
'@typescript-eslint/no-unused-vars': [
'warn',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
caughtErrorsIgnorePattern: '^_'
}
]
}
},
{
files: [
'client/**/*.ts?(x)',
'api/**/*.ts',
'shared/**/*.ts',
'tools/client-plugins/**/*.ts',
'tools/scripts/**/*.ts',
'tools/challenge-helper-scripts/**/*.ts',
'tools/challenge-auditor/**/*.ts',
'e2e/**/*.ts'
],
extends: [tseslint.configs.recommendedTypeChecked]
},
{
files: ['client/**/*.test.[jt]s?(x)'],
extends: [testingLibraryPlugin.configs['flat/react']]
},
{
files: ['**/*.test.[jt]s?(x)'],
plugins: { vitest },
extends: [vitest.configs.recommended]
},
{
files: ['e2e/*.ts'],
rules: {
'@typescript-eslint/no-unsafe-member-access': 'off',
'@typescript-eslint/no-unsafe-call': 'off',
'@typescript-eslint/no-unsafe-assignment': 'off'
}
},
{
files: ['.lintstagedrc.js', '**/.babelrc.js', '**/404.tsx'],
rules: {
'filenames-simple/naming-convention': 'off'
}
},
{
extends: [
jsdoc.configs['flat/recommended-typescript-error'],
tseslint.configs.recommendedTypeChecked
],
files: ['**/api/src/**/*.ts'],
rules: {
'jsdoc/require-jsdoc': [
'error',
{
require: {
ArrowFunctionExpression: true,
ClassDeclaration: true,
ClassExpression: true,
FunctionDeclaration: true,
FunctionExpression: true,
MethodDefinition: true
},
publicOnly: true
}
],
'jsdoc/require-description-complete-sentence': 'error',
'jsdoc/tag-lines': 'off'
}
}
);

View File

@@ -0,0 +1,31 @@
{
"name": "@freecodecamp/eslint-config",
"version": "1.0.0",
"type": "module",
"exports": {
"./base": "./base.js"
},
"author": "freeCodeCamp <team@freecodecamp.org>",
"license": "BSD-3-Clause",
"packageManager": "pnpm@10.20.0",
"devDependencies": {
"@babel/eslint-parser": "7.26.5",
"@babel/preset-react": "7.26.3",
"@eslint/compat": "^1.2.6",
"@eslint/eslintrc": "^3.2.0",
"@eslint/js": "^9.19.0",
"eslint": "^9.39.1",
"eslint-config-prettier": "10.0.1",
"eslint-import-resolver-typescript": "^3.5.5",
"eslint-plugin-filenames-simple": "0.9.0",
"eslint-plugin-import": "2.31.0",
"eslint-plugin-jsdoc": "48.2.1",
"eslint-plugin-jsx-a11y": "6.10.2",
"eslint-plugin-no-only-tests": "3.1.0",
"eslint-plugin-react": "7.37.4",
"eslint-plugin-react-hooks": "4.6.0",
"eslint-plugin-testing-library": "7.1.1",
"typescript": "5.7.3",
"typescript-eslint": "^8.23.0"
}
}

1190
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -14,6 +14,7 @@ packages:
- 'tools/scripts/lint' - 'tools/scripts/lint'
- 'tools/scripts/seed' - 'tools/scripts/seed'
- 'tools/scripts/seed-exams' - 'tools/scripts/seed-exams'
- 'packages/*'
packageExtensions: packageExtensions:
'@testing-library/jest-dom': '@testing-library/jest-dom':

9
turbo.json Normal file
View File

@@ -0,0 +1,9 @@
{
"$schema": "https://turborepo.com/schema.json",
"tasks": {
"lint": {
"dependsOn": ["^lint"]
},
"//#lint": {}
}
}