mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2025-12-19 18:18:27 -05:00
chore: upgrade eslint (#58575)
This commit is contained in:
committed by
GitHub
parent
54b028b10d
commit
6e9513a933
@@ -1,12 +0,0 @@
|
||||
client/static/**
|
||||
client/.cache/**
|
||||
client/public/**
|
||||
api-server/src/public/**
|
||||
api-server/lib/**
|
||||
shared/config/i18n.js
|
||||
shared/config/certification-settings.js
|
||||
shared/config/donation-settings.js
|
||||
shared/config/superblocks.js
|
||||
docs/**/*.md
|
||||
playwright*.config.ts
|
||||
playwright/**
|
||||
134
.eslintrc.json
134
.eslintrc.json
@@ -1,134 +0,0 @@
|
||||
{
|
||||
"env": {
|
||||
"es6": true,
|
||||
"browser": true,
|
||||
"mocha": true,
|
||||
"node": true,
|
||||
"jest": true
|
||||
},
|
||||
"parser": "@babel/eslint-parser",
|
||||
"parserOptions": {
|
||||
"babelOptions": {
|
||||
"presets": ["@babel/preset-react"]
|
||||
}
|
||||
},
|
||||
"root": true,
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:react/recommended",
|
||||
"plugin:react-hooks/recommended",
|
||||
"plugin:import/recommended",
|
||||
"plugin:jsx-a11y/recommended",
|
||||
"prettier"
|
||||
],
|
||||
"plugins": ["no-only-tests", "filenames-simple"],
|
||||
"globals": {
|
||||
"Promise": true,
|
||||
"window": true,
|
||||
"$": true,
|
||||
"ga": true,
|
||||
"jQuery": true,
|
||||
"router": true
|
||||
},
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "16.4.2"
|
||||
},
|
||||
"import/resolver": {
|
||||
"typescript": true,
|
||||
"node": true
|
||||
}
|
||||
},
|
||||
"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",
|
||||
"no-only-tests/no-only-tests": "error",
|
||||
"no-unused-vars": "off",
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"warn",
|
||||
{
|
||||
"argsIgnorePattern": "^_",
|
||||
"varsIgnorePattern": "^_",
|
||||
"caughtErrorsIgnorePattern": "^_"
|
||||
}
|
||||
],
|
||||
"filenames-simple/naming-convention": ["warn"]
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["**/*.ts?(x)"],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"project": [
|
||||
"./client/tsconfig.json",
|
||||
"./tsconfig.json",
|
||||
"./api/tsconfig.json",
|
||||
"./shared/tsconfig.json",
|
||||
"./tools/client-plugins/browser-scripts/tsconfig.json",
|
||||
"./e2e/tsconfig.json"
|
||||
]
|
||||
},
|
||||
"extends": [
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:@typescript-eslint/recommended-requiring-type-checking",
|
||||
"plugin:import/typescript"
|
||||
],
|
||||
"plugins": ["@typescript-eslint"],
|
||||
"rules": {
|
||||
"import/no-unresolved": "off",
|
||||
"import/named": 0,
|
||||
"@typescript-eslint/naming-convention": "off"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["./client/**/*.test.[jt]s?(x)"],
|
||||
"extends": [
|
||||
"plugin:testing-library/react",
|
||||
"plugin:jest-dom/recommended"
|
||||
],
|
||||
"rules": { "import/named": 2 }
|
||||
},
|
||||
{
|
||||
"files": ["e2e/*.ts"],
|
||||
"rules": {
|
||||
"@typescript-eslint/no-unsafe-member-access": "off",
|
||||
"@typescript-eslint/no-unsafe-call": "off",
|
||||
"@typescript-eslint/no-unsafe-assignment": "off"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["**/api-server/**/*", "**/404.*"],
|
||||
"rules": {
|
||||
"filenames-simple/naming-convention": "off"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["**/api/src/**/*.ts"],
|
||||
"plugins": ["jsdoc"],
|
||||
"extends": ["plugin:jsdoc/recommended-typescript-error"],
|
||||
"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": "warn",
|
||||
"jsdoc/tag-lines": "off"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -57,7 +57,6 @@ describe('boot/challenge', () => {
|
||||
expect(result).toHaveProperty('updateData.$push.completedChallenges');
|
||||
});
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
it('preserves file contents if the completed challenge is a JS Project', () => {
|
||||
const jsChallengeId = 'aa2e6f85cab2ab736c9a9b24';
|
||||
const completedChallenge = {
|
||||
@@ -102,7 +101,6 @@ describe('boot/challenge', () => {
|
||||
);
|
||||
});
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
it('does not attempt to update progressTimestamps for a previously completed challenge', () => {
|
||||
const completedChallengeId = 'aaa48de84e1ecc7c742e1124';
|
||||
const completedChallenge = {
|
||||
@@ -122,7 +120,6 @@ describe('boot/challenge', () => {
|
||||
expect(hasProgressTimestamps).toBe(false);
|
||||
});
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
it('provides a progressTimestamps update for new challenge completion', () => {
|
||||
expect.assertions(2);
|
||||
const { updateData } = buildUserUpdate(
|
||||
@@ -238,7 +235,6 @@ describe('boot/challenge', () => {
|
||||
});
|
||||
}, 10000);
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
it('returns the first challenge url if the provided id does not relate to a challenge', async () => {
|
||||
const challengeUrlResolver = await createChallengeUrlResolver(
|
||||
mockAllChallenges,
|
||||
@@ -403,7 +399,6 @@ describe('boot/challenge', () => {
|
||||
expect(res.redirect).toHaveBeenCalledWith(mockLearnUrl);
|
||||
});
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
it('redirects to the url provided by the challengeUrlResolver', async () => {
|
||||
const challengeUrlResolver = await createChallengeUrlResolver(
|
||||
mockAllChallenges,
|
||||
@@ -427,7 +422,6 @@ describe('boot/challenge', () => {
|
||||
expect(res.redirect).toHaveBeenCalledWith(expectedUrl);
|
||||
});
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
it('redirects to the first challenge for users without a currentChallengeId', async () => {
|
||||
const challengeUrlResolver = await createChallengeUrlResolver(
|
||||
mockAllChallenges,
|
||||
|
||||
@@ -113,9 +113,7 @@ export const mockCompletedChallenges = [
|
||||
completedDate: 1541678430790.0,
|
||||
files: [
|
||||
{
|
||||
contents:
|
||||
// eslint-disable-next-line max-len
|
||||
"function palindrome(str) {\n const clean = str.replace(/[\\W_]/g, '').toLowerCase()\n const revStr = clean.split('').reverse().join('');\n return clean === revStr;\n}\n\n\n\npalindrome(\"eye\");\n",
|
||||
contents: "function palindrome(str) {\n const clean = str.replace(/[\\W_]/g, '').toLowerCase()\n const revStr = clean.split('').reverse().join('');\n return clean === revStr;\n}\n\n\n\npalindrome(\"eye\");\n",
|
||||
ext: 'js',
|
||||
path: 'index.js',
|
||||
name: 'index',
|
||||
|
||||
@@ -77,7 +77,6 @@ app.start = _.once(function () {
|
||||
log('DB connection closed');
|
||||
// exit process
|
||||
// this may close kept alive sockets
|
||||
// eslint-disable-next-line no-process-exit
|
||||
process.exit(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,7 +13,6 @@ export default function getCsurf() {
|
||||
return function csrf(req, res, next) {
|
||||
const { path } = req;
|
||||
if (
|
||||
// eslint-disable-next-line max-len
|
||||
/^\/donate\/charge-stripe$|^\/donate\/create-stripe-payment-intent$|^\/coderoad-challenge-completed$/.test(
|
||||
path
|
||||
)
|
||||
|
||||
@@ -43,7 +43,7 @@ describe('getSetAccessToken', () => {
|
||||
describe('cookies', () => {
|
||||
it('returns `invalid token` error for malformed tokens', () => {
|
||||
const invalidJWT = jwt.sign({ accessToken }, invalidJWTSecret);
|
||||
// eslint-disable-next-line camelcase
|
||||
|
||||
const req = mockReq({ cookie: { jwt_access_token: invalidJWT } });
|
||||
const result = getAccessTokenFromRequest(req, validJWTSecret);
|
||||
|
||||
@@ -55,7 +55,7 @@ describe('getSetAccessToken', () => {
|
||||
{ accessToken: { ...accessToken, created: theBeginningOfTime } },
|
||||
validJWTSecret
|
||||
);
|
||||
// eslint-disable-next-line camelcase
|
||||
|
||||
const req = mockReq({ cookie: { jwt_access_token: invalidJWT } });
|
||||
const result = getAccessTokenFromRequest(req, validJWTSecret);
|
||||
|
||||
@@ -65,7 +65,7 @@ describe('getSetAccessToken', () => {
|
||||
it('returns a valid access token with no errors ', () => {
|
||||
expect.assertions(2);
|
||||
const validJWT = jwt.sign({ accessToken }, validJWTSecret);
|
||||
// eslint-disable-next-line camelcase
|
||||
|
||||
const req = mockReq({ cookie: { jwt_access_token: validJWT } });
|
||||
const result = getAccessTokenFromRequest(req, validJWTSecret);
|
||||
|
||||
@@ -101,7 +101,6 @@ describe('getSetAccessToken', () => {
|
||||
});
|
||||
|
||||
describe('removeCookies', () => {
|
||||
// eslint-disable-next-line max-len
|
||||
it('removes four cookies set in the lifetime of an authenticated session', () => {
|
||||
// expect.assertions(4);
|
||||
const req = mockReq();
|
||||
|
||||
@@ -5,7 +5,7 @@ const args = process.argv.slice(2);
|
||||
const ENV_EXAM_ID = args[0];
|
||||
|
||||
if (!ENV_EXAM_ID) {
|
||||
throw 'First argument must be the EnvExam _id';
|
||||
throw Error('First argument must be the EnvExam _id');
|
||||
}
|
||||
|
||||
const prisma = new PrismaClient({
|
||||
|
||||
@@ -7,10 +7,10 @@ const ENV_EXAM_ID = args[0];
|
||||
const NUMBER_OF_EXAMS_TO_GENERATE = Number(args[1]);
|
||||
|
||||
if (!ENV_EXAM_ID) {
|
||||
throw 'First argument must be the EnvExam _id';
|
||||
throw Error('First argument must be the EnvExam _id');
|
||||
}
|
||||
if (!NUMBER_OF_EXAMS_TO_GENERATE) {
|
||||
throw 'Second argument must be an unsigned integer';
|
||||
throw Error('Second argument must be an unsigned integer');
|
||||
}
|
||||
|
||||
const prisma = new PrismaClient({
|
||||
|
||||
@@ -19,7 +19,7 @@ async function main() {
|
||||
console.info('Connected.');
|
||||
|
||||
if (!EXAM_JSON_PATH) {
|
||||
throw 'First argument must be the file path to the exam';
|
||||
throw Error('First argument must be the file path to the exam');
|
||||
}
|
||||
|
||||
const exam_str = await readFile(EXAM_JSON_PATH, 'utf-8');
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// TODO: enable this, since strings don't make good errors.
|
||||
/* eslint-disable @typescript-eslint/only-throw-error */
|
||||
/* eslint-disable jsdoc/require-returns, jsdoc/require-param */
|
||||
import {
|
||||
EnvAnswer,
|
||||
@@ -292,6 +294,7 @@ export function generateExam(exam: EnvExam): Omit<EnvGeneratedExam, 'id'> {
|
||||
(acc, curr) => {
|
||||
// If type is already in accumulator, add to it.
|
||||
const typeIndex = acc.findIndex(a => a[0]?.type === curr.type);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
||||
acc[typeIndex]?.push(curr) ?? acc.push([curr]);
|
||||
return acc;
|
||||
},
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
import Fastify, { FastifyInstance } from 'fastify';
|
||||
|
||||
import { defaultUserEmail } from '../../jest.utils';
|
||||
|
||||
@@ -180,7 +180,7 @@ describe('auth0 plugin', () => {
|
||||
getAccessTokenFromAuthorizationCodeFlowSpy.mockResolvedValueOnce({
|
||||
token: 'any token'
|
||||
});
|
||||
userinfoSpy.mockResolvedValueOnce(Promise.reject('any error'));
|
||||
userinfoSpy.mockResolvedValueOnce(Promise.reject(Error('any error')));
|
||||
|
||||
const res = await fastify.inject({
|
||||
method: 'GET',
|
||||
|
||||
@@ -57,7 +57,7 @@ const shadowCapture: FastifyPluginCallback = (fastify, _options, done) => {
|
||||
done();
|
||||
};
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-return */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
function captureRequest(req: FastifyRequest) {
|
||||
const savedRequest = {
|
||||
// @ts-expect-error Exists
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
import {
|
||||
devLogin,
|
||||
|
||||
@@ -127,7 +127,6 @@ describe('auth0 routes', () => {
|
||||
|
||||
const res = await superRequest('/mobile-login', {
|
||||
method: 'GET',
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
setCookies: firstRes.get('Set-Cookie')
|
||||
})
|
||||
.set('Authorization', 'Bearer does-not-matter')
|
||||
|
||||
@@ -102,6 +102,7 @@ export const chargeStripeRoute: FastifyPluginCallbackTypebox = (
|
||||
const subscription =
|
||||
await stripe.subscriptions.retrieve(subscriptionId);
|
||||
const isSubscriptionActive = subscription.status === 'active';
|
||||
// eslint-disable-next-line @typescript-eslint/no-base-to-string
|
||||
const productId = subscription.items.data[0]?.plan.product?.toString();
|
||||
const isStartedRecently = inLastFiveMinutes(
|
||||
subscription.current_period_start
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import { type FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox';
|
||||
import * as schemas from '../../schemas';
|
||||
import { getRedirectParams } from '../../utils/redirection';
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable jsdoc/require-returns, jsdoc/require-param */
|
||||
import { type FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox';
|
||||
import { type FastifyInstance, type FastifyReply } from 'fastify';
|
||||
|
||||
@@ -29,9 +28,6 @@ export const sentryRoutes: FastifyPluginCallbackTypebox = (
|
||||
done();
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new event in Sentry.
|
||||
*/
|
||||
function postSentryEventHandler(
|
||||
this: FastifyInstance,
|
||||
req: UpdateReqType<typeof schemas.sentryPostEvent>,
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
import type { Prisma } from '@prisma/client';
|
||||
import { ObjectId } from 'mongodb';
|
||||
import _ from 'lodash';
|
||||
|
||||
@@ -105,7 +105,7 @@ function getParamsFromUrl(
|
||||
let returnUrl;
|
||||
try {
|
||||
returnUrl = new URL(url ? url : HOME_LOCATION);
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
returnUrl = new URL(HOME_LOCATION);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable camelcase */
|
||||
import React, { Component } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
@@ -112,7 +111,6 @@ export default class PayPalButtonScriptLoader extends Component<
|
||||
prevProps.style.height !== this.props.style.height ||
|
||||
prevProps.isMinimalForm !== this.props.isMinimalForm
|
||||
) {
|
||||
// eslint-disable-next-line react/no-did-update-set-state
|
||||
this.setState({ isSdkLoaded: false });
|
||||
this.loadScript(this.state.isSubscription, true);
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ function useIsInViewport(ref: React.RefObject<HTMLDivElement>) {
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
ref.current && observer.observe(ref.current);
|
||||
if (ref.current) observer.observe(ref.current);
|
||||
return () => {
|
||||
observer.disconnect();
|
||||
};
|
||||
|
||||
@@ -72,7 +72,7 @@ const GrowthBookWrapper = ({
|
||||
});
|
||||
}
|
||||
}),
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
|
||||
[]
|
||||
);
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// eslint-disable-next-line filenames-simple/naming-convention
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Spacer } from '@freecodecamp/ui';
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
import CalendarHeatMap from '@freecodecamp/react-calendar-heatmap';
|
||||
// TODO: Check if we can import { addDays, addMonths ... } from 'date-fns'
|
||||
// without bundling all of the package then we can remove the disable-next-line
|
||||
// comments.
|
||||
|
||||
// eslint-disable-next-line import/no-duplicates
|
||||
import addDays from 'date-fns/addDays';
|
||||
// eslint-disable-next-line import/no-duplicates
|
||||
import addMonths from 'date-fns/addMonths';
|
||||
// eslint-disable-next-line import/no-duplicates
|
||||
import isEqual from 'date-fns/isEqual';
|
||||
// eslint-disable-next-line import/no-duplicates
|
||||
import startOfDay from 'date-fns/startOfDay';
|
||||
import React, { Component } from 'react';
|
||||
import type { TFunction } from 'i18next';
|
||||
@@ -21,7 +29,6 @@ import { getLangCode } from '../../../../../shared/config/i18n';
|
||||
import { User } from '../../../redux/prop-types';
|
||||
import FullWidthRow from '../../helpers/full-width-row';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const { clientLocale } = envData;
|
||||
|
||||
const localeCode = getLangCode(clientLocale);
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
// TODO: Check if we can import { addDays, addMonths ... } from 'date-fns'
|
||||
// without bundling all of the package then we can remove the disable-next-line
|
||||
// comments.
|
||||
|
||||
// eslint-disable-next-line import/no-duplicates
|
||||
import addDays from 'date-fns/addDays';
|
||||
// eslint-disable-next-line import/no-duplicates
|
||||
import addMonths from 'date-fns/addMonths';
|
||||
// eslint-disable-next-line import/no-duplicates
|
||||
import isEqual from 'date-fns/isEqual';
|
||||
import { Spacer } from '@freecodecamp/ui';
|
||||
// eslint-disable-next-line import/no-duplicates
|
||||
import startOfDay from 'date-fns/startOfDay';
|
||||
import { User } from '../../../redux/prop-types';
|
||||
import { FullWidthRow } from '../../helpers';
|
||||
|
||||
@@ -240,7 +240,7 @@ function TimelineInner({
|
||||
);
|
||||
}
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/restrict-template-expressions, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call*/
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call*/
|
||||
function useIdToNameMap(t: TFunction): Map<string, NameMap> {
|
||||
const {
|
||||
allChallengeNode: { edges }
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import React from 'react';
|
||||
import renderer from 'react-test-renderer';
|
||||
import { Provider } from 'react-redux';
|
||||
|
||||
@@ -72,7 +72,6 @@ const userProps = {
|
||||
isCollegeAlgebraPyCertV8: true,
|
||||
isFoundationalCSharpVertV8: true
|
||||
},
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
navigate: () => {}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
|
||||
@@ -24,7 +24,7 @@ function ExamToken(): JSX.Element {
|
||||
} = response;
|
||||
setExamToken(examEnvironmentAuthorizationToken);
|
||||
setExamTokenError('');
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
setExamTokenError(t('exam-token.error'));
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,8 @@ import { Router } from '@reach/router';
|
||||
import { withPrefix } from 'gatsby';
|
||||
import React from 'react';
|
||||
|
||||
/* eslint-disable max-len */
|
||||
import ShowProfileOrFourOhFour from '../client-only-routes/show-profile-or-four-oh-four';
|
||||
import FourOhFour from '../components/FourOhFour';
|
||||
/* eslint-enable max-len */
|
||||
|
||||
function FourOhFourPage(): JSX.Element {
|
||||
return (
|
||||
|
||||
@@ -58,7 +58,6 @@ function DonatePage({
|
||||
event: 'donation_view',
|
||||
action: `Displayed Donate Page`
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return showLoading ? (
|
||||
|
||||
@@ -233,7 +233,6 @@ const DesktopLayout = (props: DesktopLayoutProps): JSX.Element => {
|
||||
} else if (!isAdvancing && !showPreviewPane && !showPreviewPortal) {
|
||||
togglePane('showPreviewPane');
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const challengeFile = getChallengeFile();
|
||||
|
||||
@@ -635,9 +635,11 @@ const Editor = (props: EditorProps): JSX.Element => {
|
||||
const setAriaRoledescription = (value: boolean) => {
|
||||
const textareas = document.querySelectorAll('.monaco-editor textarea');
|
||||
textareas.forEach(textarea => {
|
||||
value
|
||||
? textarea.setAttribute('aria-roledescription', 'editor')
|
||||
: textarea.removeAttribute('aria-roledescription');
|
||||
if (value) {
|
||||
textarea.setAttribute('aria-roledescription', 'editor');
|
||||
} else {
|
||||
textarea.removeAttribute('aria-roledescription');
|
||||
}
|
||||
});
|
||||
store.set('ariaRoledescription', value);
|
||||
};
|
||||
@@ -699,7 +701,7 @@ const Editor = (props: EditorProps): JSX.Element => {
|
||||
dataRef.current.descriptionZoneTop =
|
||||
editor.getTopForLineNumber(getLineBeforeEditableRegion() + 1) -
|
||||
domNode.offsetHeight;
|
||||
dataRef.current.descriptionWidget &&
|
||||
if (dataRef.current.descriptionWidget)
|
||||
editor.layoutContentWidget(dataRef.current.descriptionWidget);
|
||||
}
|
||||
};
|
||||
@@ -1247,7 +1249,6 @@ const Editor = (props: EditorProps): JSX.Element => {
|
||||
}
|
||||
);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [props.tests]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -1258,7 +1259,6 @@ const Editor = (props: EditorProps): JSX.Element => {
|
||||
if (!isTabTrapped()) {
|
||||
setMonacoTabTrapped(false);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [props.dimensions]);
|
||||
|
||||
function updateDescriptionZone() {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable @typescript-eslint/restrict-template-expressions */
|
||||
import React, { Component } from 'react';
|
||||
import type { TFunction } from 'i18next';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
import { parseBlanks } from './parse-blanks';
|
||||
|
||||
describe('parseBlanks', () => {
|
||||
|
||||
@@ -13,8 +13,7 @@ interface ExitQuizModalProps {
|
||||
}
|
||||
|
||||
const mapStateToProps = (state: unknown) => ({
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call
|
||||
isExitQuizModalOpen: isExitQuizModalOpenSelector(state)
|
||||
isExitQuizModalOpen: isExitQuizModalOpenSelector(state) as boolean
|
||||
});
|
||||
|
||||
const mapDispatchToProps = {
|
||||
|
||||
@@ -13,8 +13,7 @@ interface FinishQuizModalProps {
|
||||
}
|
||||
|
||||
const mapStateToProps = (state: unknown) => ({
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call
|
||||
isFinishQuizModalOpen: isFinishQuizModalOpenSelector(state)
|
||||
isFinishQuizModalOpen: isFinishQuizModalOpenSelector(state) as boolean
|
||||
});
|
||||
|
||||
const mapDispatchToProps = {
|
||||
|
||||
@@ -178,7 +178,8 @@ const ShowQuiz = ({
|
||||
},
|
||||
passingGrade: 90,
|
||||
onSuccess: () => {
|
||||
openCompletionModal(), setIsPassed(true);
|
||||
openCompletionModal();
|
||||
setIsPassed(true);
|
||||
},
|
||||
onFailure: () => setIsPassed(false)
|
||||
});
|
||||
|
||||
@@ -50,11 +50,9 @@ let presetsJS, presetsJSX;
|
||||
|
||||
async function loadBabel() {
|
||||
if (Babel) return;
|
||||
/* eslint-disable no-inline-comments */
|
||||
Babel = await import(
|
||||
/* webpackChunkName: "@babel/standalone" */ '@babel/standalone'
|
||||
);
|
||||
/* eslint-enable no-inline-comments */
|
||||
Babel.registerPlugin(
|
||||
'loopProtection',
|
||||
protect(protectTimeout, loopProtectCB, loopsPerTimeoutCheck)
|
||||
@@ -66,12 +64,10 @@ async function loadBabel() {
|
||||
}
|
||||
|
||||
async function loadPresetEnv() {
|
||||
/* eslint-disable no-inline-comments */
|
||||
if (!presetEnv)
|
||||
presetEnv = await import(
|
||||
/* webpackChunkName: "@babel/preset-env" */ '@babel/preset-env'
|
||||
);
|
||||
/* eslint-enable no-inline-comments */
|
||||
|
||||
presetsJS = {
|
||||
presets: [presetEnv]
|
||||
@@ -79,7 +75,6 @@ async function loadPresetEnv() {
|
||||
}
|
||||
|
||||
async function loadPresetReact() {
|
||||
/* eslint-disable no-inline-comments */
|
||||
if (!presetReact)
|
||||
presetReact = await import(
|
||||
/* webpackChunkName: "@babel/preset-react" */ '@babel/preset-react'
|
||||
@@ -88,7 +83,7 @@ async function loadPresetReact() {
|
||||
presetEnv = await import(
|
||||
/* webpackChunkName: "@babel/preset-env" */ '@babel/preset-env'
|
||||
);
|
||||
/* eslint-enable no-inline-comments */
|
||||
|
||||
presetsJSX = {
|
||||
presets: [presetEnv, presetReact]
|
||||
};
|
||||
|
||||
@@ -306,7 +306,6 @@ export function* previewChallengeSaga(action) {
|
||||
} catch (err) {
|
||||
if (err[0] === 'timeout') {
|
||||
// TODO: translate the error
|
||||
// eslint-disable-next-line no-ex-assign
|
||||
err[0] = `The code you have written is taking longer than the ${previewTimeout}ms our challenges allow. You may have created an infinite loop or need to write a more efficient algorithm`;
|
||||
}
|
||||
// If the preview fails, the most useful thing to do is to show the learner
|
||||
|
||||
@@ -115,8 +115,6 @@ export function canBuildChallenge(challengeData: BuildChallengeData): boolean {
|
||||
return Object.prototype.hasOwnProperty.call(buildFunctions, challengeType);
|
||||
}
|
||||
|
||||
// TODO: Figure out and (hopefully) simplify the return type.
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
export async function buildChallenge(
|
||||
challengeData: BuildChallengeData,
|
||||
options: BuildOptions
|
||||
@@ -139,8 +137,7 @@ const testRunners = {
|
||||
[challengeTypes.multifilePythonCertProject]: getPyTestRunner,
|
||||
[challengeTypes.lab]: getDOMTestRunner
|
||||
};
|
||||
// TODO: Figure out and (hopefully) simplify the return type.
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
|
||||
export function getTestRunner(
|
||||
buildData: BuildChallengeData,
|
||||
runnerConfig: TestRunnerConfig,
|
||||
|
||||
@@ -161,6 +161,7 @@ export const runTestInTestFrame = async function (
|
||||
return await Promise.race([
|
||||
new Promise<
|
||||
{ pass: boolean } | { err: { message: string; stack?: string } }
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors
|
||||
>((_, reject) => setTimeout(() => reject('timeout'), timeout)),
|
||||
contentDocument.__runTest(test)
|
||||
]);
|
||||
@@ -201,7 +202,6 @@ const mountFrame =
|
||||
const oldFrame = document.getElementById(element.id) as HTMLIFrameElement;
|
||||
if (oldFrame) {
|
||||
element.className = oldFrame.className || hiddenFrameClassName;
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
oldFrame.parentNode!.replaceChild(element, oldFrame);
|
||||
// only test frames can be added (and hidden) here, other frames must be
|
||||
// added by react
|
||||
@@ -358,6 +358,7 @@ const initPreviewFrame = () => (frameContext: Context) => frameContext;
|
||||
const waitForFrame = (frameContext: Context) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!frameContext.document) {
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors
|
||||
reject(DOCUMENT_NOT_FOUND_ERROR);
|
||||
} else if (frameContext.document.readyState === 'loading') {
|
||||
frameContext.document.addEventListener('DOMContentLoaded', resolve);
|
||||
|
||||
@@ -143,7 +143,6 @@ function parseApiResponseToClientUser(data: ApiUser): UserResponse {
|
||||
}
|
||||
|
||||
// TODO: this at least needs a few aliases so it's human readable
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
export function mapFilesToChallengeFiles<File, Rest>(
|
||||
fileContainer: ({ files: (File & { key: string })[] } & Rest)[] = []
|
||||
) {
|
||||
|
||||
@@ -32,15 +32,7 @@ describe('format', () => {
|
||||
);
|
||||
});
|
||||
it('handles all primitive values', () => {
|
||||
const primitives = [
|
||||
'str',
|
||||
57,
|
||||
true,
|
||||
false,
|
||||
null,
|
||||
// eslint-disable-next-line no-undefined
|
||||
undefined
|
||||
];
|
||||
const primitives = ['str', 57, true, false, null, undefined];
|
||||
expect(format(primitives)).toBe(
|
||||
`[ 'str', 57, true, false, null, undefined ]`
|
||||
);
|
||||
|
||||
@@ -112,11 +112,11 @@ if (FREECODECAMP_NODE_ENV !== 'development') {
|
||||
checkClientLocale();
|
||||
checkCurriculumLocale();
|
||||
if (fs.existsSync(`${configPath}/env.json`)) {
|
||||
/* eslint-disable @typescript-eslint/no-var-requires, @typescript-eslint/no-unsafe-assignment */
|
||||
const { showUpcomingChanges } = require(`${configPath}/env.json`);
|
||||
/* eslint-enable @typescript-eslint/no-var-requires, @typescript-eslint/no-unsafe-assignment */
|
||||
const { showUpcomingChanges } = JSON.parse(
|
||||
fs.readFileSync(`${configPath}/env.json`, 'utf-8')
|
||||
) as { showUpcomingChanges: boolean };
|
||||
|
||||
if (env['showUpcomingChanges'] !== showUpcomingChanges) {
|
||||
/* eslint-enable @typescript-eslint/no-unsafe-member-access */
|
||||
console.log('Feature flags have been changed, cleaning client cache.');
|
||||
const child = spawn('pnpm', ['run', '-w', 'clean:client']);
|
||||
child.stdout.setEncoding('utf8');
|
||||
|
||||
@@ -81,7 +81,7 @@ export const generateSearchPlaceholder = async (
|
||||
)
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
} catch (_err) {
|
||||
if (environment === 'production') {
|
||||
console.warn(`
|
||||
----------------------------------------------------------
|
||||
|
||||
@@ -8,11 +8,10 @@ const readDirP = require('readdirp');
|
||||
const { curriculum: curriculumLangs } =
|
||||
require('../shared/config/i18n').availableLangs;
|
||||
const { parseMD } = require('../tools/challenge-parser/parser');
|
||||
/* eslint-disable max-len */
|
||||
|
||||
const {
|
||||
translateCommentsInChallenge
|
||||
} = require('../tools/challenge-parser/translation-parser');
|
||||
/* eslint-enable max-len*/
|
||||
|
||||
const { isAuditedSuperBlock } = require('../shared/utils/is-audited');
|
||||
const { createPoly } = require('../shared/utils/polyvinyl');
|
||||
@@ -267,7 +266,6 @@ async function buildChallenges({ path: filePath }, curriculum, lang) {
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(`failed to create superBlock from ${superBlockDir}`);
|
||||
// eslint-disable-next-line no-process-exit
|
||||
process.exit(1);
|
||||
}
|
||||
const { meta } = challengeBlock;
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// utils are not typed (yet), so we have to disable some checks
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { config } from 'dotenv';
|
||||
|
||||
@@ -177,7 +177,6 @@ test.describe('Profile component', () => {
|
||||
test('should not show portfolio when empty', async ({ page }) => {
|
||||
// @certifieduser doesn't have portfolio information
|
||||
await expect(
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
page.getByText(translations.profile.projects)
|
||||
).not.toBeVisible();
|
||||
});
|
||||
|
||||
220
eslint.config.mjs
Normal file
220
eslint.config.mjs
Normal file
@@ -0,0 +1,220 @@
|
||||
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 jestDomPlugin from 'eslint-plugin-jest-dom';
|
||||
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/**/*',
|
||||
'api-server/**/*',
|
||||
'shared/**/*.js',
|
||||
'docs/**/*.md',
|
||||
'**/playwright*.config.ts',
|
||||
'playwright/**/*'
|
||||
]
|
||||
},
|
||||
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,
|
||||
...globals.jest,
|
||||
Promise: true,
|
||||
window: true,
|
||||
$: true,
|
||||
ga: true,
|
||||
jQuery: true,
|
||||
router: 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',
|
||||
'no-only-tests/no-only-tests': 'error',
|
||||
'no-unused-vars': 'off',
|
||||
'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'],
|
||||
jestDomPlugin.configs['flat/recommended']
|
||||
],
|
||||
rules: {
|
||||
'import/named': 2
|
||||
}
|
||||
},
|
||||
{
|
||||
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'
|
||||
}
|
||||
}
|
||||
);
|
||||
25
package.json
25
package.json
@@ -91,8 +91,11 @@
|
||||
"dotenv": "16.4.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/eslint-parser": "7.23.3",
|
||||
"@babel/preset-react": "7.23.3",
|
||||
"@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",
|
||||
"@playwright/test": "^1.47.1",
|
||||
"@testing-library/dom": "9.3.4",
|
||||
"@testing-library/jest-dom": "5.17.0",
|
||||
@@ -101,20 +104,21 @@
|
||||
"@types/node": "20.12.8",
|
||||
"@types/testing-library__jest-dom": "^5.14.5",
|
||||
"@typescript-eslint/eslint-plugin": "7.1.1",
|
||||
"@typescript-eslint/parser": "7.1.1",
|
||||
"@typescript-eslint/parser": "8.23.0",
|
||||
"babel-jest": "29.7.0",
|
||||
"eslint": "8.57.0",
|
||||
"eslint-config-prettier": "9.1.0",
|
||||
"eslint": "9.19.0",
|
||||
"eslint-config-prettier": "10.0.1",
|
||||
"eslint-import-resolver-typescript": "^3.5.5",
|
||||
"eslint-plugin-filenames-simple": "0.9.0",
|
||||
"eslint-plugin-import": "2.29.1",
|
||||
"eslint-plugin-jest-dom": "5.1.0",
|
||||
"eslint-plugin-import": "2.31.0",
|
||||
"eslint-plugin-jest-dom": "5.5.0",
|
||||
"eslint-plugin-jsdoc": "48.2.1",
|
||||
"eslint-plugin-jsx-a11y": "6.7.1",
|
||||
"eslint-plugin-jsx-a11y": "6.10.2",
|
||||
"eslint-plugin-no-only-tests": "3.1.0",
|
||||
"eslint-plugin-react": "7.33.2",
|
||||
"eslint-plugin-react": "7.37.4",
|
||||
"eslint-plugin-react-hooks": "4.6.0",
|
||||
"eslint-plugin-testing-library": "6.2.0",
|
||||
"eslint-plugin-testing-library": "7.1.1",
|
||||
"globals": "^15.14.0",
|
||||
"husky": "9.0.11",
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
"jest": "29.7.0",
|
||||
@@ -129,6 +133,7 @@
|
||||
"stylelint": "16.14.1",
|
||||
"ts-node": "10.9.2",
|
||||
"typescript": "5.4.5",
|
||||
"typescript-eslint": "^8.23.0",
|
||||
"webpack-bundle-analyzer": "4.10.1",
|
||||
"yargs": "17.7.2"
|
||||
},
|
||||
|
||||
2533
pnpm-lock.yaml
generated
2533
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -142,7 +142,7 @@ void (async () => {
|
||||
actionShouldFail = true;
|
||||
}
|
||||
}
|
||||
actionShouldFail ? process.exit(1) : process.exit(0);
|
||||
process.exit(actionShouldFail ? 1 : 0);
|
||||
})();
|
||||
|
||||
async function auditChallengeFiles(
|
||||
|
||||
@@ -2,8 +2,7 @@ import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import './index.css';
|
||||
import App from './app';
|
||||
// TODO: Re-enable this when we upgrade React to version 18
|
||||
// eslint-disable-next-line react/no-deprecated
|
||||
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
|
||||
@@ -16,6 +16,5 @@
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
"types": ["node"]
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,13 +14,15 @@ import {
|
||||
|
||||
function deleteStep(stepNum: number): void {
|
||||
if (stepNum < 1) {
|
||||
throw 'Step not deleted. Step num must be a number greater than 0.';
|
||||
throw Error('Step not deleted. Step num must be a number greater than 0.');
|
||||
}
|
||||
|
||||
const challengeOrder = getMetaData().challengeOrder;
|
||||
|
||||
if (stepNum > challengeOrder.length)
|
||||
throw `Step # ${stepNum} not deleted. Largest step number is ${challengeOrder.length}.`;
|
||||
throw Error(
|
||||
`Step # ${stepNum} not deleted. Largest step number is ${challengeOrder.length}.`
|
||||
);
|
||||
|
||||
const stepId = challengeOrder[stepNum - 1].id;
|
||||
|
||||
@@ -33,14 +35,16 @@ function deleteStep(stepNum: number): void {
|
||||
|
||||
function insertStep(stepNum: number): void {
|
||||
if (stepNum < 1) {
|
||||
throw 'Step not inserted. New step number must be greater than 0.';
|
||||
throw Error('Step not inserted. New step number must be greater than 0.');
|
||||
}
|
||||
const challengeOrder = getMetaData().challengeOrder;
|
||||
|
||||
if (stepNum > challengeOrder.length + 1)
|
||||
throw `Step not inserted. New step number must be less than ${
|
||||
challengeOrder.length + 2
|
||||
}.`;
|
||||
throw Error(
|
||||
`Step not inserted. New step number must be less than ${
|
||||
challengeOrder.length + 2
|
||||
}.`
|
||||
);
|
||||
const challengeType = [SuperBlocks.SciCompPy].includes(
|
||||
getMetaData().superBlock
|
||||
)
|
||||
@@ -67,7 +71,9 @@ function insertStep(stepNum: number): void {
|
||||
|
||||
function createEmptySteps(num: number): void {
|
||||
if (num < 1 || num > 1000) {
|
||||
throw `No steps created. arg 'num' must be between 1 and 1000 inclusive`;
|
||||
throw Error(
|
||||
`No steps created. arg 'num' must be between 1 and 1000 inclusive`
|
||||
);
|
||||
}
|
||||
|
||||
const nextStepNum = getMetaData().challengeOrder.length + 1;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
const isIntRE = /^\d+$/;
|
||||
function getArgValue(argv: string[] = []): number {
|
||||
if (argv.length !== 3) throw `only one argument allowed`;
|
||||
if (argv.length !== 3) throw Error('only one argument allowed');
|
||||
const value = argv[2];
|
||||
const intValue = parseInt(value, 10);
|
||||
if (!isIntRE.test(value) || !Number.isInteger(intValue))
|
||||
throw `argument must be an integer`;
|
||||
throw Error('argument must be an integer');
|
||||
return intValue;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ export const getFileName = async (id: string): Promise<string | null> => {
|
||||
let frontMatter = null;
|
||||
try {
|
||||
frontMatter = matter.read(`${path}${file}`);
|
||||
} catch (err) {
|
||||
} catch (_err) {
|
||||
frontMatter = null;
|
||||
}
|
||||
if (String(frontMatter?.data.id) === id) {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// Update given value with markers (labels)
|
||||
const insertErms = (seedCode: string, erms: number[]): string => {
|
||||
if (!erms || erms.length <= 1) {
|
||||
throw `erms should be provided`;
|
||||
throw Error('erms should be provided');
|
||||
}
|
||||
|
||||
if (erms.length <= 1) {
|
||||
throw `erms should contain 2 elements`;
|
||||
throw Error('erms should contain 2 elements');
|
||||
}
|
||||
|
||||
const separator = '\n';
|
||||
|
||||
@@ -45,7 +45,7 @@ function validateMetaData(): void {
|
||||
const filePath = `${getProjectPath()}${id}.md`;
|
||||
try {
|
||||
fs.accessSync(filePath);
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
throw new Error(
|
||||
`The file
|
||||
${filePath}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
import fs from 'fs';
|
||||
import { join } from 'path';
|
||||
import ObjectID from 'bson-objectid';
|
||||
|
||||
@@ -84,7 +84,6 @@ describe('add-text', () => {
|
||||
expect(file.data[instructionsId]).toBe(instructionsSectionText);
|
||||
});
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
it('should add nothing if a section id does not appear in the md', () => {
|
||||
const plugin = addText([missingId]);
|
||||
plugin(mockAST, file);
|
||||
@@ -110,7 +109,6 @@ describe('add-text', () => {
|
||||
expect(file.data[descriptionId]).toEqual(expect.stringContaining(expected));
|
||||
});
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
it('should not add paragraphs when html elements are separated by whitespace', () => {
|
||||
const plugin = addText([instructionsId]);
|
||||
plugin(realisticAST, file);
|
||||
|
||||
@@ -41,8 +41,7 @@ async function initTestFrame(e: InitTestFrameArg = { code: {} }) {
|
||||
JSON.stringify(a) === JSON.stringify(b);
|
||||
|
||||
// Hardcode Deep Freeze dependency
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const DeepFreeze = (o: Record<string, any>) => {
|
||||
const DeepFreeze = (o: Record<string, unknown>) => {
|
||||
Object.freeze(o);
|
||||
Object.getOwnPropertyNames(o).forEach(function (prop) {
|
||||
if (
|
||||
@@ -51,8 +50,7 @@ async function initTestFrame(e: InitTestFrameArg = { code: {} }) {
|
||||
(typeof o[prop] === 'object' || typeof o[prop] === 'function') &&
|
||||
!Object.isFrozen(o[prop])
|
||||
) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
DeepFreeze(o[prop]);
|
||||
DeepFreeze(o[prop] as Record<string, unknown>);
|
||||
}
|
||||
});
|
||||
return o;
|
||||
@@ -73,7 +71,6 @@ async function initTestFrame(e: InitTestFrameArg = { code: {} }) {
|
||||
import(/* webpackChunkName: "enzyme" */ 'enzyme'),
|
||||
import(/* webpackChunkName: "enzyme-adapter" */ 'enzyme-adapter-react-16')
|
||||
]);
|
||||
/* eslint-enable no-inline-comments */
|
||||
|
||||
Enzyme.configure({ adapter: new Adapter16() });
|
||||
/* eslint-enable prefer-const */
|
||||
@@ -96,12 +93,13 @@ async function initTestFrame(e: InitTestFrameArg = { code: {} }) {
|
||||
const test: unknown = eval(testString);
|
||||
resolve(test);
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
reject(err as Error);
|
||||
}
|
||||
})
|
||||
);
|
||||
const test = await testPromise;
|
||||
if (typeof test === 'function') {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||
await test(e.getUserInput);
|
||||
}
|
||||
return { pass: true };
|
||||
|
||||
@@ -90,7 +90,7 @@ ctx.onmessage = async (e: PythonRunEvent) => {
|
||||
eval(testString);
|
||||
resolve(test);
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
reject(err as Error);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -125,7 +125,6 @@ ctx.onmessage = async (e: TestEvaluatorEvent) => {
|
||||
// This can be reassigned by the eval inside the try block, so it should be declared as a let
|
||||
// eslint-disable-next-line prefer-const
|
||||
let __userCodeWasExecuted = false;
|
||||
/* eslint-disable no-eval */
|
||||
try {
|
||||
// Logging is proxyed after the build to catch console.log messages
|
||||
// generated during testing.
|
||||
@@ -152,8 +151,8 @@ ${e.data.testString}`)) as unknown;
|
||||
// the user code does not get executed.
|
||||
testResult = eval(e.data.testString) as unknown;
|
||||
}
|
||||
/* eslint-enable no-eval */
|
||||
if (typeof testResult === 'function') {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||
await testResult((fileName: string) =>
|
||||
__toString(e.data.sources[fileName])
|
||||
);
|
||||
|
||||
@@ -2,8 +2,7 @@ import path from 'path';
|
||||
import fs from 'fs';
|
||||
|
||||
import readdirp from 'readdirp';
|
||||
// TODO: remove chai and use jest's assertion errors
|
||||
import { AssertionError } from 'chai';
|
||||
|
||||
import { SuperBlocks } from '../../../shared/config/curriculum';
|
||||
import {
|
||||
superblockSchemaValidator,
|
||||
@@ -46,10 +45,9 @@ describe('external curriculum data build', () => {
|
||||
const result = validateAvailableSuperBlocks(availableSuperblocks);
|
||||
|
||||
if (result.error) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||
throw new AssertionError(
|
||||
result.error.message,
|
||||
`file: available-superblocks.json`
|
||||
throw Error(
|
||||
`file: available-superblocks.json
|
||||
${result.error.message}`
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -72,11 +70,8 @@ describe('external curriculum data build', () => {
|
||||
const result = validateSuperBlock(JSON.parse(fileContent));
|
||||
|
||||
if (result.error) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||
throw new AssertionError(
|
||||
result.error.message,
|
||||
`file: ${fileInArray}`
|
||||
);
|
||||
throw Error(`file: ${fileInArray}
|
||||
${result.error.message}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -43,7 +43,6 @@ describe('markdown linter', () => {
|
||||
it('should write to the console describing the problem', done => {
|
||||
function callback() {
|
||||
const expected =
|
||||
// eslint-disable-next-line max-len
|
||||
'badYML.md: 19: yaml-linter YAML code blocks should be valid [bad indentation of a mapping entry at line 3, column 17:\n testString: testString\n ^] [Context: "```yml"]';
|
||||
expect(console.log.mock.calls.length).toBe(1);
|
||||
expect(console.log.mock.calls[0][0]).toEqual(
|
||||
|
||||
@@ -33,7 +33,6 @@ function handleError(err, client) {
|
||||
} catch (e) {
|
||||
// no-op
|
||||
} finally {
|
||||
/* eslint-disable-next-line no-process-exit */
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,6 @@ function handleError(err, client) {
|
||||
} catch (e) {
|
||||
// no-op
|
||||
} finally {
|
||||
/* eslint-disable-next-line no-process-exit */
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ function handleError(err, client) {
|
||||
} catch (e) {
|
||||
// no-op
|
||||
} finally {
|
||||
/* eslint-disable-next-line no-process-exit */
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ function handleError(err, client) {
|
||||
} catch (e) {
|
||||
// no-op
|
||||
} finally {
|
||||
/* eslint-disable-next-line no-process-exit */
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable max-len */
|
||||
const { ObjectId } = require('mongodb');
|
||||
|
||||
const blankUserId = new ObjectId('5bd30e0f1caf6ac3ddddddb9');
|
||||
|
||||
Reference in New Issue
Block a user