From e118dda13adfa32e1d8d48f6be3edc851c9c1ca0 Mon Sep 17 00:00:00 2001 From: Oliver Eyton-Williams Date: Mon, 2 Aug 2021 15:39:40 +0200 Subject: [PATCH] fix: order imports and remove circular dependencies (#41824) * fix: remove circular dependency redux depended on templates/Challenges/redux and vice versa. This meant that import order mattered and confusing bugs could arise. (cherry picked from commit 7d67a4e70922bbb3051f2f9982dcc69e240d43dc) * feat: require imports to be in alphabetical order Import order generally does not matter, but there are edge cases (circular imports and css imports, for example) where changing order changes behaviour (cherry picked from commit b8d1393a91ec6e068caf8e8498a5c95df68c2b2c) * chore: order imports * fix: lift up challenge description + title comps This brings the classic Show closer to the others as they now all create the description and title components * fix: remove donation-saga/index circular import (cherry picked from commit 51a44ca668a700786d2744feffeae4fdba5fd207) * refactor: extract action-types from settings (cherry picked from commit 25e26124d691c84a0d0827d41dafb761c686fadd) * fix: lint errors * feat: prevent useless renames --- .eslintrc-base.json | 2 + .../src/common/models/User-Credential.js | 4 +- api-server/src/common/models/User-Identity.js | 4 +- api-server/src/common/models/user.js | 39 ++- api-server/src/development-start.js | 2 +- api-server/src/production-start.js | 2 +- api-server/src/server/boot/authentication.js | 8 +- api-server/src/server/boot/certificate.js | 18 +- api-server/src/server/boot/challenge.js | 6 +- api-server/src/server/boot/donate.js | 2 +- api-server/src/server/boot/settings.js | 6 +- api-server/src/server/boot/user.js | 12 +- api-server/src/server/component-passport.js | 8 +- api-server/src/server/index.js | 4 +- .../server/middlewares/constant-headers.js | 2 +- .../src/server/middlewares/passport-login.js | 2 +- .../middlewares/request-authorization.js | 8 +- .../middlewares/request-authorization.test.js | 2 +- api-server/src/server/middlewares/sessions.js | 2 +- api-server/src/server/models/donation.js | 2 +- api-server/src/server/passport-providers.js | 2 +- api-server/src/server/rss/index.js | 2 +- api-server/src/server/utils/donation.test.js | 16 +- .../src/server/utils/getSetAccessToken.js | 2 +- .../server/utils/getSetAccessToken.test.js | 4 +- .../src/server/utils/publicUserProps.js | 2 +- api-server/src/server/utils/redirection.js | 2 +- api-server/src/server/utils/rx.js | 4 +- api-server/src/server/utils/user-stats.js | 8 +- .../src/server/utils/user-stats.test.js | 2 +- client/gatsby-browser.js | 10 +- client/gatsby-node.js | 8 +- client/gatsby-ssr.js | 4 +- client/i18n/schema-validation.js | 8 +- client/i18n/validate-keys.js | 4 +- client/src/analytics/index.tsx | 2 +- client/src/assets/icons/index.tsx | 14 +- client/src/assets/images/components/index.tsx | 6 +- .../client-only-routes/show-certification.tsx | 32 +-- .../show-profile-or-four-oh-four.tsx | 8 +- .../client-only-routes/show-project-links.tsx | 17 +- .../src/client-only-routes/show-settings.tsx | 29 ++- .../client-only-routes/show-unsubscribed.tsx | 4 +- client/src/client-only-routes/show-user.tsx | 8 +- client/src/client/workers/test-evaluator.js | 2 +- .../components/Donation/DonateCompletion.js | 6 +- client/src/components/Donation/DonateForm.js | 14 +- .../src/components/Donation/DonationModal.js | 23 +- .../Donation/PayPalButtonScriptLoader.js | 4 +- .../src/components/Donation/PaypalButton.js | 9 +- client/src/components/Flash/index.tsx | 6 +- client/src/components/Flash/redux/index.ts | 2 +- client/src/components/FourOhFour/index.tsx | 4 +- client/src/components/Header/Header.test.js | 9 +- .../components/Header/components/Login.tsx | 6 +- .../Header/components/auth-or-profile.tsx | 2 +- .../Header/components/nav-links.tsx | 14 +- .../Header/components/universal-nav.tsx | 6 +- .../Header/components/user-state.tsx | 8 +- client/src/components/Intro/Intro.test.js | 2 +- .../Intro/components/IntroDescription.js | 4 +- client/src/components/Intro/index.js | 8 +- client/src/components/Map/Map.test.js | 6 +- client/src/components/Map/index.js | 11 +- .../components/SolutionViewer/ProjectModal.js | 6 +- .../SolutionViewer/SolutionViewer.js | 4 +- .../components/app-mount-notifier.test.tsx | 6 +- client/src/components/app-mount-notifier.tsx | 4 +- client/src/components/formHelpers/Form.js | 2 +- .../src/components/formHelpers/Form.test.js | 2 +- .../src/components/formHelpers/FormFields.js | 8 +- .../formHelpers/block-save-button.test.tsx | 2 +- .../formHelpers/block-save-button.tsx | 2 +- .../formHelpers/block-save-wrapper.test.tsx | 2 +- .../components/helpers/avatar-renderer.tsx | 8 +- .../helpers/form/block-save-button.tsx | 2 +- .../src/components/helpers/full-width-row.tsx | 2 +- client/src/components/helpers/link.tsx | 2 +- client/src/components/helpers/loader.test.tsx | 2 +- .../src/components/helpers/slim-width-row.tsx | 2 +- .../src/components/helpers/toggle-button.tsx | 4 +- client/src/components/landing/Landing.test.js | 2 +- .../components/landing/components/AsSeenIn.js | 4 +- .../landing/components/BigCallToAction.js | 4 +- .../landing/components/CampersImage.js | 8 +- .../landing/components/Certifications.js | 8 +- .../landing/components/LandingTop.js | 10 +- .../landing/components/Testimonials.js | 10 +- client/src/components/landing/index.js | 10 +- .../src/components/layouts/Certification.js | 6 +- client/src/components/layouts/Default.js | 29 ++- client/src/components/layouts/learn.tsx | 6 +- .../src/components/profile/Profile.test.tsx | 2 +- client/src/components/profile/Profile.tsx | 4 +- .../components/profile/components/Camper.tsx | 13 +- .../profile/components/Certifications.tsx | 8 +- .../profile/components/HeatMap.test.tsx | 2 +- .../components/profile/components/HeatMap.tsx | 15 +- .../profile/components/Portfolio.tsx | 2 +- .../profile/components/SocialIcons.tsx | 6 +- .../profile/components/TimeLine.test.tsx | 4 +- .../profile/components/TimeLine.tsx | 22 +- client/src/components/redirect-home.ts | 2 +- .../components/search/WithInstantSearch.js | 19 +- .../search/searchBar/search-bar.tsx | 10 +- .../search/searchBar/search-hits.tsx | 8 +- .../search/searchBar/search-suggestion.tsx | 2 +- .../search/searchPage/search-page-hits.tsx | 2 +- .../src/components/settings/Certification.js | 12 +- .../components/settings/Certification.test.js | 2 +- .../src/components/settings/Honesty.test.js | 4 +- client/src/components/settings/about.tsx | 6 +- .../src/components/settings/danger-zone.tsx | 6 +- .../src/components/settings/delete-modal.tsx | 2 +- client/src/components/settings/email.tsx | 12 +- client/src/components/settings/honesty.tsx | 4 +- client/src/components/settings/internet.tsx | 8 +- client/src/components/settings/portfolio.tsx | 8 +- client/src/components/settings/privacy.tsx | 10 +- .../src/components/settings/reset-modal.tsx | 2 +- client/src/components/settings/theme.tsx | 2 +- .../components/settings/toggle-setting.tsx | 4 +- client/src/components/settings/username.tsx | 12 +- client/src/pages/404.tsx | 4 +- client/src/pages/certification.tsx | 4 +- client/src/pages/challenges.test.ts | 2 +- client/src/pages/challenges.tsx | 2 +- client/src/pages/donate.tsx | 10 +- client/src/pages/email-sign-up.tsx | 19 +- client/src/pages/learn.tsx | 10 +- client/src/pages/settings.tsx | 4 +- client/src/pages/unsubscribed.tsx | 4 +- client/src/pages/update-email.tsx | 18 +- client/src/pages/user.tsx | 4 +- client/src/redux/accept-terms-saga.js | 4 +- client/src/redux/action-types.js | 33 +++ client/src/redux/app-mount-saga.js | 2 +- client/src/redux/createStore.js | 9 +- client/src/redux/donation-saga.js | 7 +- client/src/redux/error-saga.js | 4 +- client/src/redux/failed-updates-epic.js | 18 +- client/src/redux/failed-updates-epic.test.js | 8 +- client/src/redux/fetch-user-saga.js | 4 +- client/src/redux/ga-saga.test.js | 10 +- client/src/redux/hard-go-to-epic.js | 4 +- client/src/redux/index.js | 187 ++++++-------- client/src/redux/report-user-saga.js | 4 +- client/src/redux/rootEpic.js | 2 +- client/src/redux/rootReducer.js | 16 +- client/src/redux/rootSaga.js | 4 +- client/src/redux/settings/action-types.js | 19 ++ client/src/redux/settings/danger-zone-saga.js | 7 +- client/src/redux/settings/index.js | 20 +- client/src/redux/settings/settings-sagas.js | 18 +- .../src/redux/settings/update-email-saga.js | 2 +- client/src/redux/show-cert-saga.js | 2 +- client/src/redux/update-complete-epic.js | 3 +- .../templates/Challenges/classic/ActionRow.js | 4 +- .../Challenges/classic/DesktopLayout.js | 8 +- .../Challenges/classic/EditorTabs.js | 2 +- .../Challenges/classic/MobileLayout.js | 14 +- .../Challenges/classic/MultifileEditor.js | 2 +- .../src/templates/Challenges/classic/Show.tsx | 69 +++-- .../templates/Challenges/classic/editor.tsx | 39 ++- .../templates/Challenges/codeally/show.tsx | 8 +- .../Challenges/components/HelpModal.js | 12 +- .../Challenges/components/Hotkeys.tsx | 2 +- .../Challenges/components/Preview.js | 6 +- .../Challenges/components/ResetModal.tsx | 10 +- .../Challenges/components/Side-Panel.js | 66 ++--- .../Challenges/components/Test-Suite.js | 4 +- .../Challenges/components/Tool-Panel.js | 11 +- .../Challenges/components/VideoModal.js | 10 +- .../Challenges/components/bread-crumb.tsx | 2 +- .../Challenges/components/challenge-title.tsx | 9 +- .../components/completion-modal-body.test.tsx | 2 +- .../components/completion-modal-body.tsx | 4 +- .../components/completion-modal.tsx | 25 +- .../Challenges/components/output.tsx | 2 +- .../Challenges/components/prism-formatted.tsx | 2 +- .../Challenges/projects/backend/Show.tsx | 38 +-- .../Challenges/projects/frontend/Show.tsx | 22 +- .../Challenges/projects/solution-form.tsx | 2 +- .../Challenges/projects/tool-panel.tsx | 6 +- .../Challenges/rechallenge/transformers.js | 10 +- .../Challenges/redux/action-types.js | 48 ++++ .../Challenges/redux/code-lock-epic.js | 7 +- .../Challenges/redux/code-storage-epic.js | 22 +- .../Challenges/redux/completion-epic.js | 36 +-- .../Challenges/redux/create-question-epic.js | 10 +- .../redux/execute-challenge-saga.js | 31 ++- .../src/templates/Challenges/redux/index.js | 237 ++++++++---------- .../src/templates/Challenges/utils/build.js | 19 +- .../Challenges/utils/getTargetEditor.js | 2 +- .../src/templates/Challenges/video/Show.tsx | 22 +- client/src/templates/Introduction/Intro.js | 12 +- .../templates/Introduction/SuperBlockIntro.js | 24 +- .../Introduction/components/Block.js | 22 +- .../Introduction/components/CertChallenge.js | 24 +- .../components/CertificationCard.js | 12 +- .../Introduction/components/Challenges.js | 14 +- .../Introduction/components/ClaimCertSteps.js | 8 +- .../components/SuperBlockIntro.js | 4 +- client/src/utils/ajax.ts | 2 +- client/src/utils/curriculum-helpers.test.ts | 4 +- client/src/utils/curriculum-helpers.ts | 2 +- client/src/utils/handled-error.test.ts | 2 +- client/src/utils/handled-error.ts | 7 +- client/utils/buildChallenges.js | 4 +- client/utils/gatsby/layoutSelector.test.js | 6 +- client/utils/tags.js | 2 +- client/webpack-workers.js | 6 +- curriculum/getChallenges.js | 17 +- curriculum/gulpfile.js | 2 +- curriculum/test/test-challenges.js | 71 +++--- curriculum/test/utils/mongoIds.js | 2 +- .../test/utils/plugins/get-css-comments.js | 2 +- .../utils/plugins/get-script-js-comments.js | 2 +- .../create-next-step.js | 2 +- .../create-project.ts | 6 +- tools/challenge-helper-scripts/utils.js | 6 +- tools/challenge-helper-scripts/utils.test.js | 4 +- tools/challenge-parser/parser/index.js | 16 +- .../parser/plugins/add-frontmatter.js | 2 +- .../parser/plugins/add-seed.js | 6 +- .../parser/plugins/add-seed.test.js | 22 +- .../parser/plugins/add-solution.js | 6 +- .../parser/plugins/add-solution.test.js | 4 +- .../parser/plugins/add-tests.test.js | 2 +- .../parser/plugins/add-text.test.js | 2 +- .../parser/plugins/replace-imports.js | 6 +- .../parser/plugins/replace-imports.test.js | 8 +- .../parser/plugins/restore-directives.js | 4 +- .../parser/plugins/restore-directives.test.js | 4 +- .../parser/plugins/table-and-strikethrough.js | 2 +- .../parser/plugins/utils/between-headings.js | 2 +- .../plugins/utils/between-headings.test.js | 6 +- .../parser/plugins/utils/get-file-visitor.js | 2 +- .../parser/plugins/utils/get-id.test.js | 2 +- .../parser/plugins/utils/mdast-to-html.js | 2 +- .../plugins/utils/mdast-to-html.test.js | 4 +- .../parser/tools/full-parse.js | 2 +- .../parser/tools/generate-ast.js | 2 +- .../parser/tools/inspect-ast.js | 4 +- .../challenge-parser/parser/tools/parse-md.js | 2 +- .../translation-parser/index.test.js | 8 +- .../crowdin/actions/convert-chinese/index.js | 2 +- .../actions/hide-specific-string/index.js | 2 +- tools/crowdin/utils/dirs.js | 4 +- tools/crowdin/utils/files.js | 2 +- tools/scripts/build/build-curriculum.js | 2 +- tools/scripts/build/ensure-env.js | 2 +- tools/scripts/lint/index.js | 2 +- tools/scripts/seed/seedAuthUser.js | 11 +- tools/ui-components/rollup.config.js | 4 +- tools/ui-components/src/button.js | 2 +- tools/ui-components/src/button.test.js | 2 +- tools/ui-components/src/color-system.js | 2 +- utils/sort-files.test.js | 2 +- 259 files changed, 1269 insertions(+), 1303 deletions(-) create mode 100644 client/src/redux/action-types.js create mode 100644 client/src/redux/settings/action-types.js create mode 100644 client/src/templates/Challenges/redux/action-types.js diff --git a/.eslintrc-base.json b/.eslintrc-base.json index fb9225b7ff..6941a96a20 100644 --- a/.eslintrc-base.json +++ b/.eslintrc-base.json @@ -32,6 +32,7 @@ "import/newline-after-import": 2, "import/no-duplicates": 2, "import/no-unresolved": [2, { "commonjs": true }], + "import/order": [2, { "alphabetize": { "order": "asc" } }], "import/unambiguous": 2, "import/no-anonymous-default-export": 2, "jsx-quotes": [2, "prefer-single"], @@ -122,6 +123,7 @@ "no-unused-expressions": 2, "no-unused-vars": 2, "no-use-before-define": 0, + "no-useless-rename": 2, "no-void": 0, "no-warning-comments": [2, { "terms": ["fixme"], "location": "start" }], "no-with": 2, diff --git a/api-server/src/common/models/User-Credential.js b/api-server/src/common/models/User-Credential.js index 8a6d44af60..1e5688d5e0 100644 --- a/api-server/src/common/models/User-Credential.js +++ b/api-server/src/common/models/User-Credential.js @@ -1,11 +1,11 @@ -import { Observable } from 'rx'; import debug from 'debug'; +import { Observable } from 'rx'; -import { observeMethod, observeQuery } from '../../server/utils/rx'; import { createUserUpdatesFromProfile, getSocialProvider } from '../../server/utils/auth'; +import { observeMethod, observeQuery } from '../../server/utils/rx'; const log = debug('fcc:models:UserCredential'); module.exports = function (UserCredential) { diff --git a/api-server/src/common/models/User-Identity.js b/api-server/src/common/models/User-Identity.js index 67e2718ea8..8905c2edb6 100644 --- a/api-server/src/common/models/User-Identity.js +++ b/api-server/src/common/models/User-Identity.js @@ -1,10 +1,10 @@ +import dedent from 'dedent'; import { Observable } from 'rx'; // import debug from 'debug'; -import dedent from 'dedent'; import { isEmail } from 'validator'; -import { observeMethod, observeQuery } from '../../server/utils/rx'; import { wrapHandledError } from '../../server/utils/create-handled-error.js'; +import { observeMethod, observeQuery } from '../../server/utils/rx'; // const log = debug('fcc:models:userIdent'); diff --git a/api-server/src/common/models/user.js b/api-server/src/common/models/user.js index 90ce86ab13..38da2a3e3b 100644 --- a/api-server/src/common/models/user.js +++ b/api-server/src/common/models/user.js @@ -5,18 +5,31 @@ * */ +import badwordFilter from 'bad-words'; +import debugFactory from 'debug'; +import dedent from 'dedent'; +import _ from 'lodash'; +import moment from 'moment'; +import generate from 'nanoid/generate'; import { Observable } from 'rx'; import uuid from 'uuid/v4'; -import moment from 'moment'; -import dedent from 'dedent'; -import debugFactory from 'debug'; import { isEmail } from 'validator'; -import _ from 'lodash'; -import generate from 'nanoid/generate'; -import badwordFilter from 'bad-words'; +import { blocklistedUsernames } from '../../../../config/constants'; import { apiLocation } from '../../../../config/env.json'; +import { wrapHandledError } from '../../server/utils/create-handled-error.js'; +import { + setAccessTokenToResponse, + removeCookies +} from '../../server/utils/getSetAccessToken'; +import { + normaliseUserFields, + getProgress, + publicUserProps +} from '../../server/utils/publicUserProps'; +import { saveUser, observeMethod } from '../../server/utils/rx.js'; +import { getEmailSender } from '../../server/utils/url-utils'; import { fixCompletedChallengeItem, getEncodedEmail, @@ -26,20 +39,6 @@ import { renderSignInEmail } from '../utils'; -import { blocklistedUsernames } from '../../../../config/constants'; -import { wrapHandledError } from '../../server/utils/create-handled-error.js'; -import { saveUser, observeMethod } from '../../server/utils/rx.js'; -import { getEmailSender } from '../../server/utils/url-utils'; -import { - normaliseUserFields, - getProgress, - publicUserProps -} from '../../server/utils/publicUserProps'; -import { - setAccessTokenToResponse, - removeCookies -} from '../../server/utils/getSetAccessToken'; - const log = debugFactory('fcc:models:user'); const BROWNIEPOINTS_TIMEOUT = [1, 'hour']; const nanoidCharSet = diff --git a/api-server/src/development-start.js b/api-server/src/development-start.js index dbae1cd766..1110b7196f 100644 --- a/api-server/src/development-start.js +++ b/api-server/src/development-start.js @@ -1,9 +1,9 @@ const path = require('path'); require('dotenv').config({ path: path.resolve(__dirname, '../../.env') }); +const createDebugger = require('debug'); const nodemon = require('nodemon'); const SmeeClient = require('smee-client'); -const createDebugger = require('debug'); const log = createDebugger('fcc:start:development'); diff --git a/api-server/src/production-start.js b/api-server/src/production-start.js index 6d05d65c1a..ac389b2635 100644 --- a/api-server/src/production-start.js +++ b/api-server/src/production-start.js @@ -1,6 +1,6 @@ // this ensures node understands the future -const _ = require('lodash'); const createDebugger = require('debug'); +const _ = require('lodash'); const log = createDebugger('fcc:server:production-start'); const startTime = Date.now(); diff --git a/api-server/src/server/boot/authentication.js b/api-server/src/server/boot/authentication.js index 45fc4f29f7..a662fe4d1d 100644 --- a/api-server/src/server/boot/authentication.js +++ b/api-server/src/server/boot/authentication.js @@ -1,20 +1,20 @@ -import passport from 'passport'; import dedent from 'dedent'; import { check } from 'express-validator'; -import { isEmail } from 'validator'; import jwt from 'jsonwebtoken'; +import passport from 'passport'; +import { isEmail } from 'validator'; import { jwtSecret } from '../../../../config/secrets'; +import { decodeEmail } from '../../common/utils'; import { createPassportCallbackAuthenticator, devSaveResponseAuthCookies, devLoginRedirect } from '../component-passport'; -import { ifUserRedirectTo, ifNoUserRedirectHome } from '../utils/middleware'; import { wrapHandledError } from '../utils/create-handled-error.js'; import { removeCookies } from '../utils/getSetAccessToken'; -import { decodeEmail } from '../../common/utils'; +import { ifUserRedirectTo, ifNoUserRedirectHome } from '../utils/middleware'; import { getRedirectParams } from '../utils/redirection'; const passwordlessGetValidators = [ diff --git a/api-server/src/server/boot/certificate.js b/api-server/src/server/boot/certificate.js index 74fa545d86..e5c5fcb371 100644 --- a/api-server/src/server/boot/certificate.js +++ b/api-server/src/server/boot/certificate.js @@ -1,17 +1,10 @@ +import path from 'path'; +import debug from 'debug'; +import dedent from 'dedent'; import _ from 'lodash'; import loopback from 'loopback'; -import path from 'path'; -import dedent from 'dedent'; import { Observable } from 'rx'; -import debug from 'debug'; import { isEmail } from 'validator'; -import { reportError } from '../middlewares/sentry-error-handler.js'; - -import { ifNoUser401 } from '../utils/middleware'; -import { observeQuery } from '../utils/rx'; - -import { getChallenges } from '../utils/get-curriculum'; - import { completionHours, certTypes, @@ -22,6 +15,11 @@ import { oldDataVizId, superBlockCertTypeMap } from '../../../../config/certification-settings'; +import { reportError } from '../middlewares/sentry-error-handler.js'; + +import { getChallenges } from '../utils/get-curriculum'; +import { ifNoUser401 } from '../utils/middleware'; +import { observeQuery } from '../utils/rx'; const { legacyFrontEndChallengeId, diff --git a/api-server/src/server/boot/challenge.js b/api-server/src/server/boot/challenge.js index 98c3a0f64c..dd9b771db5 100644 --- a/api-server/src/server/boot/challenge.js +++ b/api-server/src/server/boot/challenge.js @@ -4,17 +4,17 @@ * a db migration to fix all completedChallenges * */ -import { Observable } from 'rx'; -import { isEmpty, pick, omit, find, uniqBy } from 'lodash'; import debug from 'debug'; import dedent from 'dedent'; +import { isEmpty, pick, omit, find, uniqBy } from 'lodash'; import { ObjectID } from 'mongodb'; +import { Observable } from 'rx'; import isNumeric from 'validator/lib/isNumeric'; import isURL from 'validator/lib/isURL'; -import { ifNoUserSend } from '../utils/middleware'; import { fixCompletedChallengeItem } from '../../common/utils'; import { getChallenges } from '../utils/get-curriculum'; +import { ifNoUserSend } from '../utils/middleware'; import { getRedirectParams, normalizeParams, diff --git a/api-server/src/server/boot/donate.js b/api-server/src/server/boot/donate.js index 099908de47..4bdd7802b9 100644 --- a/api-server/src/server/boot/donate.js +++ b/api-server/src/server/boot/donate.js @@ -1,11 +1,11 @@ import debug from 'debug'; +import keys from '../../../../config/secrets'; import { getAsyncPaypalToken, verifyWebHook, updateUser, verifyWebHookType } from '../utils/donation'; -import keys from '../../../../config/secrets'; const log = debug('fcc:boot:donate'); diff --git a/api-server/src/server/boot/settings.js b/api-server/src/server/boot/settings.js index d3d02d6c59..26de9e4050 100644 --- a/api-server/src/server/boot/settings.js +++ b/api-server/src/server/boot/settings.js @@ -1,10 +1,10 @@ import debug from 'debug'; import { check } from 'express-validator'; -import { ifNoUser401, createValidatorErrorHandler } from '../utils/middleware'; -import { themes } from '../../common/utils/themes.js'; -import { alertTypes } from '../../common/utils/flash.js'; import { isValidUsername } from '../../../../utils/validate'; +import { alertTypes } from '../../common/utils/flash.js'; +import { themes } from '../../common/utils/themes.js'; +import { ifNoUser401, createValidatorErrorHandler } from '../utils/middleware'; const log = debug('fcc:boot:settings'); diff --git a/api-server/src/server/boot/user.js b/api-server/src/server/boot/user.js index 1caec372ff..24ae6213bc 100644 --- a/api-server/src/server/boot/user.js +++ b/api-server/src/server/boot/user.js @@ -1,19 +1,19 @@ -import dedent from 'dedent'; import debugFactory from 'debug'; +import dedent from 'dedent'; +import { body } from 'express-validator'; import { pick } from 'lodash'; import { Observable } from 'rx'; -import { body } from 'express-validator'; +import { fixCompletedChallengeItem } from '../../common/utils'; +import { removeCookies } from '../utils/getSetAccessToken'; +import { ifNoUser401, ifNoUserRedirectHome } from '../utils/middleware'; import { getProgress, normaliseUserFields, userPropsForSession } from '../utils/publicUserProps'; -import { fixCompletedChallengeItem } from '../../common/utils'; -import { ifNoUser401, ifNoUserRedirectHome } from '../utils/middleware'; -import { removeCookies } from '../utils/getSetAccessToken'; -import { trimTags } from '../utils/validators'; import { getRedirectParams } from '../utils/redirection'; +import { trimTags } from '../utils/validators'; const log = debugFactory('fcc:boot:user'); const sendNonUserToHome = ifNoUserRedirectHome(); diff --git a/api-server/src/server/component-passport.js b/api-server/src/server/component-passport.js index 568387d638..e8d9d087b5 100644 --- a/api-server/src/server/component-passport.js +++ b/api-server/src/server/component-passport.js @@ -1,12 +1,13 @@ -import passport from 'passport'; // eslint-disable-next-line import { // prettier ignore PassportConfigurator } from '@freecodecamp/loopback-component-passport'; import dedent from 'dedent'; +import passport from 'passport'; -import { getUserById } from './utils/user-stats'; +import { availableLangs } from '../../../config/i18n/all-langs'; +import { jwtSecret } from '../../../config/secrets'; import passportProviders from './passport-providers'; import { setAccessTokenToResponse } from './utils/getSetAccessToken'; import { @@ -15,8 +16,7 @@ import { getRedirectParams, haveSamePath } from './utils/redirection'; -import { jwtSecret } from '../../../config/secrets'; -import { availableLangs } from '../../../config/i18n/all-langs'; +import { getUserById } from './utils/user-stats'; const passportOptions = { emailOptional: true, diff --git a/api-server/src/server/index.js b/api-server/src/server/index.js index 7d56e11474..8d04743831 100644 --- a/api-server/src/server/index.js +++ b/api-server/src/server/index.js @@ -1,12 +1,12 @@ const path = require('path'); require('dotenv').config({ path: path.resolve(__dirname, '../../../.env') }); +const Sentry = require('@sentry/node'); +const createDebugger = require('debug'); const _ = require('lodash'); const loopback = require('loopback'); const boot = require('loopback-boot'); -const createDebugger = require('debug'); const morgan = require('morgan'); -const Sentry = require('@sentry/node'); const { sentry } = require('../../../config/secrets'); const { setupPassport } = require('./component-passport'); diff --git a/api-server/src/server/middlewares/constant-headers.js b/api-server/src/server/middlewares/constant-headers.js index 2daef38919..a26a2f71eb 100644 --- a/api-server/src/server/middlewares/constant-headers.js +++ b/api-server/src/server/middlewares/constant-headers.js @@ -1,5 +1,5 @@ -import { homeLocation } from '../../../../config/env.json'; import { allowedOrigins } from '../../../../config/cors-settings'; +import { homeLocation } from '../../../../config/env.json'; export default function constantHeaders() { return function (req, res, next) { diff --git a/api-server/src/server/middlewares/passport-login.js b/api-server/src/server/middlewares/passport-login.js index 0bb4e1fa42..d58b8df13a 100644 --- a/api-server/src/server/middlewares/passport-login.js +++ b/api-server/src/server/middlewares/passport-login.js @@ -1,6 +1,6 @@ import _ from 'lodash'; -import { Observable } from 'rx'; import { login } from 'passport/lib/http/request'; +import { Observable } from 'rx'; // make login polymorphic // if supplied callback it works as normal diff --git a/api-server/src/server/middlewares/request-authorization.js b/api-server/src/server/middlewares/request-authorization.js index 18e0f88289..8d5970e7bb 100644 --- a/api-server/src/server/middlewares/request-authorization.js +++ b/api-server/src/server/middlewares/request-authorization.js @@ -1,15 +1,15 @@ import { isEmpty } from 'lodash'; -import { getUserById as _getUserById } from '../utils/user-stats'; +import { jwtSecret as _jwtSecret } from '../../../../config/secrets'; + +import { wrapHandledError } from '../utils/create-handled-error'; import { getAccessTokenFromRequest, errorTypes, authHeaderNS } from '../utils/getSetAccessToken'; -import { jwtSecret as _jwtSecret } from '../../../../config/secrets'; - -import { wrapHandledError } from '../utils/create-handled-error'; import { getRedirectParams } from '../utils/redirection'; +import { getUserById as _getUserById } from '../utils/user-stats'; const authRE = /^\/auth\//; const confirmEmailRE = /^\/confirm-email$/; diff --git a/api-server/src/server/middlewares/request-authorization.test.js b/api-server/src/server/middlewares/request-authorization.test.js index 8bd139000d..593d5930ed 100644 --- a/api-server/src/server/middlewares/request-authorization.test.js +++ b/api-server/src/server/middlewares/request-authorization.test.js @@ -1,7 +1,7 @@ -import { mockReq as mockRequest, mockRes } from '../boot_tests/challenge.test'; import jwt from 'jsonwebtoken'; import { homeLocation } from '../../../../config/env.json'; +import { mockReq as mockRequest, mockRes } from '../boot_tests/challenge.test'; import createRequestAuthorization, { isAllowedPath } from './request-authorization'; diff --git a/api-server/src/server/middlewares/sessions.js b/api-server/src/server/middlewares/sessions.js index cc488cd790..219ed97724 100644 --- a/api-server/src/server/middlewares/sessions.js +++ b/api-server/src/server/middlewares/sessions.js @@ -1,5 +1,5 @@ -import session from 'express-session'; import MongoStoreFactory from 'connect-mongo'; +import session from 'express-session'; const MongoStore = MongoStoreFactory(session); const sessionSecret = process.env.SESSION_SECRET; diff --git a/api-server/src/server/models/donation.js b/api-server/src/server/models/donation.js index 0cf543c0b2..ae9bd7f608 100644 --- a/api-server/src/server/models/donation.js +++ b/api-server/src/server/models/donation.js @@ -1,5 +1,5 @@ -import { Observable } from 'rx'; import debug from 'debug'; +import { Observable } from 'rx'; import { reportError } from '../middlewares/sentry-error-handler.js'; import InMemoryCache from '../utils/in-memory-cache'; diff --git a/api-server/src/server/passport-providers.js b/api-server/src/server/passport-providers.js index 092e8ef323..e6938162c4 100644 --- a/api-server/src/server/passport-providers.js +++ b/api-server/src/server/passport-providers.js @@ -1,5 +1,5 @@ -import { auth0 } from '../../../config/secrets'; import { homeLocation, apiLocation } from '../../../config/env.json'; +import { auth0 } from '../../../config/secrets'; const { clientID, clientSecret, domain } = auth0; diff --git a/api-server/src/server/rss/index.js b/api-server/src/server/rss/index.js index 1112970ba4..c6f59b74fe 100644 --- a/api-server/src/server/rss/index.js +++ b/api-server/src/server/rss/index.js @@ -1,6 +1,6 @@ -import _ from 'lodash'; import compareDesc from 'date-fns/compare_desc'; import debug from 'debug'; +import _ from 'lodash'; import { getLybsynFeed } from './lybsyn'; diff --git a/api-server/src/server/utils/donation.test.js b/api-server/src/server/utils/donation.test.js index 8dc43098ee..b30f8f5f44 100644 --- a/api-server/src/server/utils/donation.test.js +++ b/api-server/src/server/utils/donation.test.js @@ -1,14 +1,6 @@ /* eslint-disable camelcase */ import axios from 'axios'; import keys from '../../../../config/secrets'; -import { - getAsyncPaypalToken, - verifyWebHook, - updateUser, - capitalizeKeys, - createDonationObj -} from './donation'; -import { mockActivationHook, mockCancellationHook } from './__mocks__/donation'; import { mockApp, createDonationMockFn, @@ -16,6 +8,14 @@ import { updateDonationAttr, updateUserAttr } from '../boot_tests/fixtures'; +import { mockActivationHook, mockCancellationHook } from './__mocks__/donation'; +import { + getAsyncPaypalToken, + verifyWebHook, + updateUser, + capitalizeKeys, + createDonationObj +} from './donation'; jest.mock('axios'); diff --git a/api-server/src/server/utils/getSetAccessToken.js b/api-server/src/server/utils/getSetAccessToken.js index a7ffe8bfa9..bf19cddedf 100644 --- a/api-server/src/server/utils/getSetAccessToken.js +++ b/api-server/src/server/utils/getSetAccessToken.js @@ -1,5 +1,5 @@ -import jwt from 'jsonwebtoken'; import { isBefore } from 'date-fns'; +import jwt from 'jsonwebtoken'; import { jwtSecret as _jwtSecret } from '../../../../config/secrets'; diff --git a/api-server/src/server/utils/getSetAccessToken.test.js b/api-server/src/server/utils/getSetAccessToken.test.js index 477948eb9a..768d0ea0d0 100644 --- a/api-server/src/server/utils/getSetAccessToken.test.js +++ b/api-server/src/server/utils/getSetAccessToken.test.js @@ -1,11 +1,11 @@ +import jwt from 'jsonwebtoken'; +import { mockReq, mockRes } from '../boot_tests/challenge.test'; import { getAccessTokenFromRequest, errorTypes, setAccessTokenToResponse, removeCookies } from './getSetAccessToken'; -import { mockReq, mockRes } from '../boot_tests/challenge.test'; -import jwt from 'jsonwebtoken'; describe('getSetAccessToken', () => { const validJWTSecret = 'this is a super secret string'; diff --git a/api-server/src/server/utils/publicUserProps.js b/api-server/src/server/utils/publicUserProps.js index 6c1282bedf..e73e1be05d 100644 --- a/api-server/src/server/utils/publicUserProps.js +++ b/api-server/src/server/utils/publicUserProps.js @@ -1,11 +1,11 @@ import { isURL } from 'validator'; -import { addPlaceholderImage } from './'; import { prepUniqueDaysByHours, calcCurrentStreak, calcLongestStreak } from '../utils/user-stats'; +import { addPlaceholderImage } from './'; export const publicUserProps = [ 'about', diff --git a/api-server/src/server/utils/redirection.js b/api-server/src/server/utils/redirection.js index de441ee625..326d9c1c45 100644 --- a/api-server/src/server/utils/redirection.js +++ b/api-server/src/server/utils/redirection.js @@ -1,9 +1,9 @@ const jwt = require('jsonwebtoken'); -const { availableLangs } = require('../../../../config/i18n/all-langs'); const { allowedOrigins } = require('../../../../config/cors-settings'); // homeLocation is being used as a fallback here. If the one provided by the // client is invalid we default to this. const { homeLocation } = require('../../../../config/env.json'); +const { availableLangs } = require('../../../../config/i18n/all-langs'); function getReturnTo(encryptedParams, secret, _homeLocation = homeLocation) { let params; diff --git a/api-server/src/server/utils/rx.js b/api-server/src/server/utils/rx.js index 331a18f728..de1cd7d40b 100644 --- a/api-server/src/server/utils/rx.js +++ b/api-server/src/server/utils/rx.js @@ -1,6 +1,6 @@ -import Rx, { AsyncSubject, Observable } from 'rx'; -import moment from 'moment'; import debugFactory from 'debug'; +import moment from 'moment'; +import Rx, { AsyncSubject, Observable } from 'rx'; const debug = debugFactory('fcc:rxUtils'); diff --git a/api-server/src/server/utils/user-stats.js b/api-server/src/server/utils/user-stats.js index 9e154af5a6..95203071b9 100644 --- a/api-server/src/server/utils/user-stats.js +++ b/api-server/src/server/utils/user-stats.js @@ -1,11 +1,11 @@ -import loopback from 'loopback'; +import { isEmpty } from 'lodash'; import compose from 'lodash/fp/compose'; +import forEachRight from 'lodash/fp/forEachRight'; +import last from 'lodash/fp/last'; import map from 'lodash/fp/map'; import sortBy from 'lodash/fp/sortBy'; import trans from 'lodash/fp/transform'; -import last from 'lodash/fp/last'; -import forEachRight from 'lodash/fp/forEachRight'; -import { isEmpty } from 'lodash'; +import loopback from 'loopback'; import moment from 'moment-timezone'; import { dayCount } from '../utils/date-utils'; diff --git a/api-server/src/server/utils/user-stats.test.js b/api-server/src/server/utils/user-stats.test.js index 321622509f..5b66caa26f 100644 --- a/api-server/src/server/utils/user-stats.test.js +++ b/api-server/src/server/utils/user-stats.test.js @@ -1,12 +1,12 @@ import moment from 'moment-timezone'; +import { mockUserID, mockApp, mockUser } from '../boot_tests/fixtures'; import { prepUniqueDaysByHours, calcCurrentStreak, calcLongestStreak, getUserById } from './user-stats'; -import { mockUserID, mockApp, mockUser } from '../boot_tests/fixtures'; jest.useFakeTimers('modern'); const PST = 'America/Los_Angeles'; diff --git a/client/gatsby-browser.js b/client/gatsby-browser.js index 0a4074c55d..e2292ec15c 100644 --- a/client/gatsby-browser.js +++ b/client/gatsby-browser.js @@ -1,12 +1,12 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { Provider } from 'react-redux'; -import { I18nextProvider } from 'react-i18next'; import cookies from 'browser-cookies'; +import PropTypes from 'prop-types'; +import React from 'react'; +import { I18nextProvider } from 'react-i18next'; +import { Provider } from 'react-redux'; import i18n from './i18n/config'; -import { createStore } from './src/redux/createStore'; import AppMountNotifier from './src/components/app-mount-notifier'; +import { createStore } from './src/redux/createStore'; import layoutSelector from './utils/gatsby/layout-selector'; const store = createStore(); diff --git a/client/gatsby-node.js b/client/gatsby-node.js index 86e3ad012d..4d4fa4d406 100644 --- a/client/gatsby-node.js +++ b/client/gatsby-node.js @@ -1,10 +1,10 @@ -const env = require('../config/env.json'); -const webpack = require('webpack'); - const { createFilePath } = require('gatsby-source-filesystem'); // TODO: ideally we'd remove lodash and just use lodash-es, but we can't require // es modules here. const uniq = require('lodash/uniq'); +const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin'); +const webpack = require('webpack'); +const env = require('../config/env.json'); const { blockNameify } = require('../utils/block-nameify'); const { @@ -165,8 +165,6 @@ exports.createPages = function createPages({ graphql, actions, reporter }) { }); }; -const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin'); - exports.onCreateWebpackConfig = ({ stage, actions }) => { const newPlugins = [ // We add the shims of the node globals to the global scope diff --git a/client/gatsby-ssr.js b/client/gatsby-ssr.js index b488ead6ab..c0c0fd3cca 100644 --- a/client/gatsby-ssr.js +++ b/client/gatsby-ssr.js @@ -1,7 +1,7 @@ -import React from 'react'; import PropTypes from 'prop-types'; -import { Provider } from 'react-redux'; +import React from 'react'; import { I18nextProvider } from 'react-i18next'; +import { Provider } from 'react-redux'; import i18n from './i18n/config'; import { createStore } from './src/redux/createStore'; diff --git a/client/i18n/schema-validation.js b/client/i18n/schema-validation.js index f7e77a3956..d6efa04f84 100644 --- a/client/i18n/schema-validation.js +++ b/client/i18n/schema-validation.js @@ -1,11 +1,11 @@ const path = require('path'); const { availableLangs } = require('../../config/i18n/all-langs'); +const introSchema = require('./locales/english/intro.json'); +const linksSchema = require('./locales/english/links.json'); +const metaTagsSchema = require('./locales/english/meta-tags.json'); +const motivationSchema = require('./locales/english/motivation.json'); const translationsSchema = require('./locales/english/translations.json'); const trendingSchema = require('./locales/english/trending.json'); -const motivationSchema = require('./locales/english/motivation.json'); -const introSchema = require('./locales/english/intro.json'); -const metaTagsSchema = require('./locales/english/meta-tags.json'); -const linksSchema = require('./locales/english/links.json'); /** * Flattens a nested object structure into a single diff --git a/client/i18n/validate-keys.js b/client/i18n/validate-keys.js index f2e9bbdb03..264c0c1601 100644 --- a/client/i18n/validate-keys.js +++ b/client/i18n/validate-keys.js @@ -1,11 +1,11 @@ const fs = require('fs'); const path = require('path'); -const translationsObject = require('./locales/english/translations.json'); const introObject = require('./locales/english/intro.json'); +const linksObject = require('./locales/english/links.json'); const metaObject = require('./locales/english/meta-tags.json'); const motivationObject = require('./locales/english/motivation.json'); +const translationsObject = require('./locales/english/translations.json'); const trendingObject = require('./locales/english/trending.json'); -const linksObject = require('./locales/english/links.json'); /** * Function to flatten a nested object. Written specifically for diff --git a/client/src/analytics/index.tsx b/client/src/analytics/index.tsx index 670e447239..7979177022 100644 --- a/client/src/analytics/index.tsx +++ b/client/src/analytics/index.tsx @@ -1,9 +1,9 @@ import ReactGA from 'react-ga'; -import envData from '../../../config/env.json'; import { devAnalyticsId, prodAnalyticsId } from '../../../config/analytics-settings'; +import envData from '../../../config/env.json'; const { deploymentEnv } = envData; diff --git a/client/src/assets/icons/index.tsx b/client/src/assets/icons/index.tsx index 3fe4a33b3d..5802bb0430 100644 --- a/client/src/assets/icons/index.tsx +++ b/client/src/assets/icons/index.tsx @@ -1,15 +1,15 @@ import React from 'react'; -import ResponsiveDesign from './responsive-design'; +import APIIcon from './API-icon'; +import D3Icon from './D3-icon'; import JavaScriptIcon from './JavaScript-icon'; import ReactIcon from './React-icon'; -import D3Icon from './D3-icon'; -import APIIcon from './API-icon'; -import Clipboard from './clipboard'; -import PythonIcon from './python-icon'; -import Analytics from './analytics'; -import Shield from './shield'; import TensorflowIcon from './Tensorflow-icon'; import Algorithm from './algorithm'; +import Analytics from './analytics'; +import Clipboard from './clipboard'; +import PythonIcon from './python-icon'; +import ResponsiveDesign from './responsive-design'; +import Shield from './shield'; const iconMap = { 'responsive-web-design': ResponsiveDesign, diff --git a/client/src/assets/images/components/index.tsx b/client/src/assets/images/components/index.tsx index 88148056d1..f254ef7160 100644 --- a/client/src/assets/images/components/index.tsx +++ b/client/src/assets/images/components/index.tsx @@ -1,9 +1,9 @@ -import AppleLogo from './apple-logo'; import AmazonLogo from './amazon-logo'; +import AppleLogo from './apple-logo'; +import AsSeenInText from './as-seen-in-text'; +import GoogleLogo from './google-logo'; import MicrosoftLogo from './microsoft-logo'; import SpotifyLogo from './spotify-logo'; -import GoogleLogo from './google-logo'; -import AsSeenInText from './as-seen-in-text'; export { AmazonLogo, diff --git a/client/src/client-only-routes/show-certification.tsx b/client/src/client-only-routes/show-certification.tsx index 72c910305c..3a4b16963b 100644 --- a/client/src/client-only-routes/show-certification.tsx +++ b/client/src/client-only-routes/show-certification.tsx @@ -1,14 +1,19 @@ -import React, { useEffect, useState } from 'react'; -import { bindActionCreators, Dispatch } from 'redux'; -import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; import { Grid, Row, Col, Image, Button } from '@freecodecamp/react-bootstrap'; +import { isEmpty } from 'lodash-es'; +import React, { useEffect, useState } from 'react'; +import { Trans, useTranslation } from 'react-i18next'; +import { connect } from 'react-redux'; +import { bindActionCreators, Dispatch } from 'redux'; +import { createSelector } from 'reselect'; -import ShowProjectLinks from './show-project-links'; +import envData from '../../../config/env.json'; +import { langCodes } from '../../../config/i18n/all-langs'; import FreeCodeCampLogo from '../assets/icons/FreeCodeCamp-logo'; import DonateForm from '../components/Donation/DonateForm'; -import { Trans, useTranslation } from 'react-i18next'; +import { createFlashMessage } from '../components/Flash/redux'; +import { Loader, Spacer } from '../components/helpers'; +import RedirectHome from '../components/redirect-home'; import { showCertSelector, showCertFetchStateSelector, @@ -20,17 +25,12 @@ import { userByNameSelector, fetchProfileForUser } from '../redux'; -import { certMap } from '../resources/cert-and-project-map'; -import { createFlashMessage } from '../components/Flash/redux'; -import standardErrorMessage from '../utils/standard-error-message'; -import reallyWeirdErrorMessage from '../utils/really-weird-error-message'; -import { langCodes } from '../../../config/i18n/all-langs'; -import envData from '../../../config/env.json'; - -import RedirectHome from '../components/redirect-home'; -import { Loader, Spacer } from '../components/helpers'; -import { isEmpty } from 'lodash-es'; import { UserType } from '../redux/prop-types'; +import { certMap } from '../resources/cert-and-project-map'; +import reallyWeirdErrorMessage from '../utils/really-weird-error-message'; +import standardErrorMessage from '../utils/standard-error-message'; + +import ShowProjectLinks from './show-project-links'; const { clientLocale } = envData as { clientLocale: keyof typeof langCodes }; diff --git a/client/src/client-only-routes/show-profile-or-four-oh-four.tsx b/client/src/client-only-routes/show-profile-or-four-oh-four.tsx index 838290f56e..a8cf25b2c5 100644 --- a/client/src/client-only-routes/show-profile-or-four-oh-four.tsx +++ b/client/src/client-only-routes/show-profile-or-four-oh-four.tsx @@ -1,17 +1,17 @@ +import { isEmpty } from 'lodash-es'; import React, { Component } from 'react'; import { connect } from 'react-redux'; -import { isEmpty } from 'lodash-es'; +import { isBrowser } from '../../utils/index'; +import FourOhFour from '../components/FourOhFour'; import Loader from '../components/helpers/loader'; +import Profile from '../components/profile/Profile'; import { userByNameSelector, userProfileFetchStateSelector, fetchProfileForUser, usernameSelector } from '../redux'; -import FourOhFour from '../components/FourOhFour'; -import Profile from '../components/profile/Profile'; -import { isBrowser } from '../../utils/index'; import { UserType } from '../redux/prop-types'; interface IShowProfileOrFourOhFourProps { diff --git a/client/src/client-only-routes/show-project-links.tsx b/client/src/client-only-routes/show-project-links.tsx index 8d345e239e..29ca8ffdef 100644 --- a/client/src/client-only-routes/show-project-links.tsx +++ b/client/src/client-only-routes/show-project-links.tsx @@ -1,19 +1,20 @@ +import { find, first } from 'lodash-es'; import React, { useState } from 'react'; import '../components/layouts/project-links.css'; -import { maybeUrlRE } from '../utils'; -import { Spacer, Link } from '../components/helpers'; -import { - projectMap, - legacyProjectMap -} from '../resources/cert-and-project-map'; -import ProjectModal from '../components/SolutionViewer/ProjectModal'; -import { find, first } from 'lodash-es'; import { Trans, useTranslation } from 'react-i18next'; +import ProjectModal from '../components/SolutionViewer/ProjectModal'; +import { Spacer, Link } from '../components/helpers'; import { ChallengeFileType, CompletedChallenge, UserType } from '../redux/prop-types'; +import { + projectMap, + legacyProjectMap +} from '../resources/cert-and-project-map'; + +import { maybeUrlRE } from '../utils'; interface IShowProjectLinksProps { certName: string; diff --git a/client/src/client-only-routes/show-settings.tsx b/client/src/client-only-routes/show-settings.tsx index b40ce6a3af..76933b23b9 100644 --- a/client/src/client-only-routes/show-settings.tsx +++ b/client/src/client-only-routes/show-settings.tsx @@ -1,30 +1,29 @@ +import { Grid } from '@freecodecamp/react-bootstrap'; import React from 'react'; +import Helmet from 'react-helmet'; +import { useTranslation } from 'react-i18next'; import { connect } from 'react-redux'; import { createSelector } from 'reselect'; -import { Grid } from '@freecodecamp/react-bootstrap'; -import Helmet from 'react-helmet'; import envData from '../../../config/env.json'; +import { createFlashMessage } from '../components/Flash/redux'; +import { Loader, Spacer } from '../components/helpers'; +import Certification from '../components/settings/Certification'; +import About from '../components/settings/about'; +import DangerZone from '../components/settings/danger-zone'; +import Email from '../components/settings/email'; +import Honesty from '../components/settings/honesty'; +import Internet from '../components/settings/internet'; +import Portfolio from '../components/settings/portfolio'; +import Privacy from '../components/settings/privacy'; import { signInLoadingSelector, userSelector, isSignedInSelector, hardGoTo as navigate } from '../redux'; -import { submitNewAbout, updateUserFlag, verifyCert } from '../redux/settings'; -import { createFlashMessage } from '../components/Flash/redux'; -import { useTranslation } from 'react-i18next'; - -import { Loader, Spacer } from '../components/helpers'; -import About from '../components/settings/about'; -import Privacy from '../components/settings/privacy'; -import Email from '../components/settings/email'; -import Internet from '../components/settings/internet'; -import Portfolio from '../components/settings/portfolio'; -import Honesty from '../components/settings/honesty'; -import Certification from '../components/settings/Certification'; import { UserType } from '../redux/prop-types'; -import DangerZone from '../components/settings/danger-zone'; +import { submitNewAbout, updateUserFlag, verifyCert } from '../redux/settings'; const { apiLocation } = envData; diff --git a/client/src/client-only-routes/show-unsubscribed.tsx b/client/src/client-only-routes/show-unsubscribed.tsx index 4b22879012..0ddd3c0248 100644 --- a/client/src/client-only-routes/show-unsubscribed.tsx +++ b/client/src/client-only-routes/show-unsubscribed.tsx @@ -1,11 +1,11 @@ -import React from 'react'; import { Grid, Panel, Button } from '@freecodecamp/react-bootstrap'; +import React from 'react'; import Helmet from 'react-helmet'; import { useTranslation } from 'react-i18next'; import envData from '../../../config/env.json'; -import FullWidthRow from '../components/helpers/full-width-row'; import { Spacer } from '../components/helpers'; +import FullWidthRow from '../components/helpers/full-width-row'; const { apiLocation } = envData; diff --git a/client/src/client-only-routes/show-user.tsx b/client/src/client-only-routes/show-user.tsx index dd40a0c878..ec3bc6b798 100644 --- a/client/src/client-only-routes/show-user.tsx +++ b/client/src/client-only-routes/show-user.tsx @@ -1,6 +1,3 @@ -import React, { Component } from 'react'; -import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; import { Panel, FormControl, @@ -10,18 +7,21 @@ import { Col, Row } from '@freecodecamp/react-bootstrap'; +import React, { Component } from 'react'; import Helmet from 'react-helmet'; import { TFunction, Trans, withTranslation } from 'react-i18next'; +import { connect } from 'react-redux'; +import { createSelector } from 'reselect'; import Login from '../components/Header/components/Login'; +import { Spacer, Loader, FullWidthRow } from '../components/helpers'; import { isSignedInSelector, userFetchStateSelector, userSelector, reportUser } from '../redux'; -import { Spacer, Loader, FullWidthRow } from '../components/helpers'; interface IShowUserProps { email: string; diff --git a/client/src/client/workers/test-evaluator.js b/client/src/client/workers/test-evaluator.js index 13adf0cb60..bc1189cbe7 100644 --- a/client/src/client/workers/test-evaluator.js +++ b/client/src/client/workers/test-evaluator.js @@ -1,10 +1,10 @@ import chai from 'chai'; import '@babel/polyfill'; import { toString as __toString } from 'lodash-es'; -import { format as __format } from '../../utils/format'; import curriculumHelpers, { removeJSComments } from '../../utils/curriculum-helpers'; +import { format as __format } from '../../utils/format'; const __utils = (() => { const MAX_LOGS_SIZE = 64 * 1024; diff --git a/client/src/components/Donation/DonateCompletion.js b/client/src/components/Donation/DonateCompletion.js index 1745109a4b..892b47c82c 100644 --- a/client/src/components/Donation/DonateCompletion.js +++ b/client/src/components/Donation/DonateCompletion.js @@ -1,8 +1,8 @@ -import React from 'react'; -import PropTypes from 'prop-types'; import { Alert, Button } from '@freecodecamp/react-bootstrap'; -import Spinner from 'react-spinkit'; +import PropTypes from 'prop-types'; +import React from 'react'; import { useTranslation } from 'react-i18next'; +import Spinner from 'react-spinkit'; import './Donation.css'; diff --git a/client/src/components/Donation/DonateForm.js b/client/src/components/Donation/DonateForm.js index 295bff1365..61ab4cf107 100644 --- a/client/src/components/Donation/DonateForm.js +++ b/client/src/components/Donation/DonateForm.js @@ -1,8 +1,4 @@ /* eslint-disable no-nested-ternary */ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; import { Col, Row, @@ -11,7 +7,11 @@ import { ToggleButton, ToggleButtonGroup } from '@freecodecamp/react-bootstrap'; +import PropTypes from 'prop-types'; +import React, { Component } from 'react'; import { withTranslation } from 'react-i18next'; +import { connect } from 'react-redux'; +import { createSelector } from 'reselect'; import { amountsConfig, @@ -20,9 +20,6 @@ import { defaultDonation, modalDefaultDonation } from '../../../../config/donation-settings'; -import Spacer from '../helpers/spacer'; -import PaypalButton from './PaypalButton'; -import DonateCompletion from './DonateCompletion'; import { isSignedInSelector, signInLoadingSelector, @@ -32,6 +29,9 @@ import { defaultDonationFormState, userSelector } from '../../redux'; +import Spacer from '../helpers/spacer'; +import DonateCompletion from './DonateCompletion'; +import PaypalButton from './PaypalButton'; import './Donation.css'; diff --git a/client/src/components/Donation/DonationModal.js b/client/src/components/Donation/DonationModal.js index 12bbbbfd1b..558a84e1cf 100644 --- a/client/src/components/Donation/DonationModal.js +++ b/client/src/components/Donation/DonationModal.js @@ -1,25 +1,24 @@ /* eslint-disable max-len */ -import React, { useEffect } from 'react'; -import PropTypes from 'prop-types'; -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; import { Modal, Button, Col, Row } from '@freecodecamp/react-bootstrap'; -import { Spacer } from '../helpers'; -import Heart from '../../assets/icons/heart'; -import Cup from '../../assets/icons/cup'; -import DonateForm from './DonateForm'; -import { modalDefaultDonation } from '../../../../config/donation-settings'; +import PropTypes from 'prop-types'; +import React, { useEffect } from 'react'; import { useTranslation } from 'react-i18next'; +import { connect } from 'react-redux'; import { goToAnchor } from 'react-scrollable-anchor'; -import { isLocationSuperBlock } from '../../utils/path-parsers'; - +import { bindActionCreators } from 'redux'; +import { createSelector } from 'reselect'; +import { modalDefaultDonation } from '../../../../config/donation-settings'; +import Cup from '../../assets/icons/cup'; +import Heart from '../../assets/icons/heart'; import { closeDonationModal, isDonationModalOpenSelector, recentlyClaimedBlockSelector, executeGA } from '../../redux'; +import { isLocationSuperBlock } from '../../utils/path-parsers'; +import { Spacer } from '../helpers'; +import DonateForm from './DonateForm'; import './Donation.css'; diff --git a/client/src/components/Donation/PayPalButtonScriptLoader.js b/client/src/components/Donation/PayPalButtonScriptLoader.js index 45a033fa99..ac73a54a68 100644 --- a/client/src/components/Donation/PayPalButtonScriptLoader.js +++ b/client/src/components/Donation/PayPalButtonScriptLoader.js @@ -1,11 +1,11 @@ /* eslint-disable camelcase */ +import PropTypes from 'prop-types'; import React, { Component } from 'react'; import ReactDOM from 'react-dom'; -import PropTypes from 'prop-types'; -import { scriptLoader, scriptRemover } from '../../utils/script-loaders'; import { Loader } from '../../components/helpers'; +import { scriptLoader, scriptRemover } from '../../utils/script-loaders'; export class PayPalButtonScriptLoader extends Component { state = { isSdkLoaded: window.paypal && true, isSubscription: true }; diff --git a/client/src/components/Donation/PaypalButton.js b/client/src/components/Donation/PaypalButton.js index c2b15ca9ff..1aacb89bcd 100644 --- a/client/src/components/Donation/PaypalButton.js +++ b/client/src/components/Donation/PaypalButton.js @@ -1,18 +1,17 @@ /* eslint-disable camelcase */ -import React, { Component } from 'react'; import PropTypes from 'prop-types'; +import React, { Component } from 'react'; +import { withTranslation } from 'react-i18next'; import { connect } from 'react-redux'; import { createSelector } from 'reselect'; -import PayPalButtonScriptLoader from './PayPalButtonScriptLoader'; -import { withTranslation } from 'react-i18next'; - -import envData from '../../../../config/env.json'; import { paypalConfigurator, paypalConfigTypes } from '../../../../config/donation-settings'; +import envData from '../../../../config/env.json'; import { signInLoadingSelector, userSelector } from '../../redux'; +import PayPalButtonScriptLoader from './PayPalButtonScriptLoader'; const { paypalClientId, deploymentEnv } = envData; export class PaypalButton extends Component { diff --git a/client/src/components/Flash/index.tsx b/client/src/components/Flash/index.tsx index 01f7423541..01f649cdbe 100644 --- a/client/src/components/Flash/index.tsx +++ b/client/src/components/Flash/index.tsx @@ -1,8 +1,8 @@ -import React, { useState, useEffect } from 'react'; -import PropTypes from 'prop-types'; import { Alert } from '@freecodecamp/react-bootstrap'; -import { TransitionGroup, CSSTransition } from 'react-transition-group'; +import PropTypes from 'prop-types'; +import React, { useState, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; +import { TransitionGroup, CSSTransition } from 'react-transition-group'; import './flash.css'; diff --git a/client/src/components/Flash/redux/index.ts b/client/src/components/Flash/redux/index.ts index e3c2f2fcf0..46059692a8 100644 --- a/client/src/components/Flash/redux/index.ts +++ b/client/src/components/Flash/redux/index.ts @@ -1,5 +1,5 @@ -import { createAction, handleActions } from 'redux-actions'; import { nanoid } from 'nanoid'; +import { createAction, handleActions } from 'redux-actions'; import { createTypes } from '../../../utils/create-types'; diff --git a/client/src/components/FourOhFour/index.tsx b/client/src/components/FourOhFour/index.tsx index dbda4bb868..34a8abc9a1 100644 --- a/client/src/components/FourOhFour/index.tsx +++ b/client/src/components/FourOhFour/index.tsx @@ -1,11 +1,11 @@ +import { Link } from 'gatsby'; import React from 'react'; import Helmet from 'react-helmet'; -import { Spacer } from '../helpers'; -import { Link } from 'gatsby'; import { useTranslation } from 'react-i18next'; import notFoundLogo from '../../assets/images/freeCodeCamp-404.svg'; import { randomQuote } from '../../utils/get-words'; +import { Spacer } from '../helpers'; import './404.css'; diff --git a/client/src/components/Header/Header.test.js b/client/src/components/Header/Header.test.js index 1e793aefbf..748af6494e 100644 --- a/client/src/components/Header/Header.test.js +++ b/client/src/components/Header/Header.test.js @@ -1,13 +1,12 @@ import React from 'react'; +import { useTranslation } from 'react-i18next'; import { create } from 'react-test-renderer'; import ShallowRenderer from 'react-test-renderer/shallow'; -import { UniversalNav } from './components/universal-nav'; -import { NavLinks } from './components/nav-links'; -import AuthOrProfile from './components/auth-or-profile'; - import envData from '../../../../config/env.json'; -import { useTranslation } from 'react-i18next'; +import AuthOrProfile from './components/auth-or-profile'; +import { NavLinks } from './components/nav-links'; +import { UniversalNav } from './components/universal-nav'; const { apiLocation } = envData; diff --git a/client/src/components/Header/components/Login.tsx b/client/src/components/Header/components/Login.tsx index 8b166d7c6d..2bf96c08e2 100644 --- a/client/src/components/Header/components/Login.tsx +++ b/client/src/components/Header/components/Login.tsx @@ -1,11 +1,11 @@ +import { Button } from '@freecodecamp/react-bootstrap'; import React from 'react'; +import { useTranslation } from 'react-i18next'; import { connect } from 'react-redux'; import { createSelector } from 'reselect'; -import { Button } from '@freecodecamp/react-bootstrap'; -import { useTranslation } from 'react-i18next'; -import { isSignedInSelector } from '../../../redux'; import envData from '../../../../../config/env.json'; +import { isSignedInSelector } from '../../../redux'; import './login.css'; diff --git a/client/src/components/Header/components/auth-or-profile.tsx b/client/src/components/Header/components/auth-or-profile.tsx index b228a3806a..22334f5184 100644 --- a/client/src/components/Header/components/auth-or-profile.tsx +++ b/client/src/components/Header/components/auth-or-profile.tsx @@ -3,8 +3,8 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ // @ts-nocheck import React from 'react'; -import { Link, AvatarRenderer } from '../../helpers'; import { useTranslation } from 'react-i18next'; +import { Link, AvatarRenderer } from '../../helpers'; import Login from './Login'; export interface AuthOrProfileProps { diff --git a/client/src/components/Header/components/nav-links.tsx b/client/src/components/Header/components/nav-links.tsx index d6c99a83c3..c7281d41b3 100644 --- a/client/src/components/Header/components/nav-links.tsx +++ b/client/src/components/Header/components/nav-links.tsx @@ -8,10 +8,6 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/restrict-template-expressions */ // @ts-nocheck -import React, { Component, Fragment } from 'react'; -import { connect } from 'react-redux'; -import { TFunction, withTranslation } from 'react-i18next'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faCheck, faCheckSquare, @@ -19,15 +15,19 @@ import { faSquare, faExternalLinkAlt } from '@fortawesome/free-solid-svg-icons'; -import { Link } from '../../helpers'; -import { updateUserFlag } from '../../../redux/settings'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import React, { Component, Fragment } from 'react'; +import { TFunction, withTranslation } from 'react-i18next'; +import { connect } from 'react-redux'; import envData from '../../../../../config/env.json'; -import createLanguageRedirect from '../../create-language-redirect'; import { availableLangs, i18nextCodes, langDisplayNames } from '../../../../../config/i18n/all-langs'; +import { updateUserFlag } from '../../../redux/settings'; +import createLanguageRedirect from '../../create-language-redirect'; +import { Link } from '../../helpers'; const { clientLocale, radioLocation, apiLocation } = envData; diff --git a/client/src/components/Header/components/universal-nav.tsx b/client/src/components/Header/components/universal-nav.tsx index 701fda03fc..9da7a223f1 100644 --- a/client/src/components/Header/components/universal-nav.tsx +++ b/client/src/components/Header/components/universal-nav.tsx @@ -3,14 +3,14 @@ /* eslint-disable @typescript-eslint/ban-ts-comment */ /* eslint-disable react/prop-types */ // @ts-nocheck +import Loadable from '@loadable/component'; import React, { Ref } from 'react'; +import { isLanding } from '../../../utils/path-parsers'; import { Link, SkeletonSprite } from '../../helpers'; -import NavLogo from './nav-logo'; import MenuButton from './menu-button'; import NavLinks from './nav-links'; +import NavLogo from './nav-logo'; import './universal-nav.css'; -import { isLanding } from '../../../utils/path-parsers'; -import Loadable from '@loadable/component'; const SearchBar = Loadable(() => import('../../search/searchBar/search-bar')); const SearchBarOptimized = Loadable( diff --git a/client/src/components/Header/components/user-state.tsx b/client/src/components/Header/components/user-state.tsx index 72981789d0..54e6d3339a 100644 --- a/client/src/components/Header/components/user-state.tsx +++ b/client/src/components/Header/components/user-state.tsx @@ -2,12 +2,12 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ -import React from 'react'; -import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; import { Link } from 'gatsby'; -import Spinner from 'react-spinkit'; +import React from 'react'; import { useTranslation } from 'react-i18next'; +import { connect } from 'react-redux'; +import Spinner from 'react-spinkit'; +import { createSelector } from 'reselect'; import { isSignedInSelector, userFetchStateSelector } from '../../../redux'; import Login from './Login'; diff --git a/client/src/components/Intro/Intro.test.js b/client/src/components/Intro/Intro.test.js index e638b653b8..e332aaaa3e 100644 --- a/client/src/components/Intro/Intro.test.js +++ b/client/src/components/Intro/Intro.test.js @@ -1,6 +1,6 @@ import React from 'react'; -import renderer from 'react-test-renderer'; import { Provider } from 'react-redux'; +import renderer from 'react-test-renderer'; import { createStore } from '../../redux/createStore'; import Intro from './'; diff --git a/client/src/components/Intro/components/IntroDescription.js b/client/src/components/Intro/components/IntroDescription.js index 04af9bb4c7..0e7586d52a 100644 --- a/client/src/components/Intro/components/IntroDescription.js +++ b/client/src/components/Intro/components/IntroDescription.js @@ -1,7 +1,7 @@ import React from 'react'; -import { Link, Spacer } from '../../helpers'; -import envData from '../../../../../config/env.json'; import { Trans, useTranslation } from 'react-i18next'; +import envData from '../../../../../config/env.json'; +import { Link, Spacer } from '../../helpers'; import '../intro.css'; diff --git a/client/src/components/Intro/index.js b/client/src/components/Intro/index.js index 3e02b7ae7d..f5ffb701d6 100644 --- a/client/src/components/Intro/index.js +++ b/client/src/components/Intro/index.js @@ -1,13 +1,13 @@ -import React from 'react'; import PropTypes from 'prop-types'; -import { Link, Spacer, Loader, FullWidthRow } from '../helpers'; +import React from 'react'; +import { Trans, useTranslation } from 'react-i18next'; import { randomQuote } from '../../utils/get-words'; +import Login from '../Header/components/Login'; +import { Link, Spacer, Loader, FullWidthRow } from '../helpers'; import CurrentChallengeLink from '../helpers/current-challenge-link'; import IntroDescription from './components/IntroDescription'; -import { Trans, useTranslation } from 'react-i18next'; import './intro.css'; -import Login from '../Header/components/Login'; const propTypes = { complete: PropTypes.bool, diff --git a/client/src/components/Map/Map.test.js b/client/src/components/Map/Map.test.js index 37e5c5bc33..947ecde5bd 100644 --- a/client/src/components/Map/Map.test.js +++ b/client/src/components/Map/Map.test.js @@ -1,9 +1,9 @@ -import React from 'react'; -import { useStaticQuery } from 'gatsby'; import { render } from '@testing-library/react'; +import { useStaticQuery } from 'gatsby'; +import React from 'react'; -import { Map } from './'; import mockChallengeNodes from '../../__mocks__/challenge-nodes'; +import { Map } from './'; beforeEach(() => { useStaticQuery.mockImplementationOnce(() => ({ diff --git a/client/src/components/Map/index.js b/client/src/components/Map/index.js index cfdf208240..6b7b761ffd 100644 --- a/client/src/components/Map/index.js +++ b/client/src/components/Map/index.js @@ -1,14 +1,15 @@ -import React from 'react'; -import PropTypes from 'prop-types'; import { graphql, useStaticQuery } from 'gatsby'; import i18next from 'i18next'; +import PropTypes from 'prop-types'; +import React from 'react'; + +import envData from '../../../../config/env.json'; +import { isAuditedCert } from '../../../../utils/is-audited'; import { generateIconComponent } from '../../assets/icons'; -import { Link, Spacer } from '../helpers'; import LinkButton from '../../assets/icons/link-button'; +import { Link, Spacer } from '../helpers'; import './map.css'; -import { isAuditedCert } from '../../../../utils/is-audited'; -import envData from '../../../../config/env.json'; const { curriculumLocale } = envData; diff --git a/client/src/components/SolutionViewer/ProjectModal.js b/client/src/components/SolutionViewer/ProjectModal.js index 77836f9423..791b61d8a1 100644 --- a/client/src/components/SolutionViewer/ProjectModal.js +++ b/client/src/components/SolutionViewer/ProjectModal.js @@ -1,8 +1,8 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import SolutionViewer from './SolutionViewer'; import { Button, Modal } from '@freecodecamp/react-bootstrap'; +import PropTypes from 'prop-types'; +import React from 'react'; import { useTranslation } from 'react-i18next'; +import SolutionViewer from './SolutionViewer'; const propTypes = { files: PropTypes.arrayOf( diff --git a/client/src/components/SolutionViewer/SolutionViewer.js b/client/src/components/SolutionViewer/SolutionViewer.js index 0e3ce7955d..c380cde3c6 100644 --- a/client/src/components/SolutionViewer/SolutionViewer.js +++ b/client/src/components/SolutionViewer/SolutionViewer.js @@ -1,7 +1,7 @@ -import React from 'react'; -import PropTypes from 'prop-types'; import { Panel } from '@freecodecamp/react-bootstrap'; import Prism from 'prismjs'; +import PropTypes from 'prop-types'; +import React from 'react'; const prismLang = { css: 'css', diff --git a/client/src/components/app-mount-notifier.test.tsx b/client/src/components/app-mount-notifier.test.tsx index 240a42dd71..d0a43f2f27 100644 --- a/client/src/components/app-mount-notifier.test.tsx +++ b/client/src/components/app-mount-notifier.test.tsx @@ -1,12 +1,12 @@ +import { render, waitFor } from '@testing-library/react'; import React from 'react'; import { I18nextProvider } from 'react-i18next'; -import { render, waitFor } from '@testing-library/react'; import { Provider } from 'react-redux'; import { i18nextCodes } from '../../../config/i18n/all-langs'; -import AppMountNotifier from './app-mount-notifier'; -import { createStore } from '../redux/createStore'; import i18nTestConfig from '../../i18n/configForTests'; +import { createStore } from '../redux/createStore'; +import AppMountNotifier from './app-mount-notifier'; jest.mock('react-ga'); jest.unmock('react-i18next'); diff --git a/client/src/components/app-mount-notifier.tsx b/client/src/components/app-mount-notifier.tsx index f3d6830fec..1b15347703 100644 --- a/client/src/components/app-mount-notifier.tsx +++ b/client/src/components/app-mount-notifier.tsx @@ -1,8 +1,8 @@ import React, { useEffect } from 'react'; -import { bindActionCreators, Dispatch } from 'redux'; -import { connect } from 'react-redux'; import { Helmet } from 'react-helmet'; import { useTranslation } from 'react-i18next'; +import { connect } from 'react-redux'; +import { bindActionCreators, Dispatch } from 'redux'; import { appMount } from '../redux'; diff --git a/client/src/components/formHelpers/Form.js b/client/src/components/formHelpers/Form.js index e0115dba1b..4910875baa 100644 --- a/client/src/components/formHelpers/Form.js +++ b/client/src/components/formHelpers/Form.js @@ -1,5 +1,5 @@ -import React from 'react'; import PropTypes from 'prop-types'; +import React from 'react'; import { Form } from 'react-final-form'; import { diff --git a/client/src/components/formHelpers/Form.test.js b/client/src/components/formHelpers/Form.test.js index d61660efee..d091d70c5e 100644 --- a/client/src/components/formHelpers/Form.test.js +++ b/client/src/components/formHelpers/Form.test.js @@ -1,5 +1,5 @@ -import React from 'react'; import { render, fireEvent, screen } from '@testing-library/react'; +import React from 'react'; import Form from './Form'; diff --git a/client/src/components/formHelpers/FormFields.js b/client/src/components/formHelpers/FormFields.js index 1864462d12..4a4ef0921a 100644 --- a/client/src/components/formHelpers/FormFields.js +++ b/client/src/components/formHelpers/FormFields.js @@ -1,7 +1,3 @@ -import React from 'react'; -import { kebabCase } from 'lodash-es'; -import normalizeUrl from 'normalize-url'; -import PropTypes from 'prop-types'; import { Alert, Col, @@ -10,6 +6,10 @@ import { FormGroup, HelpBlock } from '@freecodecamp/react-bootstrap'; +import { kebabCase } from 'lodash-es'; +import normalizeUrl from 'normalize-url'; +import PropTypes from 'prop-types'; +import React from 'react'; import { Field } from 'react-final-form'; import { editorValidator, diff --git a/client/src/components/formHelpers/block-save-button.test.tsx b/client/src/components/formHelpers/block-save-button.test.tsx index 7324bc006c..e2193d5b50 100644 --- a/client/src/components/formHelpers/block-save-button.test.tsx +++ b/client/src/components/formHelpers/block-save-button.test.tsx @@ -1,5 +1,5 @@ -import React from 'react'; import { render, screen } from '@testing-library/react'; +import React from 'react'; import BlockSaveButton from './block-save-button'; diff --git a/client/src/components/formHelpers/block-save-button.tsx b/client/src/components/formHelpers/block-save-button.tsx index 86a2c622cc..1a57838e3c 100644 --- a/client/src/components/formHelpers/block-save-button.tsx +++ b/client/src/components/formHelpers/block-save-button.tsx @@ -1,5 +1,5 @@ -import React from 'react'; import { Button } from '@freecodecamp/react-bootstrap'; +import React from 'react'; import { useTranslation } from 'react-i18next'; function BlockSaveButton(props?: Record): JSX.Element { diff --git a/client/src/components/formHelpers/block-save-wrapper.test.tsx b/client/src/components/formHelpers/block-save-wrapper.test.tsx index 93cd59dce5..25e9acff3b 100644 --- a/client/src/components/formHelpers/block-save-wrapper.test.tsx +++ b/client/src/components/formHelpers/block-save-wrapper.test.tsx @@ -1,5 +1,5 @@ -import React from 'react'; import { render } from '@testing-library/react'; +import React from 'react'; import BlockSaveWrapper from './block-save-wrapper'; diff --git a/client/src/components/helpers/avatar-renderer.tsx b/client/src/components/helpers/avatar-renderer.tsx index fb847c83f9..362390d3fd 100644 --- a/client/src/components/helpers/avatar-renderer.tsx +++ b/client/src/components/helpers/avatar-renderer.tsx @@ -1,9 +1,9 @@ -import React, { useState, useEffect } from 'react'; import { Image } from '@freecodecamp/react-bootstrap'; -import DefaultAvatar from '../../assets/icons/default-avatar'; -import { defaultUserImage } from '../../../../config/misc'; -import { borderColorPicker } from '.'; +import React, { useState, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; +import { defaultUserImage } from '../../../../config/misc'; +import DefaultAvatar from '../../assets/icons/default-avatar'; +import { borderColorPicker } from '.'; interface AvatarRendererProps { isDonating?: boolean; diff --git a/client/src/components/helpers/form/block-save-button.tsx b/client/src/components/helpers/form/block-save-button.tsx index 186d0dfbcc..fc26e84fbd 100644 --- a/client/src/components/helpers/form/block-save-button.tsx +++ b/client/src/components/helpers/form/block-save-button.tsx @@ -1,5 +1,5 @@ -import React from 'react'; import { Button } from '@freecodecamp/react-bootstrap'; +import React from 'react'; import { useTranslation } from 'react-i18next'; function BlockSaveButton({ diff --git a/client/src/components/helpers/full-width-row.tsx b/client/src/components/helpers/full-width-row.tsx index dfb3d4de31..798ced0a87 100644 --- a/client/src/components/helpers/full-width-row.tsx +++ b/client/src/components/helpers/full-width-row.tsx @@ -1,5 +1,5 @@ -import React from 'react'; import { Row, Col } from '@freecodecamp/react-bootstrap'; +import React from 'react'; function FullWidthRow({ children, diff --git a/client/src/components/helpers/link.tsx b/client/src/components/helpers/link.tsx index ebb39fa196..bc9dd2d672 100644 --- a/client/src/components/helpers/link.tsx +++ b/client/src/components/helpers/link.tsx @@ -1,5 +1,5 @@ -import React from 'react'; import { Link as GatsbyLink } from 'gatsby'; +import React from 'react'; interface LinkProps { children?: React.ReactNode; diff --git a/client/src/components/helpers/loader.test.tsx b/client/src/components/helpers/loader.test.tsx index 78425d80a6..8708115747 100644 --- a/client/src/components/helpers/loader.test.tsx +++ b/client/src/components/helpers/loader.test.tsx @@ -1,5 +1,5 @@ -import React from 'react'; import { render, cleanup, screen } from '@testing-library/react'; +import React from 'react'; import Loader from './loader'; diff --git a/client/src/components/helpers/slim-width-row.tsx b/client/src/components/helpers/slim-width-row.tsx index 983b6fe10c..dc8ceb67ef 100644 --- a/client/src/components/helpers/slim-width-row.tsx +++ b/client/src/components/helpers/slim-width-row.tsx @@ -1,5 +1,5 @@ -import React from 'react'; import { Row, Col } from '@freecodecamp/react-bootstrap'; +import React from 'react'; interface SlimWidthRowProps { children: JSX.ElementChildrenAttribute; diff --git a/client/src/components/helpers/toggle-button.tsx b/client/src/components/helpers/toggle-button.tsx index 7657175490..89e1892f2b 100644 --- a/client/src/components/helpers/toggle-button.tsx +++ b/client/src/components/helpers/toggle-button.tsx @@ -1,12 +1,12 @@ -import React from 'react'; import { ToggleButtonGroup as BSBG, ToggleButton as TB } from '@freecodecamp/react-bootstrap'; +import React from 'react'; import './toggle-button.css'; -import ToggleCheck from '../../assets/icons/toggle-check'; import Spacer from '../../assets/icons/spacer'; +import ToggleCheck from '../../assets/icons/toggle-check'; interface ButtonProps { name: string; diff --git a/client/src/components/landing/Landing.test.js b/client/src/components/landing/Landing.test.js index 9e4e5a26b1..c34a120c8a 100644 --- a/client/src/components/landing/Landing.test.js +++ b/client/src/components/landing/Landing.test.js @@ -1,8 +1,8 @@ import React from 'react'; import ShallowRenderer from 'react-test-renderer/shallow'; -import IndexPage from '../../pages'; import mockChallengeNodes from '../../__mocks__/challenge-nodes'; +import IndexPage from '../../pages'; jest.mock('../../analytics'); diff --git a/client/src/components/landing/components/AsSeenIn.js b/client/src/components/landing/components/AsSeenIn.js index 3461b5ac49..9d1d93599b 100644 --- a/client/src/components/landing/components/AsSeenIn.js +++ b/client/src/components/landing/components/AsSeenIn.js @@ -1,7 +1,7 @@ -import React from 'react'; import { Col, Row } from '@freecodecamp/react-bootstrap'; -import { AsSeenInText } from '../../../assets/images/components'; +import React from 'react'; import { useTranslation } from 'react-i18next'; +import { AsSeenInText } from '../../../assets/images/components'; const AsSeenIn = () => { const { t } = useTranslation(); diff --git a/client/src/components/landing/components/BigCallToAction.js b/client/src/components/landing/components/BigCallToAction.js index 62dff19f1e..252953cab8 100644 --- a/client/src/components/landing/components/BigCallToAction.js +++ b/client/src/components/landing/components/BigCallToAction.js @@ -1,7 +1,7 @@ -import React from 'react'; import PropTypes from 'prop-types'; -import Login from '../../Header/components/Login'; +import React from 'react'; import { useTranslation } from 'react-i18next'; +import Login from '../../Header/components/Login'; const propTypes = { page: PropTypes.string diff --git a/client/src/components/landing/components/CampersImage.js b/client/src/components/landing/components/CampersImage.js index 20017619dc..cda1a77ef1 100644 --- a/client/src/components/landing/components/CampersImage.js +++ b/client/src/components/landing/components/CampersImage.js @@ -1,9 +1,9 @@ -import React from 'react'; import PropTypes from 'prop-types'; -import Media from 'react-responsive'; -import { Spacer, ImageLoader } from '../../helpers'; -import wideImg from '../../../assets/images/landing/wide-image.png'; +import React from 'react'; import { useTranslation } from 'react-i18next'; +import Media from 'react-responsive'; +import wideImg from '../../../assets/images/landing/wide-image.png'; +import { Spacer, ImageLoader } from '../../helpers'; const propTypes = { page: PropTypes.string diff --git a/client/src/components/landing/components/Certifications.js b/client/src/components/landing/components/Certifications.js index 103a3e9aa1..d1af6cb536 100644 --- a/client/src/components/landing/components/Certifications.js +++ b/client/src/components/landing/components/Certifications.js @@ -1,10 +1,10 @@ -import React from 'react'; -import PropTypes from 'prop-types'; import { Col, Row } from '@freecodecamp/react-bootstrap'; -import { Spacer } from '../../helpers'; -import BigCallToAction from './BigCallToAction'; +import PropTypes from 'prop-types'; +import React from 'react'; import { useTranslation } from 'react-i18next'; import Map from '../../Map/index'; +import { Spacer } from '../../helpers'; +import BigCallToAction from './BigCallToAction'; const propTypes = { page: PropTypes.string diff --git a/client/src/components/landing/components/LandingTop.js b/client/src/components/landing/components/LandingTop.js index 9e94d40b1a..a7be1cab31 100644 --- a/client/src/components/landing/components/LandingTop.js +++ b/client/src/components/landing/components/LandingTop.js @@ -1,7 +1,7 @@ -import React from 'react'; -import PropTypes from 'prop-types'; import { Col, Row } from '@freecodecamp/react-bootstrap'; -import { Spacer } from '../../helpers'; +import PropTypes from 'prop-types'; +import React from 'react'; +import { useTranslation } from 'react-i18next'; import { AmazonLogo, AppleLogo, @@ -9,9 +9,9 @@ import { SpotifyLogo, GoogleLogo } from '../../../assets/images/components'; -import CampersImage from './CampersImage'; +import { Spacer } from '../../helpers'; import BigCallToAction from './BigCallToAction'; -import { useTranslation } from 'react-i18next'; +import CampersImage from './CampersImage'; const propTypes = { page: PropTypes.string diff --git a/client/src/components/landing/components/Testimonials.js b/client/src/components/landing/components/Testimonials.js index a4fc310de5..ff6e0fec5f 100644 --- a/client/src/components/landing/components/Testimonials.js +++ b/client/src/components/landing/components/Testimonials.js @@ -1,11 +1,11 @@ -import React from 'react'; import PropTypes from 'prop-types'; +import React from 'react'; -import { ImageLoader } from '../../helpers'; -import shawnImg from '../../../assets/images/landing/Shawn.png'; -import sarahImg from '../../../assets/images/landing/Sarah.png'; -import emmaImg from '../../../assets/images/landing/Emma.png'; import { Trans, useTranslation } from 'react-i18next'; +import emmaImg from '../../../assets/images/landing/Emma.png'; +import sarahImg from '../../../assets/images/landing/Sarah.png'; +import shawnImg from '../../../assets/images/landing/Shawn.png'; +import { ImageLoader } from '../../helpers'; const propTypes = { page: PropTypes.string diff --git a/client/src/components/landing/index.js b/client/src/components/landing/index.js index e0a6c6f015..aff37de6ae 100644 --- a/client/src/components/landing/index.js +++ b/client/src/components/landing/index.js @@ -1,13 +1,13 @@ -import React from 'react'; import { Grid } from '@freecodecamp/react-bootstrap'; -import Helmet from 'react-helmet'; import PropTypes from 'prop-types'; +import React from 'react'; +import Helmet from 'react-helmet'; import { useTranslation } from 'react-i18next'; -import Testimonials from './components/Testimonials'; -import LandingTop from './components/LandingTop'; -import Certifications from './components/Certifications'; import AsSeenIn from './components/AsSeenIn'; +import Certifications from './components/Certifications'; +import LandingTop from './components/LandingTop'; +import Testimonials from './components/Testimonials'; import './landing.css'; diff --git a/client/src/components/layouts/Certification.js b/client/src/components/layouts/Certification.js index cc02f20593..a8350f4527 100644 --- a/client/src/components/layouts/Certification.js +++ b/client/src/components/layouts/Certification.js @@ -1,10 +1,10 @@ -import React, { Component } from 'react'; import PropTypes from 'prop-types'; +import React, { Component } from 'react'; +import Helmet from 'react-helmet'; import { connect } from 'react-redux'; -import { fetchUser, isSignedInSelector, executeGA } from '../../redux'; import { createSelector } from 'reselect'; -import Helmet from 'react-helmet'; +import { fetchUser, isSignedInSelector, executeGA } from '../../redux'; const mapStateToProps = createSelector(isSignedInSelector, isSignedIn => ({ isSignedIn diff --git a/client/src/components/layouts/Default.js b/client/src/components/layouts/Default.js index d0795763bb..d0fb3363ec 100644 --- a/client/src/components/layouts/Default.js +++ b/client/src/components/layouts/Default.js @@ -1,12 +1,19 @@ -import React, { Component } from 'react'; +import fontawesome from '@fortawesome/fontawesome'; import PropTypes from 'prop-types'; +import React, { Component } from 'react'; +import Helmet from 'react-helmet'; +import { withTranslation } from 'react-i18next'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import { createSelector } from 'reselect'; -import Helmet from 'react-helmet'; -import fontawesome from '@fortawesome/fontawesome'; -import { withTranslation } from 'react-i18next'; +import latoBoldURL from '../../../static/fonts/lato/Lato-Bold.woff'; +import latoLightURL from '../../../static/fonts/lato/Lato-Light.woff'; +import latoRegularURL from '../../../static/fonts/lato/Lato-Regular.woff'; +import robotoBoldURL from '../../../static/fonts/roboto-mono/RobotoMono-Bold.woff'; +import robotoItalicURL from '../../../static/fonts/roboto-mono/RobotoMono-Italic.woff'; +import robotoRegularURL from '../../../static/fonts/roboto-mono/RobotoMono-Regular.woff'; +import { isBrowser } from '../../../utils'; import { fetchUser, isSignedInSelector, @@ -17,24 +24,16 @@ import { usernameSelector, executeGA } from '../../redux'; +import Flash from '../Flash'; import { flashMessageSelector, removeFlashMessage } from '../Flash/redux'; -import { isBrowser } from '../../../utils'; - -import OfflineWarning from '../OfflineWarning'; -import Flash from '../Flash'; -import Header from '../Header'; import Footer from '../Footer'; +import Header from '../Header'; +import OfflineWarning from '../OfflineWarning'; // preload common fonts -import latoLightURL from '../../../static/fonts/lato/Lato-Light.woff'; -import latoRegularURL from '../../../static/fonts/lato/Lato-Regular.woff'; -import latoBoldURL from '../../../static/fonts/lato/Lato-Bold.woff'; // eslint-disable-next-line max-len -import robotoRegularURL from '../../../static/fonts/roboto-mono/RobotoMono-Regular.woff'; // eslint-disable-next-line max-len -import robotoBoldURL from '../../../static/fonts/roboto-mono/RobotoMono-Bold.woff'; // eslint-disable-next-line max-len -import robotoItalicURL from '../../../static/fonts/roboto-mono/RobotoMono-Italic.woff'; import './fonts.css'; import './global.css'; diff --git a/client/src/components/layouts/learn.tsx b/client/src/components/layouts/learn.tsx index 4c64dcdc72..9df35f09a7 100644 --- a/client/src/components/layouts/learn.tsx +++ b/client/src/components/layouts/learn.tsx @@ -1,7 +1,7 @@ import React, { Component } from 'react'; -import { createSelector } from 'reselect'; -import { connect } from 'react-redux'; import { Helmet } from 'react-helmet'; +import { connect } from 'react-redux'; +import { createSelector } from 'reselect'; import { Loader } from '../../components/helpers'; import { userSelector, @@ -9,8 +9,8 @@ import { isSignedInSelector, tryToShowDonationModal } from '../../redux'; -import createRedirect from '../create-redirect'; import DonateModal from '../Donation/DonationModal'; +import createRedirect from '../create-redirect'; import './prism.css'; import './prism-night.css'; diff --git a/client/src/components/profile/Profile.test.tsx b/client/src/components/profile/Profile.test.tsx index 224cfd751f..2c148bcd79 100644 --- a/client/src/components/profile/Profile.test.tsx +++ b/client/src/components/profile/Profile.test.tsx @@ -1,5 +1,5 @@ -import React from 'react'; import { render, screen } from '@testing-library/react'; +import React from 'react'; import Profile from './Profile'; diff --git a/client/src/components/profile/Profile.tsx b/client/src/components/profile/Profile.tsx index c712d283ac..44aa7bb04b 100644 --- a/client/src/components/profile/Profile.tsx +++ b/client/src/components/profile/Profile.tsx @@ -1,12 +1,12 @@ -import React from 'react'; import { Grid, Row } from '@freecodecamp/react-bootstrap'; +import React from 'react'; import Helmet from 'react-helmet'; import { TFunction, useTranslation } from 'react-i18next'; import { CurrentChallengeLink, FullWidthRow, Link, Spacer } from '../helpers'; import Camper from './components/Camper'; -import HeatMap from './components/HeatMap'; import Certifications from './components/Certifications'; +import HeatMap from './components/HeatMap'; import Portfolio from './components/Portfolio'; import Timeline from './components/TimeLine'; diff --git a/client/src/components/profile/components/Camper.tsx b/client/src/components/profile/components/Camper.tsx index 131dec4511..ae6f4ae31f 100644 --- a/client/src/components/profile/components/Camper.tsx +++ b/client/src/components/profile/components/Camper.tsx @@ -1,22 +1,21 @@ -import React from 'react'; -import { Col, Row } from '@freecodecamp/react-bootstrap'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faAward, faCalendar, faHeart } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { Col, Row } from '@freecodecamp/react-bootstrap'; +import React from 'react'; import { TFunction, useTranslation } from 'react-i18next'; +import envData from '../../../../../config/env.json'; +import { langCodes } from '../../../../../config/i18n/all-langs'; import { AvatarRenderer } from '../../helpers'; -import SocialIcons from './SocialIcons'; import Link from '../../helpers/link'; +import SocialIcons from './SocialIcons'; import './camper.css'; -import { langCodes } from '../../../../../config/i18n/all-langs'; -import envData from '../../../../../config/env.json'; - const { clientLocale } = envData; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore diff --git a/client/src/components/profile/components/Certifications.tsx b/client/src/components/profile/components/Certifications.tsx index 319ff68a8b..70ee6a995a 100644 --- a/client/src/components/profile/components/Certifications.tsx +++ b/client/src/components/profile/components/Certifications.tsx @@ -1,9 +1,9 @@ -import React, { Fragment } from 'react'; -import { curry } from 'lodash-es'; -import { createSelector } from 'reselect'; -import { connect } from 'react-redux'; import { Col, Row } from '@freecodecamp/react-bootstrap'; +import { curry } from 'lodash-es'; +import React, { Fragment } from 'react'; import { useTranslation } from 'react-i18next'; +import { connect } from 'react-redux'; +import { createSelector } from 'reselect'; import { certificatesByNameSelector } from '../../../redux'; import { ButtonSpacer, FullWidthRow, Link, Spacer } from '../../helpers'; diff --git a/client/src/components/profile/components/HeatMap.test.tsx b/client/src/components/profile/components/HeatMap.test.tsx index 61ff1d4649..625b6de507 100644 --- a/client/src/components/profile/components/HeatMap.test.tsx +++ b/client/src/components/profile/components/HeatMap.test.tsx @@ -1,5 +1,5 @@ -import React from 'react'; import { render, screen } from '@testing-library/react'; +import React from 'react'; import HeatMap from './HeatMap'; diff --git a/client/src/components/profile/components/HeatMap.tsx b/client/src/components/profile/components/HeatMap.tsx index 1bab5e4936..f04f3b5272 100644 --- a/client/src/components/profile/components/HeatMap.tsx +++ b/client/src/components/profile/components/HeatMap.tsx @@ -1,25 +1,24 @@ -import React, { Component } from 'react'; +import { Row } from '@freecodecamp/react-bootstrap'; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore import CalendarHeatMap from '@freecodecamp/react-calendar-heatmap'; -import { Row } from '@freecodecamp/react-bootstrap'; -import ReactTooltip from 'react-tooltip'; import addDays from 'date-fns/addDays'; import addMonths from 'date-fns/addMonths'; -import startOfDay from 'date-fns/startOfDay'; import isEqual from 'date-fns/isEqual'; +import startOfDay from 'date-fns/startOfDay'; +import React, { Component } from 'react'; import { TFunction, useTranslation } from 'react-i18next'; - -import FullWidthRow from '../../helpers/full-width-row'; -import Spacer from '../../helpers/spacer'; +import ReactTooltip from 'react-tooltip'; import '@freecodecamp/react-calendar-heatmap/dist/styles.css'; import './heatmap.css'; -import { langCodes } from '../../../../../config/i18n/all-langs'; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore import envData from '../../../../../config/env.json'; +import { langCodes } from '../../../../../config/i18n/all-langs'; +import FullWidthRow from '../../helpers/full-width-row'; +import Spacer from '../../helpers/spacer'; // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const { clientLocale } = envData; diff --git a/client/src/components/profile/components/Portfolio.tsx b/client/src/components/profile/components/Portfolio.tsx index 288d45ba27..304251c81d 100644 --- a/client/src/components/profile/components/Portfolio.tsx +++ b/client/src/components/profile/components/Portfolio.tsx @@ -1,5 +1,5 @@ -import React from 'react'; import { Media } from '@freecodecamp/react-bootstrap'; +import React from 'react'; import { useTranslation } from 'react-i18next'; import { FullWidthRow } from '../../helpers'; diff --git a/client/src/components/profile/components/SocialIcons.tsx b/client/src/components/profile/components/SocialIcons.tsx index d8cad06034..781b53afdd 100644 --- a/client/src/components/profile/components/SocialIcons.tsx +++ b/client/src/components/profile/components/SocialIcons.tsx @@ -1,12 +1,12 @@ -import React from 'react'; -import { Row, Col } from '@freecodecamp/react-bootstrap'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faLinkedin, faGithub, faTwitter } from '@fortawesome/free-brands-svg-icons'; import { faLink } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { Row, Col } from '@freecodecamp/react-bootstrap'; +import React from 'react'; import { useTranslation } from 'react-i18next'; import './social-icons.css'; diff --git a/client/src/components/profile/components/TimeLine.test.tsx b/client/src/components/profile/components/TimeLine.test.tsx index 966911d5b9..21c9a1782a 100644 --- a/client/src/components/profile/components/TimeLine.test.tsx +++ b/client/src/components/profile/components/TimeLine.test.tsx @@ -1,9 +1,9 @@ /* eslint-disable @typescript-eslint/ban-ts-comment */ /* eslint-disable @typescript-eslint/no-unsafe-call */ -import React from 'react'; import { render, screen } from '@testing-library/react'; -import TimeLine from './TimeLine'; import { useStaticQuery } from 'gatsby'; +import React from 'react'; +import TimeLine from './TimeLine'; beforeEach(() => { // @ts-ignore diff --git a/client/src/components/profile/components/TimeLine.tsx b/client/src/components/profile/components/TimeLine.tsx index 3d354502f6..0920781569 100644 --- a/client/src/components/profile/components/TimeLine.tsx +++ b/client/src/components/profile/components/TimeLine.tsx @@ -4,8 +4,6 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/restrict-template-expressions */ /* eslint-disable @typescript-eslint/unbound-method */ -import React, { Component, useMemo } from 'react'; -import { reverse, sortBy } from 'lodash-es'; import { Button, Modal, @@ -13,27 +11,25 @@ import { DropdownButton, MenuItem } from '@freecodecamp/react-bootstrap'; +import Loadable from '@loadable/component'; import { useStaticQuery, graphql } from 'gatsby'; +import { reverse, sortBy } from 'lodash-es'; +import React, { Component, useMemo } from 'react'; import { TFunction, withTranslation } from 'react-i18next'; -import './timeline.css'; -import TimelinePagination from './TimelinePagination'; -import { FullWidthRow, Link } from '../../helpers'; -import Loadable from '@loadable/component'; - +import envData from '../../../../../config/env.json'; +import { langCodes } from '../../../../../config/i18n/all-langs'; import { getCertIds, getPathFromID, getTitleFromId } from '../../../../../utils'; - -import { maybeUrlRE } from '../../../utils'; import CertificationIcon from '../../../assets/icons/certification-icon'; +import { maybeUrlRE } from '../../../utils'; +import { FullWidthRow, Link } from '../../helpers'; +import TimelinePagination from './TimelinePagination'; -import { langCodes } from '../../../../../config/i18n/all-langs'; -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore -import envData from '../../../../../config/env.json'; +import './timeline.css'; const SolutionViewer = Loadable( () => diff --git a/client/src/components/redirect-home.ts b/client/src/components/redirect-home.ts index 9336e5e53d..29eeb64ae5 100644 --- a/client/src/components/redirect-home.ts +++ b/client/src/components/redirect-home.ts @@ -1,4 +1,4 @@ -import createRedirect from './create-redirect'; import { withPrefix } from 'gatsby'; +import createRedirect from './create-redirect'; export default createRedirect(withPrefix('/')); diff --git a/client/src/components/search/WithInstantSearch.js b/client/src/components/search/WithInstantSearch.js index 619f4712c4..c4c4d6ca20 100644 --- a/client/src/components/search/WithInstantSearch.js +++ b/client/src/components/search/WithInstantSearch.js @@ -1,12 +1,14 @@ -import React, { Component } from 'react'; import { Location } from '@reach/router'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { InstantSearch, Configure } from 'react-instantsearch-dom'; -import qs from 'query-string'; -import { navigate } from 'gatsby'; -import Media from 'react-responsive'; import algoliasearch from 'algoliasearch/lite'; +import { navigate } from 'gatsby'; +import PropTypes from 'prop-types'; +import qs from 'query-string'; +import React, { Component } from 'react'; +import { InstantSearch, Configure } from 'react-instantsearch-dom'; +import { connect } from 'react-redux'; +import Media from 'react-responsive'; +import { createSelector } from 'reselect'; +import envData from '../../../../config/env.json'; import { newsIndex } from '../../utils/algolia-locale-setup'; import { @@ -15,9 +17,6 @@ import { toggleSearchDropdown, updateSearchQuery } from './redux'; -import envData from '../../../../config/env.json'; - -import { createSelector } from 'reselect'; const { algoliaAppId, algoliaAPIKey } = envData; diff --git a/client/src/components/search/searchBar/search-bar.tsx b/client/src/components/search/searchBar/search-bar.tsx index 1a0341dd4b..8fea9ce5d9 100644 --- a/client/src/components/search/searchBar/search-bar.tsx +++ b/client/src/components/search/searchBar/search-bar.tsx @@ -1,16 +1,16 @@ +import { isEqual } from 'lodash-es'; import React, { Component } from 'react'; +import { HotKeys, ObserveKeys } from 'react-hotkeys'; +import { withTranslation } from 'react-i18next'; +import { Hit } from 'react-instantsearch-core'; +import { SearchBox } from 'react-instantsearch-dom'; import { connect } from 'react-redux'; import { AnyAction, bindActionCreators, Dispatch } from 'redux'; import { createSelector } from 'reselect'; -import { SearchBox } from 'react-instantsearch-dom'; -import { HotKeys, ObserveKeys } from 'react-hotkeys'; -import { isEqual } from 'lodash-es'; -import { withTranslation } from 'react-i18next'; import { searchPageUrl } from '../../../utils/algolia-locale-setup'; import WithInstantSearch from '../WithInstantSearch'; -import { Hit } from 'react-instantsearch-core'; import { isSearchDropdownEnabledSelector, isSearchBarFocusedSelector, diff --git a/client/src/components/search/searchBar/search-hits.tsx b/client/src/components/search/searchBar/search-hits.tsx index 5cb6fef440..d9241487fd 100644 --- a/client/src/components/search/searchBar/search-hits.tsx +++ b/client/src/components/search/searchBar/search-hits.tsx @@ -1,11 +1,11 @@ -import React, { useEffect } from 'react'; -import { connectStateResults, connectHits } from 'react-instantsearch-dom'; -import { SearchState, Hit } from 'react-instantsearch-core'; import { isEmpty } from 'lodash-es'; +import React, { useEffect } from 'react'; import { useTranslation } from 'react-i18next'; +import { SearchState, Hit } from 'react-instantsearch-core'; +import { connectStateResults, connectHits } from 'react-instantsearch-dom'; import { searchPageUrl } from '../../../utils/algolia-locale-setup'; -import Suggestion from './search-suggestion'; import NoHitsSuggestion from './no-hits-suggestion'; +import Suggestion from './search-suggestion'; const searchUrl = searchPageUrl; interface customHitsPropTypes { diff --git a/client/src/components/search/searchBar/search-suggestion.tsx b/client/src/components/search/searchBar/search-suggestion.tsx index 9fdf8ed793..b8f16c69c1 100644 --- a/client/src/components/search/searchBar/search-suggestion.tsx +++ b/client/src/components/search/searchBar/search-suggestion.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import { Highlight } from 'react-instantsearch-dom'; import { Hit } from 'react-instantsearch-core'; +import { Highlight } from 'react-instantsearch-dom'; interface suggestionPropTypes { hit: Hit; diff --git a/client/src/components/search/searchPage/search-page-hits.tsx b/client/src/components/search/searchPage/search-page-hits.tsx index 617ce54f37..2290935013 100644 --- a/client/src/components/search/searchPage/search-page-hits.tsx +++ b/client/src/components/search/searchPage/search-page-hits.tsx @@ -1,3 +1,4 @@ +import { isEmpty } from 'lodash-es'; import React, { EventHandler, SyntheticEvent } from 'react'; import { AutocompleteExposed, SearchState } from 'react-instantsearch-core'; import { @@ -5,7 +6,6 @@ import { connectStateResults, connectAutoComplete } from 'react-instantsearch-dom'; -import { isEmpty } from 'lodash-es'; import EmptySearch from './empty-search'; import NoResults from './no-results'; diff --git a/client/src/components/settings/Certification.js b/client/src/components/settings/Certification.js index a40ac252f3..f2c38b21cb 100644 --- a/client/src/components/settings/Certification.js +++ b/client/src/components/settings/Certification.js @@ -1,6 +1,3 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { find, first } from 'lodash-es'; import { Table, Button, @@ -8,19 +5,22 @@ import { MenuItem } from '@freecodecamp/react-bootstrap'; import { Link, navigate } from 'gatsby'; -import { createSelector } from 'reselect'; +import { find, first } from 'lodash-es'; +import PropTypes from 'prop-types'; +import React, { Component } from 'react'; import { withTranslation } from 'react-i18next'; +import { createSelector } from 'reselect'; import { projectMap, legacyProjectMap } from '../../resources/cert-and-project-map'; -import SectionHeader from './section-header'; +import { maybeUrlRE } from '../../utils'; import ProjectModal from '../SolutionViewer/ProjectModal'; import { FullWidthRow, Spacer } from '../helpers'; -import { maybeUrlRE } from '../../utils'; +import SectionHeader from './section-header'; import './certification.css'; diff --git a/client/src/components/settings/Certification.test.js b/client/src/components/settings/Certification.test.js index bc6ad4d0c8..eb944b36e5 100644 --- a/client/src/components/settings/Certification.test.js +++ b/client/src/components/settings/Certification.test.js @@ -1,5 +1,5 @@ -import React from 'react'; import { render, screen } from '@testing-library/react'; +import React from 'react'; import { Provider } from 'react-redux'; import { createStore } from '../../redux/createStore'; diff --git a/client/src/components/settings/Honesty.test.js b/client/src/components/settings/Honesty.test.js index 7cd60a56e7..5728a20695 100644 --- a/client/src/components/settings/Honesty.test.js +++ b/client/src/components/settings/Honesty.test.js @@ -1,9 +1,9 @@ +import { Button } from '@freecodecamp/react-bootstrap'; import React from 'react'; -import ShallowRenderer from 'react-test-renderer/shallow'; import TestRenderer from 'react-test-renderer'; +import ShallowRenderer from 'react-test-renderer/shallow'; import Honesty from './honesty'; -import { Button } from '@freecodecamp/react-bootstrap'; describe('', () => { const renderer = new ShallowRenderer(); diff --git a/client/src/components/settings/about.tsx b/client/src/components/settings/about.tsx index 8fa41721ce..d7d3898dd6 100644 --- a/client/src/components/settings/about.tsx +++ b/client/src/components/settings/about.tsx @@ -1,4 +1,3 @@ -import React, { Component } from 'react'; import { FormGroup, ControlLabel, @@ -8,12 +7,13 @@ import { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore } from '@freecodecamp/react-bootstrap'; +import React, { Component } from 'react'; +import { TFunction, withTranslation } from 'react-i18next'; import { FullWidthRow, Spacer } from '../helpers'; +import BlockSaveButton from '../helpers/form/block-save-button'; import ThemeSettings from './theme'; import UsernameSettings from './username'; -import BlockSaveButton from '../helpers/form/block-save-button'; -import { TFunction, withTranslation } from 'react-i18next'; type FormValues = { name: string; diff --git a/client/src/components/settings/danger-zone.tsx b/client/src/components/settings/danger-zone.tsx index 480d1e6f38..00d53102bf 100644 --- a/client/src/components/settings/danger-zone.tsx +++ b/client/src/components/settings/danger-zone.tsx @@ -1,14 +1,14 @@ +import { Button, Panel } from '@freecodecamp/react-bootstrap'; import React, { Component } from 'react'; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore -import { Button, Panel } from '@freecodecamp/react-bootstrap'; +import { TFunction, withTranslation } from 'react-i18next'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; -import { TFunction, withTranslation } from 'react-i18next'; import type { Dispatch } from 'redux'; -import { FullWidthRow, ButtonSpacer, Spacer } from '../helpers'; import { deleteAccount, resetProgress } from '../../redux/settings'; +import { FullWidthRow, ButtonSpacer, Spacer } from '../helpers'; import DeleteModal from './delete-modal'; import ResetModal from './reset-modal'; diff --git a/client/src/components/settings/delete-modal.tsx b/client/src/components/settings/delete-modal.tsx index 6e68a2eba6..1c27d34aa9 100644 --- a/client/src/components/settings/delete-modal.tsx +++ b/client/src/components/settings/delete-modal.tsx @@ -1,10 +1,10 @@ +import { Button, Modal } from '@freecodecamp/react-bootstrap'; import React from 'react'; import { Trans, useTranslation } from 'react-i18next'; import { ButtonSpacer } from '../helpers'; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore -import { Button, Modal } from '@freecodecamp/react-bootstrap'; import './danger-zone.css'; diff --git a/client/src/components/settings/email.tsx b/client/src/components/settings/email.tsx index 1f8ce44c0e..0ce5ee6c1d 100644 --- a/client/src/components/settings/email.tsx +++ b/client/src/components/settings/email.tsx @@ -1,7 +1,3 @@ -import React, { Component } from 'react'; -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; -import { Link } from 'gatsby'; import { HelpBlock, Alert, @@ -12,17 +8,21 @@ import { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore } from '@freecodecamp/react-bootstrap'; -import isEmail from 'validator/lib/isEmail'; +import { Link } from 'gatsby'; +import React, { Component } from 'react'; import { TFunction, Trans, withTranslation } from 'react-i18next'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; import type { Dispatch } from 'redux'; +import isEmail from 'validator/lib/isEmail'; import { updateMyEmail } from '../../redux/settings'; import { maybeEmailRE } from '../../utils'; +import BlockSaveButton from '../helpers/form/block-save-button'; import FullWidthRow from '../helpers/full-width-row'; import Spacer from '../helpers/spacer'; import SectionHeader from './section-header'; -import BlockSaveButton from '../helpers/form/block-save-button'; import ToggleSetting from './toggle-setting'; const mapStateToProps = () => ({}); diff --git a/client/src/components/settings/honesty.tsx b/client/src/components/settings/honesty.tsx index 25f73c157d..a697284f9a 100644 --- a/client/src/components/settings/honesty.tsx +++ b/client/src/components/settings/honesty.tsx @@ -1,12 +1,12 @@ +import { Button, Panel } from '@freecodecamp/react-bootstrap'; import React from 'react'; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore -import { Button, Panel } from '@freecodecamp/react-bootstrap'; import { useTranslation } from 'react-i18next'; +import HonestyPolicy from '../../resources/honesty-policy'; import { FullWidthRow } from '../helpers'; import SectionHeader from './section-header'; -import HonestyPolicy from '../../resources/honesty-policy'; import './honesty.css'; diff --git a/client/src/components/settings/internet.tsx b/client/src/components/settings/internet.tsx index edecbf6208..8ff29629be 100644 --- a/client/src/components/settings/internet.tsx +++ b/client/src/components/settings/internet.tsx @@ -1,6 +1,5 @@ -import React, { Component } from 'react'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faCheck } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { HelpBlock, FormControl, @@ -9,14 +8,15 @@ import { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore } from '@freecodecamp/react-bootstrap'; -import isURL from 'validator/lib/isURL'; +import React, { Component } from 'react'; import { TFunction, withTranslation } from 'react-i18next'; +import isURL from 'validator/lib/isURL'; import { maybeUrlRE } from '../../utils'; -import SectionHeader from './section-header'; import { FullWidthRow } from '../helpers'; import BlockSaveButton from '../helpers/form/block-save-button'; +import SectionHeader from './section-header'; interface InternetFormValues { githubProfile: string; diff --git a/client/src/components/settings/portfolio.tsx b/client/src/components/settings/portfolio.tsx index 4e7b1480ac..e7a259422d 100644 --- a/client/src/components/settings/portfolio.tsx +++ b/client/src/components/settings/portfolio.tsx @@ -1,5 +1,3 @@ -import React, { Component, FormEvent } from 'react'; -import { nanoid } from 'nanoid'; import { Button, FormGroup, @@ -10,14 +8,16 @@ import { // @ts-ignore } from '@freecodecamp/react-bootstrap'; import { findIndex, find, isEqual } from 'lodash-es'; -import isURL from 'validator/lib/isURL'; +import { nanoid } from 'nanoid'; +import React, { Component, FormEvent } from 'react'; import { TFunction, withTranslation } from 'react-i18next'; +import isURL from 'validator/lib/isURL'; import { hasProtocolRE } from '../../utils'; import { FullWidthRow, ButtonSpacer, Spacer } from '../helpers'; -import SectionHeader from './section-header'; import BlockSaveButton from '../helpers/form/block-save-button'; +import SectionHeader from './section-header'; type PortfolioValues = { id: string; diff --git a/client/src/components/settings/privacy.tsx b/client/src/components/settings/privacy.tsx index f29f28e0c9..18b8f232c6 100644 --- a/client/src/components/settings/privacy.tsx +++ b/client/src/components/settings/privacy.tsx @@ -1,20 +1,20 @@ +import { Button, Form } from '@freecodecamp/react-bootstrap'; import React, { Component } from 'react'; -import { bindActionCreators } from 'redux'; +import { TFunction, withTranslation } from 'react-i18next'; import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; +import type { Dispatch } from 'redux'; import { createSelector } from 'reselect'; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore -import { Button, Form } from '@freecodecamp/react-bootstrap'; -import { TFunction, withTranslation } from 'react-i18next'; -import type { Dispatch } from 'redux'; import { userSelector } from '../../redux'; import { submitProfileUI } from '../../redux/settings'; import FullWidthRow from '../helpers/full-width-row'; import Spacer from '../helpers/spacer'; -import ToggleSetting from './toggle-setting'; import SectionHeader from './section-header'; +import ToggleSetting from './toggle-setting'; const mapStateToProps = createSelector(userSelector, user => ({ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment diff --git a/client/src/components/settings/reset-modal.tsx b/client/src/components/settings/reset-modal.tsx index 77d2e58e48..90fc5ce8ab 100644 --- a/client/src/components/settings/reset-modal.tsx +++ b/client/src/components/settings/reset-modal.tsx @@ -1,10 +1,10 @@ +import { Button, Modal } from '@freecodecamp/react-bootstrap'; import React from 'react'; import { useTranslation } from 'react-i18next'; import { ButtonSpacer } from '../helpers'; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore -import { Button, Modal } from '@freecodecamp/react-bootstrap'; type ResetModalProps = { onHide: () => void; diff --git a/client/src/components/settings/theme.tsx b/client/src/components/settings/theme.tsx index aeb13a0fa8..93b4d30afe 100644 --- a/client/src/components/settings/theme.tsx +++ b/client/src/components/settings/theme.tsx @@ -1,7 +1,7 @@ +import { Form } from '@freecodecamp/react-bootstrap'; import React from 'react'; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore -import { Form } from '@freecodecamp/react-bootstrap'; import { useTranslation } from 'react-i18next'; import ToggleSetting from './toggle-setting'; diff --git a/client/src/components/settings/toggle-setting.tsx b/client/src/components/settings/toggle-setting.tsx index c3f74765b5..ccb7804cde 100644 --- a/client/src/components/settings/toggle-setting.tsx +++ b/client/src/components/settings/toggle-setting.tsx @@ -1,4 +1,3 @@ -import React from 'react'; import { FormGroup, ControlLabel, @@ -6,9 +5,10 @@ import { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore } from '@freecodecamp/react-bootstrap'; +import React from 'react'; -import TB from '../helpers/toggle-button'; import { ButtonSpacer } from '../helpers'; +import TB from '../helpers/toggle-button'; import './toggle-setting.css'; diff --git a/client/src/components/settings/username.tsx b/client/src/components/settings/username.tsx index 0e76a5c991..461324fedc 100644 --- a/client/src/components/settings/username.tsx +++ b/client/src/components/settings/username.tsx @@ -1,8 +1,4 @@ /* eslint-disable @typescript-eslint/unbound-method */ -import React, { Component } from 'react'; -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; import { ControlLabel, FormControl, @@ -11,17 +7,21 @@ import { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore } from '@freecodecamp/react-bootstrap'; +import React, { Component } from 'react'; import { TFunction, withTranslation } from 'react-i18next'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; import type { Dispatch } from 'redux'; +import { createSelector } from 'reselect'; +import { isValidUsername } from '../../../../utils/validate'; import { validateUsername, usernameValidationSelector, submitNewUsername } from '../../redux/settings'; -import FullWidthRow from '../helpers/full-width-row'; import BlockSaveButton from '../helpers/form/block-save-button'; -import { isValidUsername } from '../../../../utils/validate'; +import FullWidthRow from '../helpers/full-width-row'; type UsernameProps = { isValidUsername: boolean; diff --git a/client/src/pages/404.tsx b/client/src/pages/404.tsx index 95ff28fefe..55e7eced04 100644 --- a/client/src/pages/404.tsx +++ b/client/src/pages/404.tsx @@ -1,10 +1,10 @@ -import React from 'react'; import { Router } from '@reach/router'; import { withPrefix } from 'gatsby'; +import React from 'react'; -import FourOhFour from '../components/FourOhFour'; /* 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 { diff --git a/client/src/pages/certification.tsx b/client/src/pages/certification.tsx index 38f3c48ed1..50660e41c7 100644 --- a/client/src/pages/certification.tsx +++ b/client/src/pages/certification.tsx @@ -1,9 +1,9 @@ -import React from 'react'; import { Router } from '@reach/router'; import { withPrefix } from 'gatsby'; +import React from 'react'; -import RedirectHome from '../components/redirect-home'; import ShowCertification from '../client-only-routes/show-certification'; +import RedirectHome from '../components/redirect-home'; import './certification.css'; diff --git a/client/src/pages/challenges.test.ts b/client/src/pages/challenges.test.ts index 2dc85df4c1..03784f35d8 100644 --- a/client/src/pages/challenges.test.ts +++ b/client/src/pages/challenges.test.ts @@ -6,8 +6,8 @@ * We should either make the expected properties optional, or reevaluate * these tests. */ -import toLearnPath from '../utils/to-learn-path'; import { withPrefix } from 'gatsby'; +import toLearnPath from '../utils/to-learn-path'; describe('toLearnPath', () => { it('should return a string', () => { diff --git a/client/src/pages/challenges.tsx b/client/src/pages/challenges.tsx index b85e576c65..86a5c4e478 100644 --- a/client/src/pages/challenges.tsx +++ b/client/src/pages/challenges.tsx @@ -1,7 +1,7 @@ // this exists purely to redirect legacy challenge paths to /learn -import React from 'react'; import { Router } from '@reach/router'; import { navigate, withPrefix } from 'gatsby'; +import React from 'react'; import toLearnPath from '../utils/to-learn-path'; diff --git a/client/src/pages/donate.tsx b/client/src/pages/donate.tsx index b2391851dd..ea0e36467a 100644 --- a/client/src/pages/donate.tsx +++ b/client/src/pages/donate.tsx @@ -1,13 +1,12 @@ +import { Grid, Row, Col, Alert } from '@freecodecamp/react-bootstrap'; import React, { useEffect } from 'react'; import Helmet from 'react-helmet'; +import { TFunction, withTranslation } from 'react-i18next'; +import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import type { Dispatch } from 'redux'; -import { connect } from 'react-redux'; import { createSelector } from 'reselect'; -import { Grid, Row, Col, Alert } from '@freecodecamp/react-bootstrap'; -import { TFunction, withTranslation } from 'react-i18next'; -import { Spacer, Loader } from '../components/helpers'; import DonateForm from '../components/Donation/DonateForm'; import { DonationText, @@ -15,8 +14,9 @@ import { DonationOptionsText, DonationOptionsAlertText } from '../components/Donation/DonationTextComponents'; -import { signInLoadingSelector, userSelector, executeGA } from '../redux'; +import { Spacer, Loader } from '../components/helpers'; import CampersImage from '../components/landing/components/CampersImage'; +import { signInLoadingSelector, userSelector, executeGA } from '../redux'; interface ExecuteGaArg { type: string; diff --git a/client/src/pages/email-sign-up.tsx b/client/src/pages/email-sign-up.tsx index d6f17e015b..7df6f055e1 100644 --- a/client/src/pages/email-sign-up.tsx +++ b/client/src/pages/email-sign-up.tsx @@ -1,18 +1,17 @@ +import { Row, Col, Button, Grid } from '@freecodecamp/react-bootstrap'; import React, { useEffect } from 'react'; +import Helmet from 'react-helmet'; +import { TFunction, withTranslation } from 'react-i18next'; +import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import type { Dispatch } from 'redux'; -import { connect } from 'react-redux'; -import SectionHeader from '../components/settings/section-header'; -import IntroDescription from '../components/Intro/components/IntroDescription'; -import { TFunction, withTranslation } from 'react-i18next'; - -import { Row, Col, Button, Grid } from '@freecodecamp/react-bootstrap'; -import Helmet from 'react-helmet'; import { createSelector } from 'reselect'; - -import { ButtonSpacer, Spacer } from '../components/helpers'; -import { acceptTerms, userSelector } from '../redux'; +import IntroDescription from '../components/Intro/components/IntroDescription'; import createRedirect from '../components/create-redirect'; +import { ButtonSpacer, Spacer } from '../components/helpers'; +import SectionHeader from '../components/settings/section-header'; + +import { acceptTerms, userSelector } from '../redux'; import './email-sign-up.css'; diff --git a/client/src/pages/learn.tsx b/client/src/pages/learn.tsx index 04a7a83a81..1434a74a9c 100644 --- a/client/src/pages/learn.tsx +++ b/client/src/pages/learn.tsx @@ -1,15 +1,15 @@ -import React from 'react'; import { Grid, Row, Col } from '@freecodecamp/react-bootstrap'; -import { createSelector } from 'reselect'; import { graphql } from 'gatsby'; +import React from 'react'; import Helmet from 'react-helmet'; -import { connect } from 'react-redux'; import { useTranslation } from 'react-i18next'; +import { connect } from 'react-redux'; +import { createSelector } from 'reselect'; +import Intro from '../components/Intro'; +import Map from '../components/Map'; import { Spacer } from '../components/helpers'; import LearnLayout from '../components/layouts/learn'; -import Map from '../components/Map'; -import Intro from '../components/Intro'; import { userFetchStateSelector, isSignedInSelector, diff --git a/client/src/pages/settings.tsx b/client/src/pages/settings.tsx index ab8eb83d58..95a19aec6a 100644 --- a/client/src/pages/settings.tsx +++ b/client/src/pages/settings.tsx @@ -1,9 +1,9 @@ -import React from 'react'; import { Router } from '@reach/router'; import { withPrefix } from 'gatsby'; +import React from 'react'; -import RedirectHome from '../components/redirect-home'; import ShowSettings from '../client-only-routes/show-settings'; +import RedirectHome from '../components/redirect-home'; function Settings(): JSX.Element { return ( diff --git a/client/src/pages/unsubscribed.tsx b/client/src/pages/unsubscribed.tsx index f08f7216b9..55f39702b8 100644 --- a/client/src/pages/unsubscribed.tsx +++ b/client/src/pages/unsubscribed.tsx @@ -1,9 +1,9 @@ -import React from 'react'; import { Router } from '@reach/router'; import { withPrefix } from 'gatsby'; +import React from 'react'; -import RedirectHome from '../components/redirect-home'; import ShowUnsubscribed from '../client-only-routes/show-unsubscribed'; +import RedirectHome from '../components/redirect-home'; function Unsubscribed(): JSX.Element { return ( diff --git a/client/src/pages/update-email.tsx b/client/src/pages/update-email.tsx index 65981674b0..f98c5d2ca1 100644 --- a/client/src/pages/update-email.tsx +++ b/client/src/pages/update-email.tsx @@ -1,10 +1,3 @@ -import React, { useState } from 'react'; -import type { FormEvent, ChangeEvent } from 'react'; -import { Link } from 'gatsby'; -import { bindActionCreators } from 'redux'; -import type { Dispatch } from 'redux'; -import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; import { Form, FormGroup, @@ -15,10 +8,17 @@ import { Col, Button } from '@freecodecamp/react-bootstrap'; -import Helmet from 'react-helmet'; -import isEmail from 'validator/lib/isEmail'; +import { Link } from 'gatsby'; import { isString } from 'lodash-es'; +import React, { useState } from 'react'; +import type { FormEvent, ChangeEvent } from 'react'; +import Helmet from 'react-helmet'; import { TFunction, withTranslation } from 'react-i18next'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; +import type { Dispatch } from 'redux'; +import { createSelector } from 'reselect'; +import isEmail from 'validator/lib/isEmail'; import { Spacer } from '../components/helpers'; import './update-email.css'; diff --git a/client/src/pages/user.tsx b/client/src/pages/user.tsx index 7cac7a5a63..03559eafa8 100644 --- a/client/src/pages/user.tsx +++ b/client/src/pages/user.tsx @@ -1,9 +1,9 @@ -import React from 'react'; import { Router } from '@reach/router'; import { withPrefix } from 'gatsby'; +import React from 'react'; -import RedirectHome from '../components/redirect-home'; import ShowUser from '../client-only-routes/show-user'; +import RedirectHome from '../components/redirect-home'; function User(): JSX.Element { return ( diff --git a/client/src/redux/accept-terms-saga.js b/client/src/redux/accept-terms-saga.js index b5027e2e3f..b97bf59a60 100644 --- a/client/src/redux/accept-terms-saga.js +++ b/client/src/redux/accept-terms-saga.js @@ -1,10 +1,10 @@ -import { call, put, takeEvery } from 'redux-saga/effects'; import { navigate } from 'gatsby'; +import { call, put, takeEvery } from 'redux-saga/effects'; -import { acceptTermsComplete, acceptTermsError } from './'; import { createFlashMessage } from '../components/Flash/redux'; import { putUserAcceptsTerms } from '../utils/ajax'; +import { acceptTermsComplete, acceptTermsError } from './'; function* acceptTermsSaga({ payload: quincyEmails }) { try { diff --git a/client/src/redux/action-types.js b/client/src/redux/action-types.js new file mode 100644 index 0000000000..778053c54e --- /dev/null +++ b/client/src/redux/action-types.js @@ -0,0 +1,33 @@ +import { createTypes, createAsyncTypes } from '../utils/create-types'; + +export const ns = 'app'; + +export const actionTypes = createTypes( + [ + 'appMount', + 'hardGoTo', + 'allowBlockDonationRequests', + 'closeDonationModal', + 'preventBlockDonationRequests', + 'preventProgressDonationRequests', + 'openDonationModal', + 'onlineStatusChange', + 'resetUserData', + 'tryToShowDonationModal', + 'executeGA', + 'submitComplete', + 'updateComplete', + 'updateCurrentChallengeId', + 'updateFailed', + 'updateDonationFormState', + ...createAsyncTypes('fetchUser'), + ...createAsyncTypes('addDonation'), + ...createAsyncTypes('createStripeSession'), + ...createAsyncTypes('postChargeStripe'), + ...createAsyncTypes('fetchProfileForUser'), + ...createAsyncTypes('acceptTerms'), + ...createAsyncTypes('showCert'), + ...createAsyncTypes('reportUser') + ], + ns +); diff --git a/client/src/redux/app-mount-saga.js b/client/src/redux/app-mount-saga.js index d5f2774f18..6f9e68ca9b 100644 --- a/client/src/redux/app-mount-saga.js +++ b/client/src/redux/app-mount-saga.js @@ -1,5 +1,5 @@ -import { put, takeEvery } from 'redux-saga/effects'; import qs from 'query-string'; +import { put, takeEvery } from 'redux-saga/effects'; import { createFlashMessage } from '../components/Flash/redux'; function* parseMessagesSaga() { diff --git a/client/src/redux/createStore.js b/client/src/redux/createStore.js index 7582e49acd..edab62b618 100644 --- a/client/src/redux/createStore.js +++ b/client/src/redux/createStore.js @@ -1,15 +1,14 @@ /* eslint-disable-next-line max-len */ -import { composeWithDevTools } from 'redux-devtools-extension/logOnlyInProduction'; import { createStore as reduxCreateStore, applyMiddleware } from 'redux'; -import createSagaMiddleware from 'redux-saga'; +import { composeWithDevTools } from 'redux-devtools-extension/logOnlyInProduction'; import { createEpicMiddleware } from 'redux-observable'; +import createSagaMiddleware from 'redux-saga'; +import envData from '../../../config/env.json'; +import { isBrowser } from '../../utils'; import rootEpic from './rootEpic'; import rootReducer from './rootReducer'; import rootSaga from './rootSaga'; -import { isBrowser } from '../../utils'; - -import envData from '../../../config/env.json'; const { environment } = envData; diff --git a/client/src/redux/donation-saga.js b/client/src/redux/donation-saga.js index ed4594fc29..40e9490a8b 100644 --- a/client/src/redux/donation-saga.js +++ b/client/src/redux/donation-saga.js @@ -1,4 +1,6 @@ import { put, select, takeEvery, delay, call, take } from 'redux-saga/effects'; +import { addDonation } from '../utils/ajax'; +import { actionTypes as appTypes } from './action-types'; import { openDonationModal, @@ -7,12 +9,9 @@ import { preventProgressDonationRequests, recentlyClaimedBlockSelector, addDonationComplete, - addDonationError, - types as appTypes + addDonationError } from './'; -import { addDonation } from '../utils/ajax'; - const defaultDonationError = `Something is not right. Please contact donors@freecodecamp.org`; function* showDonateModalSaga() { diff --git a/client/src/redux/error-saga.js b/client/src/redux/error-saga.js index fa3d81ffe5..b419164e55 100644 --- a/client/src/redux/error-saga.js +++ b/client/src/redux/error-saga.js @@ -1,10 +1,10 @@ import { navigate } from 'gatsby'; -import { takeEvery, put } from 'redux-saga/effects'; import { isError } from 'lodash-es'; +import { takeEvery, put } from 'redux-saga/effects'; +import { createFlashMessage } from '../components/Flash/redux'; import { isHandledError, unwrapHandledError } from '../utils/handled-error'; import { reportClientSideError } from '../utils/report-error'; -import { createFlashMessage } from '../components/Flash/redux'; import reportedErrorMessage from '../utils/reported-error-message'; const errorActionSelector = action => isError(action.payload); diff --git a/client/src/redux/failed-updates-epic.js b/client/src/redux/failed-updates-epic.js index 6c057e9a7a..1c96b121b1 100644 --- a/client/src/redux/failed-updates-epic.js +++ b/client/src/redux/failed-updates-epic.js @@ -1,3 +1,4 @@ +import { ofType } from 'redux-observable'; import { merge, empty } from 'rxjs'; import { tap, @@ -7,19 +8,14 @@ import { switchMap, catchError } from 'rxjs/operators'; -import { ofType } from 'redux-observable'; import store from 'store'; import { v4 as uuid } from 'uuid'; -import { - types, - onlineStatusChange, - isOnlineSelector, - isSignedInSelector -} from './'; -import postUpdate$ from '../templates/Challenges/utils/postUpdate$'; -import { isGoodXHRStatus } from '../templates/Challenges/utils'; import { backEndProject } from '../../utils/challengeTypes'; +import { isGoodXHRStatus } from '../templates/Challenges/utils'; +import postUpdate$ from '../templates/Challenges/utils/postUpdate$'; +import { actionTypes } from './action-types'; +import { onlineStatusChange, isOnlineSelector, isSignedInSelector } from './'; const key = 'fcc-failed-updates'; @@ -33,7 +29,7 @@ const isSubmitable = failure => function failedUpdateEpic(action$, state$) { const storeUpdates = action$.pipe( - ofType(types.updateFailed), + ofType(actionTypes.updateFailed), tap(({ payload = {} }) => { if ('endpoint' in payload && 'payload' in payload) { const failures = store.get(key) || []; @@ -45,7 +41,7 @@ function failedUpdateEpic(action$, state$) { ); const flushUpdates = action$.pipe( - ofType(types.fetchUserComplete, types.updateComplete), + ofType(actionTypes.fetchUserComplete, actionTypes.updateComplete), filter(() => isSignedInSelector(state$.value)), filter(() => store.get(key)), filter(() => isOnlineSelector(state$.value)), diff --git a/client/src/redux/failed-updates-epic.test.js b/client/src/redux/failed-updates-epic.test.js index 7f3e66eb0e..f344e74619 100644 --- a/client/src/redux/failed-updates-epic.test.js +++ b/client/src/redux/failed-updates-epic.test.js @@ -1,8 +1,8 @@ -import { Subject } from 'rxjs'; import { ActionsObservable, StateObservable } from 'redux-observable'; -import failedUpdatesEpic from './failed-updates-epic'; -import { types } from './'; +import { Subject } from 'rxjs'; import store from 'store'; +import { actionTypes } from './action-types'; +import failedUpdatesEpic from './failed-updates-epic'; jest.mock('../analytics'); @@ -13,7 +13,7 @@ describe('failed-updates-epic', () => { store.set(key, failedSubmissions); const action$ = ActionsObservable.of({ - type: types.updateComplete + type: actionTypes.updateComplete }); const state$ = new StateObservable(new Subject(), initialState); const epic$ = failedUpdatesEpic(action$, state$); diff --git a/client/src/redux/fetch-user-saga.js b/client/src/redux/fetch-user-saga.js index e2147413b4..5d60b990b8 100644 --- a/client/src/redux/fetch-user-saga.js +++ b/client/src/redux/fetch-user-saga.js @@ -1,13 +1,13 @@ import { call, put, takeEvery } from 'redux-saga/effects'; +import { getSessionUser, getUserProfile } from '../utils/ajax'; +import { jwt } from './cookieValues'; import { fetchUserComplete, fetchUserError, fetchProfileForUserError, fetchProfileForUserComplete } from './'; -import { getSessionUser, getUserProfile } from '../utils/ajax'; -import { jwt } from './cookieValues'; function* fetchSessionUser() { if (!jwt) { diff --git a/client/src/redux/ga-saga.test.js b/client/src/redux/ga-saga.test.js index bb6a8bf06f..582b87035e 100644 --- a/client/src/redux/ga-saga.test.js +++ b/client/src/redux/ga-saga.test.js @@ -1,7 +1,7 @@ -import { types } from '.'; -import { createGaSaga } from './ga-saga'; -import ga from '../analytics'; import { expectSaga } from 'redux-saga-test-plan'; +import ga from '../analytics'; +import { actionTypes } from './action-types'; +import { createGaSaga } from './ga-saga'; jest.mock('../analytics'); @@ -16,12 +16,12 @@ describe('ga-saga', () => { } }; return ( - expectSaga(createGaSaga, types) + expectSaga(createGaSaga, actionTypes) // Assert that the `call` with expected pramater will eventually happen. .call(GaTypes.event, mockEventPayload.data) // Dispatch any actions that the saga will `take`. - .dispatch({ type: types.executeGA, payload: mockEventPayload }) + .dispatch({ type: actionTypes.executeGA, payload: mockEventPayload }) // Start the test. .run() diff --git a/client/src/redux/hard-go-to-epic.js b/client/src/redux/hard-go-to-epic.js index 300f4e31c6..53a4cb302a 100644 --- a/client/src/redux/hard-go-to-epic.js +++ b/client/src/redux/hard-go-to-epic.js @@ -1,11 +1,11 @@ import { ofType } from 'redux-observable'; import { tap, ignoreElements } from 'rxjs/operators'; -import { types } from './'; +import { actionTypes } from './action-types'; export default function hardGoToEpic(action$, _, { location }) { return action$.pipe( - ofType(types.hardGoTo), + ofType(actionTypes.hardGoTo), tap(({ payload }) => { location.href = payload; }), diff --git a/client/src/redux/index.js b/client/src/redux/index.js index d00dbe3b53..33eb196d81 100644 --- a/client/src/redux/index.js +++ b/client/src/redux/index.js @@ -1,27 +1,26 @@ -import { createAction, handleActions } from 'redux-actions'; import { uniqBy } from 'lodash-es'; +import { createAction, handleActions } from 'redux-actions'; import store from 'store'; -import { createTypes, createAsyncTypes } from '../utils/create-types'; -import { createFetchUserSaga } from './fetch-user-saga'; +import { actionTypes as challengeTypes } from '../templates/Challenges/redux/action-types'; +import { CURRENT_CHALLENGE_KEY } from '../templates/Challenges/redux/current-challenge-saga'; import { createAcceptTermsSaga } from './accept-terms-saga'; +import { actionTypes, ns } from './action-types'; import { createAppMountSaga } from './app-mount-saga'; -import { createReportUserSaga } from './report-user-saga'; -import { createShowCertSaga } from './show-cert-saga'; -import { createNightModeSaga } from './night-mode-saga'; import { createDonationSaga } from './donation-saga'; +import failedUpdatesEpic from './failed-updates-epic'; +import { createFetchUserSaga } from './fetch-user-saga'; import { createGaSaga } from './ga-saga'; import hardGoToEpic from './hard-go-to-epic'; -import failedUpdatesEpic from './failed-updates-epic'; +import { createNightModeSaga } from './night-mode-saga'; +import { createReportUserSaga } from './report-user-saga'; + +import { actionTypes as settingsTypes } from './settings/action-types'; +import { createShowCertSaga } from './show-cert-saga'; import updateCompleteEpic from './update-complete-epic'; -import { types as settingsTypes } from './settings'; -import { types as challengeTypes } from '../templates/Challenges/redux/'; -// eslint-disable-next-line max-len -import { CURRENT_CHALLENGE_KEY } from '../templates/Challenges/redux/current-challenge-saga'; - -export const ns = 'app'; +export { ns }; export const defaultFetchState = { pending: true, @@ -62,114 +61,92 @@ const initialState = { } }; -export const types = createTypes( - [ - 'appMount', - 'hardGoTo', - 'allowBlockDonationRequests', - 'closeDonationModal', - 'preventBlockDonationRequests', - 'preventProgressDonationRequests', - 'openDonationModal', - 'onlineStatusChange', - 'resetUserData', - 'tryToShowDonationModal', - 'executeGA', - 'submitComplete', - 'updateComplete', - 'updateCurrentChallengeId', - 'updateFailed', - 'updateDonationFormState', - ...createAsyncTypes('fetchUser'), - ...createAsyncTypes('addDonation'), - ...createAsyncTypes('fetchProfileForUser'), - ...createAsyncTypes('acceptTerms'), - ...createAsyncTypes('showCert'), - ...createAsyncTypes('reportUser') - ], - ns -); - export const epics = [hardGoToEpic, failedUpdatesEpic, updateCompleteEpic]; export const sagas = [ - ...createAcceptTermsSaga(types), - ...createAppMountSaga(types), - ...createDonationSaga(types), - ...createGaSaga(types), - ...createFetchUserSaga(types), - ...createShowCertSaga(types), - ...createReportUserSaga(types), - ...createNightModeSaga({ ...types, ...settingsTypes }) + ...createAcceptTermsSaga(actionTypes), + ...createAppMountSaga(actionTypes), + ...createDonationSaga(actionTypes), + ...createGaSaga(actionTypes), + ...createFetchUserSaga(actionTypes), + ...createShowCertSaga(actionTypes), + ...createReportUserSaga(actionTypes), + ...createNightModeSaga({ ...actionTypes, ...settingsTypes }) ]; -export const appMount = createAction(types.appMount); +export const appMount = createAction(actionTypes.appMount); export const tryToShowDonationModal = createAction( - types.tryToShowDonationModal + actionTypes.tryToShowDonationModal ); -export const executeGA = createAction(types.executeGA); +export const executeGA = createAction(actionTypes.executeGA); export const allowBlockDonationRequests = createAction( - types.allowBlockDonationRequests + actionTypes.allowBlockDonationRequests ); -export const closeDonationModal = createAction(types.closeDonationModal); -export const openDonationModal = createAction(types.openDonationModal); +export const closeDonationModal = createAction(actionTypes.closeDonationModal); +export const openDonationModal = createAction(actionTypes.openDonationModal); export const preventBlockDonationRequests = createAction( - types.preventBlockDonationRequests + actionTypes.preventBlockDonationRequests ); export const preventProgressDonationRequests = createAction( - types.preventProgressDonationRequests + actionTypes.preventProgressDonationRequests ); export const updateDonationFormState = createAction( - types.updateDonationFormState + actionTypes.updateDonationFormState ); -export const onlineStatusChange = createAction(types.onlineStatusChange); +export const onlineStatusChange = createAction(actionTypes.onlineStatusChange); // TODO: re-evaluate this since /internal is no longer used. // `hardGoTo` is used to hit the API server directly // without going through /internal // used for things like /signin and /signout -export const hardGoTo = createAction(types.hardGoTo); +export const hardGoTo = createAction(actionTypes.hardGoTo); -export const submitComplete = createAction(types.submitComplete); -export const updateComplete = createAction(types.updateComplete); -export const updateFailed = createAction(types.updateFailed); +export const submitComplete = createAction(actionTypes.submitComplete); +export const updateComplete = createAction(actionTypes.updateComplete); +export const updateFailed = createAction(actionTypes.updateFailed); -export const acceptTerms = createAction(types.acceptTerms); -export const acceptTermsComplete = createAction(types.acceptTermsComplete); -export const acceptTermsError = createAction(types.acceptTermsError); +export const acceptTerms = createAction(actionTypes.acceptTerms); +export const acceptTermsComplete = createAction( + actionTypes.acceptTermsComplete +); +export const acceptTermsError = createAction(actionTypes.acceptTermsError); -export const fetchUser = createAction(types.fetchUser); -export const fetchUserComplete = createAction(types.fetchUserComplete); -export const fetchUserError = createAction(types.fetchUserError); +export const fetchUser = createAction(actionTypes.fetchUser); +export const fetchUserComplete = createAction(actionTypes.fetchUserComplete); +export const fetchUserError = createAction(actionTypes.fetchUserError); -export const addDonation = createAction(types.addDonation); -export const addDonationComplete = createAction(types.addDonationComplete); -export const addDonationError = createAction(types.addDonationError); +export const addDonation = createAction(actionTypes.addDonation); +export const addDonationComplete = createAction( + actionTypes.addDonationComplete +); +export const addDonationError = createAction(actionTypes.addDonationError); -export const fetchProfileForUser = createAction(types.fetchProfileForUser); +export const fetchProfileForUser = createAction( + actionTypes.fetchProfileForUser +); export const fetchProfileForUserComplete = createAction( - types.fetchProfileForUserComplete + actionTypes.fetchProfileForUserComplete ); export const fetchProfileForUserError = createAction( - types.fetchProfileForUserError + actionTypes.fetchProfileForUserError ); -export const reportUser = createAction(types.reportUser); -export const reportUserComplete = createAction(types.reportUserComplete); -export const reportUserError = createAction(types.reportUserError); +export const reportUser = createAction(actionTypes.reportUser); +export const reportUserComplete = createAction(actionTypes.reportUserComplete); +export const reportUserError = createAction(actionTypes.reportUserError); -export const resetUserData = createAction(types.resetUserData); +export const resetUserData = createAction(actionTypes.resetUserData); -export const showCert = createAction(types.showCert); -export const showCertComplete = createAction(types.showCertComplete); -export const showCertError = createAction(types.showCertError); +export const showCert = createAction(actionTypes.showCert); +export const showCertComplete = createAction(actionTypes.showCertComplete); +export const showCertError = createAction(actionTypes.showCertError); export const updateCurrentChallengeId = createAction( - types.updateCurrentChallengeId + actionTypes.updateCurrentChallengeId ); export const completedChallengesSelector = state => @@ -384,7 +361,7 @@ function spreadThePayloadOnUser(state, payload) { export const reducer = handleActions( { - [types.acceptTermsComplete]: (state, { payload }) => { + [actionTypes.acceptTermsComplete]: (state, { payload }) => { const { appUsername } = state; return { ...state, @@ -405,21 +382,21 @@ export const reducer = handleActions( } }; }, - [types.allowBlockDonationRequests]: (state, { payload }) => { + [actionTypes.allowBlockDonationRequests]: (state, { payload }) => { return { ...state, recentlyClaimedBlock: payload }; }, - [types.updateDonationFormState]: (state, { payload }) => ({ + [actionTypes.updateDonationFormState]: (state, { payload }) => ({ ...state, donationFormState: { ...state.donationFormState, ...payload } }), - [types.addDonation]: state => ({ + [actionTypes.addDonation]: state => ({ ...state, donationFormState: { ...defaultDonationFormState, processing: true } }), - [types.addDonationComplete]: state => { + [actionTypes.addDonationComplete]: state => { const { appUsername } = state; return { ...state, @@ -434,19 +411,19 @@ export const reducer = handleActions( donationFormState: { ...defaultDonationFormState, success: true } }; }, - [types.addDonationError]: (state, { payload }) => ({ + [actionTypes.addDonationError]: (state, { payload }) => ({ ...state, donationFormState: { ...defaultDonationFormState, error: payload } }), - [types.fetchUser]: state => ({ + [actionTypes.fetchUser]: state => ({ ...state, userFetchState: { ...defaultFetchState } }), - [types.fetchProfileForUser]: state => ({ + [actionTypes.fetchProfileForUser]: state => ({ ...state, userProfileFetchState: { ...defaultFetchState } }), - [types.fetchUserComplete]: ( + [actionTypes.fetchUserComplete]: ( state, { payload: { user, username, sessionMeta } } ) => ({ @@ -468,7 +445,7 @@ export const reducer = handleActions( ...sessionMeta } }), - [types.fetchUserError]: (state, { payload }) => ({ + [actionTypes.fetchUserError]: (state, { payload }) => ({ ...state, userFetchState: { pending: false, @@ -477,7 +454,7 @@ export const reducer = handleActions( error: payload } }), - [types.fetchProfileForUserComplete]: ( + [actionTypes.fetchProfileForUserComplete]: ( state, { payload: { user, username } } ) => { @@ -496,7 +473,7 @@ export const reducer = handleActions( } }; }, - [types.fetchProfileForUserError]: (state, { payload }) => ({ + [actionTypes.fetchProfileForUserError]: (state, { payload }) => ({ ...state, userProfileFetchState: { pending: false, @@ -505,37 +482,37 @@ export const reducer = handleActions( error: payload } }), - [types.onlineStatusChange]: (state, { payload: isOnline }) => ({ + [actionTypes.onlineStatusChange]: (state, { payload: isOnline }) => ({ ...state, isOnline }), - [types.closeDonationModal]: state => ({ + [actionTypes.closeDonationModal]: state => ({ ...state, showDonationModal: false }), - [types.openDonationModal]: state => ({ + [actionTypes.openDonationModal]: state => ({ ...state, showDonationModal: true }), - [types.preventBlockDonationRequests]: state => ({ + [actionTypes.preventBlockDonationRequests]: state => ({ ...state, recentlyClaimedBlock: null }), - [types.preventProgressDonationRequests]: state => ({ + [actionTypes.preventProgressDonationRequests]: state => ({ ...state, canRequestProgressDonation: false }), - [types.resetUserData]: state => ({ + [actionTypes.resetUserData]: state => ({ ...state, appUsername: '', user: {} }), - [types.showCert]: state => ({ + [actionTypes.showCert]: state => ({ ...state, showCert: {}, showCertFetchState: { ...defaultFetchState } }), - [types.showCertComplete]: (state, { payload }) => ({ + [actionTypes.showCertComplete]: (state, { payload }) => ({ ...state, showCert: payload, showCertFetchState: { @@ -544,7 +521,7 @@ export const reducer = handleActions( complete: true } }), - [types.showCertError]: (state, { payload }) => ({ + [actionTypes.showCertError]: (state, { payload }) => ({ ...state, showCert: {}, showCertFetchState: { @@ -554,7 +531,7 @@ export const reducer = handleActions( error: payload } }), - [types.submitComplete]: (state, { payload }) => { + [actionTypes.submitComplete]: (state, { payload }) => { let submittedchallenges = [{ ...payload, completedDate: Date.now() }]; if (payload.challArray) { submittedchallenges = payload.challArray; diff --git a/client/src/redux/report-user-saga.js b/client/src/redux/report-user-saga.js index 75243fba68..34906e1115 100644 --- a/client/src/redux/report-user-saga.js +++ b/client/src/redux/report-user-saga.js @@ -1,10 +1,10 @@ -import { call, put, takeEvery } from 'redux-saga/effects'; import { navigate } from 'gatsby'; +import { call, put, takeEvery } from 'redux-saga/effects'; -import { reportUserComplete, reportUserError } from './'; import { createFlashMessage } from '../components/Flash/redux'; import { postReportUser } from '../utils/ajax'; +import { reportUserComplete, reportUserError } from './'; function* reportUserSaga({ payload }) { try { diff --git a/client/src/redux/rootEpic.js b/client/src/redux/rootEpic.js index 25ed10dfd2..dd95634859 100644 --- a/client/src/redux/rootEpic.js +++ b/client/src/redux/rootEpic.js @@ -1,7 +1,7 @@ import { combineEpics } from 'redux-observable'; -import { epics as appEpics } from './'; import { epics as challengeEpics } from '../templates/Challenges/redux'; +import { epics as appEpics } from './'; const rootEpic = combineEpics(...appEpics, ...challengeEpics); diff --git a/client/src/redux/rootReducer.js b/client/src/redux/rootReducer.js index 88294fe6ae..587b52e66b 100644 --- a/client/src/redux/rootReducer.js +++ b/client/src/redux/rootReducer.js @@ -1,23 +1,23 @@ import { combineReducers } from 'redux'; -import { reducer as app, ns as appNameSpace } from './'; import { reducer as flash, ns as flashNameSpace } from '../components/Flash/redux'; -import { reducer as settings, ns as settingsNameSpace } from './settings'; import { - reducer as curriculumMap, - ns as curriculumMapNameSpace -} from '../templates/Introduction/redux'; + reducer as search, + ns as searchNameSpace +} from '../components/search/redux'; import { reducer as challenge, ns as challengeNameSpace } from '../templates/Challenges/redux'; import { - reducer as search, - ns as searchNameSpace -} from '../components/search/redux'; + reducer as curriculumMap, + ns as curriculumMapNameSpace +} from '../templates/Introduction/redux'; +import { reducer as settings, ns as settingsNameSpace } from './settings'; +import { reducer as app, ns as appNameSpace } from './'; export default combineReducers({ [appNameSpace]: app, diff --git a/client/src/redux/rootSaga.js b/client/src/redux/rootSaga.js index ccc836cf66..ec83d4a5b6 100644 --- a/client/src/redux/rootSaga.js +++ b/client/src/redux/rootSaga.js @@ -1,9 +1,9 @@ import { all } from 'redux-saga/effects'; -import errorSagas from './error-saga'; -import { sagas as appSagas } from './'; import { sagas as challengeSagas } from '../templates/Challenges/redux'; +import errorSagas from './error-saga'; import { sagas as settingsSagas } from './settings'; +import { sagas as appSagas } from './'; export default function* rootSaga() { yield all([...errorSagas, ...appSagas, ...challengeSagas, ...settingsSagas]); diff --git a/client/src/redux/settings/action-types.js b/client/src/redux/settings/action-types.js new file mode 100644 index 0000000000..a153c5568d --- /dev/null +++ b/client/src/redux/settings/action-types.js @@ -0,0 +1,19 @@ +import { createTypes, createAsyncTypes } from '../../utils/create-types'; + +export const ns = 'settings'; + +export const actionTypes = createTypes( + [ + ...createAsyncTypes('validateUsername'), + ...createAsyncTypes('submitNewAbout'), + ...createAsyncTypes('submitNewUsername'), + ...createAsyncTypes('updateMyEmail'), + ...createAsyncTypes('updateLegacyCert'), + ...createAsyncTypes('updateUserFlag'), + ...createAsyncTypes('submitProfileUI'), + ...createAsyncTypes('verifyCert'), + ...createAsyncTypes('resetProgress'), + ...createAsyncTypes('deleteAccount') + ], + ns +); diff --git a/client/src/redux/settings/danger-zone-saga.js b/client/src/redux/settings/danger-zone-saga.js index e8ebc6e372..1e1f03bc4f 100644 --- a/client/src/redux/settings/danger-zone-saga.js +++ b/client/src/redux/settings/danger-zone-saga.js @@ -1,10 +1,11 @@ import { navigate } from 'gatsby'; import { call, put, takeEvery, take } from 'redux-saga/effects'; -import { deleteAccountError, resetProgressError } from './'; -import { resetUserData, fetchUser, types as appTypes } from '../'; -import { postResetProgress, postDeleteAccount } from '../../utils/ajax'; +import { resetUserData, fetchUser } from '../'; import { createFlashMessage } from '../../components/Flash/redux'; +import { postResetProgress, postDeleteAccount } from '../../utils/ajax'; +import { actionTypes as appTypes } from '../action-types'; +import { deleteAccountError, resetProgressError } from './'; function* deleteAccountSaga() { try { diff --git a/client/src/redux/settings/index.js b/client/src/redux/settings/index.js index 9609c90c8a..be08635a29 100644 --- a/client/src/redux/settings/index.js +++ b/client/src/redux/settings/index.js @@ -1,11 +1,10 @@ import { createAction, handleActions } from 'redux-actions'; - -import { createTypes, createAsyncTypes } from '../../utils/create-types'; +import { actionTypes as types, ns } from './action-types'; import { createDangerZoneSaga } from './danger-zone-saga'; import { createSettingsSagas } from './settings-sagas'; import { createUpdateMyEmailSaga } from './update-email-saga'; -export const ns = 'settings'; +export { ns }; const defaultFetchState = { pending: false, @@ -21,21 +20,6 @@ const initialState = { } }; -export const types = createTypes( - [ - ...createAsyncTypes('validateUsername'), - ...createAsyncTypes('submitNewAbout'), - ...createAsyncTypes('submitNewUsername'), - ...createAsyncTypes('updateMyEmail'), - ...createAsyncTypes('updateUserFlag'), - ...createAsyncTypes('submitProfileUI'), - ...createAsyncTypes('verifyCert'), - ...createAsyncTypes('resetProgress'), - ...createAsyncTypes('deleteAccount') - ], - ns -); - export const sagas = [ ...createSettingsSagas(types), ...createUpdateMyEmailSaga(types), diff --git a/client/src/redux/settings/settings-sagas.js b/client/src/redux/settings/settings-sagas.js index 1472f4ec1c..1e24291848 100644 --- a/client/src/redux/settings/settings-sagas.js +++ b/client/src/redux/settings/settings-sagas.js @@ -1,5 +1,14 @@ import { call, delay, put, takeLatest, takeEvery } from 'redux-saga/effects'; +import { createFlashMessage } from '../../components/Flash/redux'; +import { + getUsernameExists, + putUpdateMyAbout, + putUpdateMyProfileUI, + putUpdateMyUsername, + putUpdateUserFlag, + putVerifyCert +} from '../../utils/ajax'; import { updateUserFlagComplete, updateUserFlagError, @@ -14,15 +23,6 @@ import { verifyCertComplete, verifyCertError } from './'; -import { - getUsernameExists, - putUpdateMyAbout, - putUpdateMyProfileUI, - putUpdateMyUsername, - putUpdateUserFlag, - putVerifyCert -} from '../../utils/ajax'; -import { createFlashMessage } from '../../components/Flash/redux'; function* submitNewAboutSaga({ payload }) { try { diff --git a/client/src/redux/settings/update-email-saga.js b/client/src/redux/settings/update-email-saga.js index b3809fde34..a1dce59b0c 100644 --- a/client/src/redux/settings/update-email-saga.js +++ b/client/src/redux/settings/update-email-saga.js @@ -1,11 +1,11 @@ import { call, put, takeEvery } from 'redux-saga/effects'; import isEmail from 'validator/lib/isEmail'; -import { updateMyEmailComplete, updateMyEmailError } from './'; import { createFlashMessage } from '../../components/Flash/redux'; import { putUserUpdateEmail } from '../../utils/ajax'; import reallyWeirdErrorMessage from '../../utils/really-weird-error-message'; +import { updateMyEmailComplete, updateMyEmailError } from './'; function* updateMyEmailSaga({ payload: email = '' }) { if (!email || !isEmail(email)) { diff --git a/client/src/redux/show-cert-saga.js b/client/src/redux/show-cert-saga.js index e352fab778..682aafe20d 100644 --- a/client/src/redux/show-cert-saga.js +++ b/client/src/redux/show-cert-saga.js @@ -1,5 +1,5 @@ -import { put, takeEvery, call } from 'redux-saga/effects'; import { navigate } from 'gatsby'; +import { put, takeEvery, call } from 'redux-saga/effects'; import { createFlashMessage } from '../components/Flash/redux'; import { getShowCert } from '../utils/ajax'; diff --git a/client/src/redux/update-complete-epic.js b/client/src/redux/update-complete-epic.js index 792a68855f..bde7029816 100644 --- a/client/src/redux/update-complete-epic.js +++ b/client/src/redux/update-complete-epic.js @@ -1,7 +1,8 @@ import { ofType } from 'redux-observable'; import { mapTo, filter } from 'rxjs/operators'; -import { types, onlineStatusChange, isOnlineSelector } from './'; +import { actionTypes as types } from './action-types'; +import { onlineStatusChange, isOnlineSelector } from './'; export default function updateCompleteEpic(action$, state$) { return action$.pipe( diff --git a/client/src/templates/Challenges/classic/ActionRow.js b/client/src/templates/Challenges/classic/ActionRow.js index 841de84751..bafbe97094 100644 --- a/client/src/templates/Challenges/classic/ActionRow.js +++ b/client/src/templates/Challenges/classic/ActionRow.js @@ -1,7 +1,7 @@ -import React from 'react'; import PropTypes from 'prop-types'; -import EditorTabs from './EditorTabs'; +import React from 'react'; import BreadCrumb from '../components/bread-crumb'; +import EditorTabs from './EditorTabs'; const propTypes = { block: PropTypes.string, diff --git a/client/src/templates/Challenges/classic/DesktopLayout.js b/client/src/templates/Challenges/classic/DesktopLayout.js index 3fa3bf0509..cdf7bf2232 100644 --- a/client/src/templates/Challenges/classic/DesktopLayout.js +++ b/client/src/templates/Challenges/classic/DesktopLayout.js @@ -1,10 +1,10 @@ +import { first } from 'lodash-es'; +import PropTypes from 'prop-types'; import React, { Component } from 'react'; import { ReflexContainer, ReflexSplitter, ReflexElement } from 'react-reflex'; -import PropTypes from 'prop-types'; -import { first } from 'lodash-es'; -import EditorTabs from './EditorTabs'; -import ActionRow from './ActionRow'; import envData from '../../../../../config/env.json'; +import ActionRow from './ActionRow'; +import EditorTabs from './EditorTabs'; const { showUpcomingChanges } = envData; diff --git a/client/src/templates/Challenges/classic/EditorTabs.js b/client/src/templates/Challenges/classic/EditorTabs.js index 112b4bb6e2..81dbedb567 100644 --- a/client/src/templates/Challenges/classic/EditorTabs.js +++ b/client/src/templates/Challenges/classic/EditorTabs.js @@ -1,6 +1,6 @@ +import PropTypes from 'prop-types'; import React, { Component } from 'react'; import { connect } from 'react-redux'; -import PropTypes from 'prop-types'; import { createSelector } from 'reselect'; import { diff --git a/client/src/templates/Challenges/classic/MobileLayout.js b/client/src/templates/Challenges/classic/MobileLayout.js index dd8d9af2d3..476020e54e 100644 --- a/client/src/templates/Challenges/classic/MobileLayout.js +++ b/client/src/templates/Challenges/classic/MobileLayout.js @@ -1,15 +1,15 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; import { TabPane, Tabs } from '@freecodecamp/react-bootstrap'; +import i18next from 'i18next'; +import PropTypes from 'prop-types'; +import React, { Component } from 'react'; import { connect } from 'react-redux'; -import ToolPanel from '../components/Tool-Panel'; -import { createStructuredSelector } from 'reselect'; -import { currentTabSelector, moveToTab } from '../redux'; import { bindActionCreators } from 'redux'; -import EditorTabs from './EditorTabs'; +import { createStructuredSelector } from 'reselect'; import envData from '../../../../../config/env.json'; -import i18next from 'i18next'; +import ToolPanel from '../components/Tool-Panel'; +import { currentTabSelector, moveToTab } from '../redux'; +import EditorTabs from './EditorTabs'; const { showUpcomingChanges } = envData; diff --git a/client/src/templates/Challenges/classic/MultifileEditor.js b/client/src/templates/Challenges/classic/MultifileEditor.js index 694fc373b4..101c2c1630 100644 --- a/client/src/templates/Challenges/classic/MultifileEditor.js +++ b/client/src/templates/Challenges/classic/MultifileEditor.js @@ -3,7 +3,6 @@ import React, { Component } from 'react'; import { connect } from 'react-redux'; import { ReflexContainer, ReflexElement, ReflexSplitter } from 'react-reflex'; import { createSelector } from 'reselect'; -import { getTargetEditor } from '../utils/getTargetEditor'; import { isDonationModalOpenSelector, userSelector } from '../../../redux'; import { canFocusEditorSelector, @@ -16,6 +15,7 @@ import { visibleEditorsSelector, updateFile } from '../redux'; +import { getTargetEditor } from '../utils/getTargetEditor'; import './editor.css'; import Editor from './editor'; diff --git a/client/src/templates/Challenges/classic/Show.tsx b/client/src/templates/Challenges/classic/Show.tsx index adbeb57751..f709ed52ff 100644 --- a/client/src/templates/Challenges/classic/Show.tsx +++ b/client/src/templates/Challenges/classic/Show.tsx @@ -2,32 +2,19 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ // Package Utilities +import { graphql } from 'gatsby'; import React, { Component } from 'react'; +import Helmet from 'react-helmet'; +import { TFunction, withTranslation } from 'react-i18next'; +import { connect } from 'react-redux'; +import Media from 'react-responsive'; import { bindActionCreators, Dispatch } from 'redux'; import { createStructuredSelector } from 'reselect'; -import { connect } from 'react-redux'; -import Helmet from 'react-helmet'; -import { graphql } from 'gatsby'; -import Media from 'react-responsive'; -import { TFunction, withTranslation } from 'react-i18next'; // Local Utilities -import LearnLayout from '../../../components/layouts/learn'; -import MultifileEditor from './MultifileEditor'; -import Preview from '../components/Preview'; -import SidePanel from '../components/Side-Panel'; -import Output from '../components/output'; -import CompletionModal from '../components/completion-modal'; -import HelpModal from '../components/HelpModal'; -import VideoModal from '../components/VideoModal'; -import ResetModal from '../components/ResetModal'; -import MobileLayout from './MobileLayout'; -import DesktopLayout from './DesktopLayout'; -import Hotkeys from '../components/Hotkeys'; -import { getGuideUrl } from '../utils'; import store from 'store'; import { challengeTypes } from '../../../../utils/challengeTypes'; -import { isContained } from '../../../utils/is-contained'; +import LearnLayout from '../../../components/layouts/learn'; import { ChallengeNodeType, ChallengeFileType, @@ -35,6 +22,17 @@ import { TestType, ResizePropsType } from '../../../redux/prop-types'; +import { isContained } from '../../../utils/is-contained'; +import ChallengeDescription from '../components/Challenge-Description'; +import HelpModal from '../components/HelpModal'; +import Hotkeys from '../components/Hotkeys'; +import Preview from '../components/Preview'; +import ResetModal from '../components/ResetModal'; +import SidePanel from '../components/Side-Panel'; +import VideoModal from '../components/VideoModal'; +import ChallengeTitle from '../components/challenge-title'; +import CompletionModal from '../components/completion-modal'; +import Output from '../components/output'; import { createFiles, challengeFilesSelector, @@ -45,8 +43,13 @@ import { challengeMounted, consoleOutputSelector, executeChallenge, - cancelTests + cancelTests, + isChallengeCompletedSelector } from '../redux'; +import { getGuideUrl } from '../utils'; +import DesktopLayout from './DesktopLayout'; +import MobileLayout from './MobileLayout'; +import MultifileEditor from './MultifileEditor'; // Styles import './classic.css'; @@ -56,7 +59,8 @@ import '../components/test-frame.css'; const mapStateToProps = createStructuredSelector({ files: challengeFilesSelector, tests: challengeTestsSelector, - output: consoleOutputSelector + output: consoleOutputSelector, + isChallengeCompleted: isChallengeCompletedSelector }); const mapDispatchToProps = (dispatch: Dispatch) => @@ -83,6 +87,7 @@ interface ShowClassicProps { files: ChallengeFileType; initConsole: (arg0: string) => void; initTests: (tests: TestType[]) => void; + isChallengeCompleted: boolean; output: string[]; pageContext: { challengeMeta: ChallengeMetaType; @@ -287,15 +292,27 @@ class ShowClassic extends Component { return ( + } + challengeTitle={ + + {title} + + } className='full-height' - description={description} guideUrl={getGuideUrl({ forumTopicId, title })} - instructions={instructions} instructionsPanelRef={this.instructionsPanelRef} showToolPanel={showToolPanel} - superBlock={superBlock} - title={title} - translationPending={translationPending} videoUrl={this.getVideoUrl()} /> ); diff --git a/client/src/templates/Challenges/classic/editor.tsx b/client/src/templates/Challenges/classic/editor.tsx index 2003faf70b..b797bcd65e 100644 --- a/client/src/templates/Challenges/classic/editor.tsx +++ b/client/src/templates/Challenges/classic/editor.tsx @@ -1,3 +1,12 @@ +import Loadable from '@loadable/component'; +// eslint-disable-next-line import/no-duplicates +import type * as monacoEditor from 'monaco-editor/esm/vs/editor/editor.api'; +import type { + IRange, + editor, + Range as RangeType + // eslint-disable-next-line import/no-duplicates +} from 'monaco-editor/esm/vs/editor/editor.api'; import React, { useEffect, Suspense, @@ -7,8 +16,17 @@ import React, { } from 'react'; import { connect } from 'react-redux'; import { createSelector } from 'reselect'; -import Loadable from '@loadable/component'; +import { Loader } from '../../../components/helpers'; +import { userSelector, isDonationModalOpenSelector } from '../../../redux'; +import { + ChallengeFileType, + DimensionsType, + ExtTypes, + FileKeyTypes, + ResizePropsType, + TestType +} from '../../../redux/prop-types'; import { canFocusEditorSelector, consoleOutputSelector, @@ -21,25 +39,6 @@ import { challengeTestsSelector, submitChallenge } from '../redux'; -import { userSelector, isDonationModalOpenSelector } from '../../../redux'; -import { Loader } from '../../../components/helpers'; -import { - ChallengeFileType, - DimensionsType, - ExtTypes, - FileKeyTypes, - ResizePropsType, - TestType -} from '../../../redux/prop-types'; - -// eslint-disable-next-line import/no-duplicates -import type * as monacoEditor from 'monaco-editor/esm/vs/editor/editor.api'; -import type { - IRange, - editor, - Range as RangeType - // eslint-disable-next-line import/no-duplicates -} from 'monaco-editor/esm/vs/editor/editor.api'; import './editor.css'; diff --git a/client/src/templates/Challenges/codeally/show.tsx b/client/src/templates/Challenges/codeally/show.tsx index a0a38d25f3..70de9bbd59 100644 --- a/client/src/templates/Challenges/codeally/show.tsx +++ b/client/src/templates/Challenges/codeally/show.tsx @@ -1,11 +1,11 @@ /* eslint-disable max-len */ // Package Utilities -import React, { Component } from 'react'; -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; -import Helmet from 'react-helmet'; import { graphql } from 'gatsby'; +import React, { Component } from 'react'; +import Helmet from 'react-helmet'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; import type { Dispatch } from 'redux'; // Local Utilities diff --git a/client/src/templates/Challenges/components/HelpModal.js b/client/src/templates/Challenges/components/HelpModal.js index 1fc9435708..1e84b29fd0 100644 --- a/client/src/templates/Challenges/components/HelpModal.js +++ b/client/src/templates/Challenges/components/HelpModal.js @@ -1,13 +1,13 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; import { Button, Modal } from '@freecodecamp/react-bootstrap'; +import PropTypes from 'prop-types'; +import React, { Component } from 'react'; import { Trans, withTranslation } from 'react-i18next'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; -import { createQuestion, closeModal, isHelpModalOpenSelector } from '../redux'; -import { executeGA } from '../../../redux'; import envData from '../../../../../config/env.json'; +import { executeGA } from '../../../redux'; +import { createQuestion, closeModal, isHelpModalOpenSelector } from '../redux'; import './help-modal.css'; diff --git a/client/src/templates/Challenges/components/Hotkeys.tsx b/client/src/templates/Challenges/components/Hotkeys.tsx index 5debfd2f25..0d2a2a94d6 100644 --- a/client/src/templates/Challenges/components/Hotkeys.tsx +++ b/client/src/templates/Challenges/components/Hotkeys.tsx @@ -1,8 +1,8 @@ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ +import { navigate } from 'gatsby'; import React from 'react'; import { HotKeys, GlobalHotKeys } from 'react-hotkeys'; -import { navigate } from 'gatsby'; import { connect } from 'react-redux'; import { createSelector } from 'reselect'; diff --git a/client/src/templates/Challenges/components/Preview.js b/client/src/templates/Challenges/components/Preview.js index 869e4aa0bc..87f476e764 100644 --- a/client/src/templates/Challenges/components/Preview.js +++ b/client/src/templates/Challenges/components/Preview.js @@ -1,8 +1,8 @@ -import React, { Component } from 'react'; -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; import PropTypes from 'prop-types'; +import React, { Component } from 'react'; import { withTranslation } from 'react-i18next'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; import { previewMounted } from '../redux'; diff --git a/client/src/templates/Challenges/components/ResetModal.tsx b/client/src/templates/Challenges/components/ResetModal.tsx index 64071b4541..c97313c968 100644 --- a/client/src/templates/Challenges/components/ResetModal.tsx +++ b/client/src/templates/Challenges/components/ResetModal.tsx @@ -1,14 +1,14 @@ // Package Utilities -import React from 'react'; -import { bindActionCreators, Dispatch } from 'redux'; -import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; import { Button, Modal } from '@freecodecamp/react-bootstrap'; +import React from 'react'; import { useTranslation } from 'react-i18next'; +import { connect } from 'react-redux'; +import { bindActionCreators, Dispatch } from 'redux'; +import { createSelector } from 'reselect'; // Local Utilities -import { isResetModalOpenSelector, closeModal, resetChallenge } from '../redux'; import { executeGA } from '../../../redux'; +import { isResetModalOpenSelector, closeModal, resetChallenge } from '../redux'; // Styles import './reset-modal.css'; diff --git a/client/src/templates/Challenges/components/Side-Panel.js b/client/src/templates/Challenges/components/Side-Panel.js index bba4137a10..f54b4e775b 100644 --- a/client/src/templates/Challenges/components/Side-Panel.js +++ b/client/src/templates/Challenges/components/Side-Panel.js @@ -1,38 +1,27 @@ -import React, { Component } from 'react'; import PropTypes from 'prop-types'; +import React, { Component } from 'react'; import { connect } from 'react-redux'; -import ChallengeTitle from './challenge-title'; -import ChallengeDescription from './Challenge-Description'; -import ToolPanel from './Tool-Panel'; -import TestSuite from './Test-Suite'; - -import { challengeTestsSelector, isChallengeCompletedSelector } from '../redux'; import { createSelector } from 'reselect'; -import './side-panel.css'; import { mathJaxScriptLoader } from '../../../utils/script-loaders'; +import { challengeTestsSelector } from '../redux'; +import TestSuite from './Test-Suite'; +import ToolPanel from './Tool-Panel'; -const mapStateToProps = createSelector( - isChallengeCompletedSelector, - challengeTestsSelector, - (isChallengeCompleted, tests) => ({ - isChallengeCompleted, - tests - }) -); +import './side-panel.css'; + +const mapStateToProps = createSelector(challengeTestsSelector, tests => ({ + tests +})); const propTypes = { block: PropTypes.string, - description: PropTypes.string, + challengeDescription: PropTypes.element.isRequired, + challengeTitle: PropTypes.element.isRequired, guideUrl: PropTypes.string, - instructions: PropTypes.string, instructionsPanelRef: PropTypes.any.isRequired, - isChallengeCompleted: PropTypes.bool, showToolPanel: PropTypes.bool, - superBlock: PropTypes.string, tests: PropTypes.arrayOf(PropTypes.object), - title: PropTypes.string, - translationPending: PropTypes.bool.isRequired, videoUrl: PropTypes.string }; @@ -68,20 +57,8 @@ export class SidePanel extends Component { } render() { - const { - block, - title, - description, - instructions, - instructionsPanelRef, - isChallengeCompleted, - guideUrl, - tests, - showToolPanel, - superBlock, - translationPending, - videoUrl - } = this.props; + const { instructionsPanelRef, guideUrl, tests, showToolPanel, videoUrl } = + this.props; return (
-
- - {title} - - -
+ {this.props.challengeTitle} + {this.props.challengeDescription} {showToolPanel && }
diff --git a/client/src/templates/Challenges/components/Test-Suite.js b/client/src/templates/Challenges/components/Test-Suite.js index 903d2c64ab..74134981e5 100644 --- a/client/src/templates/Challenges/components/Test-Suite.js +++ b/client/src/templates/Challenges/components/Test-Suite.js @@ -1,8 +1,8 @@ -import React from 'react'; import PropTypes from 'prop-types'; +import React from 'react'; -import GreenPass from '../../../assets/icons/green-pass'; import Fail from '../../../assets/icons/fail'; +import GreenPass from '../../../assets/icons/green-pass'; import Initial from '../../../assets/icons/initial'; import './test-suite.css'; diff --git a/client/src/templates/Challenges/components/Tool-Panel.js b/client/src/templates/Challenges/components/Tool-Panel.js index 23afc5e5e7..4c20bac207 100644 --- a/client/src/templates/Challenges/components/Tool-Panel.js +++ b/client/src/templates/Challenges/components/Tool-Panel.js @@ -1,14 +1,13 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; -import { useTranslation } from 'react-i18next'; - import { Button, DropdownButton, MenuItem } from '@freecodecamp/react-bootstrap'; +import PropTypes from 'prop-types'; +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; import './tool-panel.css'; import { openModal, executeChallenge } from '../redux'; diff --git a/client/src/templates/Challenges/components/VideoModal.js b/client/src/templates/Challenges/components/VideoModal.js index e67cc44e95..791b285c9d 100644 --- a/client/src/templates/Challenges/components/VideoModal.js +++ b/client/src/templates/Challenges/components/VideoModal.js @@ -1,12 +1,12 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; import { Modal } from '@freecodecamp/react-bootstrap'; +import PropTypes from 'prop-types'; +import React, { Component } from 'react'; import { withTranslation } from 'react-i18next'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; -import { closeModal, isVideoModalOpenSelector } from '../redux'; import { executeGA } from '../../../redux'; +import { closeModal, isVideoModalOpenSelector } from '../redux'; import './video-modal.css'; diff --git a/client/src/templates/Challenges/components/bread-crumb.tsx b/client/src/templates/Challenges/components/bread-crumb.tsx index d21dd8dfe0..2555c15f72 100644 --- a/client/src/templates/Challenges/components/bread-crumb.tsx +++ b/client/src/templates/Challenges/components/bread-crumb.tsx @@ -1,8 +1,8 @@ +import i18next from 'i18next'; import React from 'react'; import { Link } from '../../../components/helpers/index'; import './challenge-title.css'; -import i18next from 'i18next'; interface BreadCrumbProps { block: string; diff --git a/client/src/templates/Challenges/components/challenge-title.tsx b/client/src/templates/Challenges/components/challenge-title.tsx index a263102093..791e04ec89 100644 --- a/client/src/templates/Challenges/components/challenge-title.tsx +++ b/client/src/templates/Challenges/components/challenge-title.tsx @@ -1,12 +1,11 @@ -import React from 'react'; -import { Link } from '../../../components/helpers/index'; import i18next from 'i18next'; +import React from 'react'; +import GreenPass from '../../../assets/icons/green-pass'; +import { Link } from '../../../components/helpers/index'; +import BreadCrumb from './bread-crumb'; import './challenge-title.css'; -import GreenPass from '../../../assets/icons/green-pass'; -import BreadCrumb from './bread-crumb'; - interface ChallengeTitleProps { block: string; children: string; diff --git a/client/src/templates/Challenges/components/completion-modal-body.test.tsx b/client/src/templates/Challenges/components/completion-modal-body.test.tsx index 4744c9c801..af1d8fd617 100644 --- a/client/src/templates/Challenges/components/completion-modal-body.test.tsx +++ b/client/src/templates/Challenges/components/completion-modal-body.test.tsx @@ -1,5 +1,5 @@ -import React from 'react'; import { render, fireEvent, screen } from '@testing-library/react'; +import React from 'react'; import CompletionModalBody from './completion-modal-body'; diff --git a/client/src/templates/Challenges/components/completion-modal-body.tsx b/client/src/templates/Challenges/components/completion-modal-body.tsx index a1b4196959..7352263609 100644 --- a/client/src/templates/Challenges/components/completion-modal-body.tsx +++ b/client/src/templates/Challenges/components/completion-modal-body.tsx @@ -1,7 +1,7 @@ -import React, { PureComponent } from 'react'; import BezierEasing from 'bezier-easing'; -import GreenPass from '../../../assets/icons/green-pass'; +import React, { PureComponent } from 'react'; import { TFunction, withTranslation } from 'react-i18next'; +import GreenPass from '../../../assets/icons/green-pass'; interface CompletionModalBodyProps { block: string; diff --git a/client/src/templates/Challenges/components/completion-modal.tsx b/client/src/templates/Challenges/components/completion-modal.tsx index 40fe2d9c2d..e0339c186a 100644 --- a/client/src/templates/Challenges/components/completion-modal.tsx +++ b/client/src/templates/Challenges/components/completion-modal.tsx @@ -1,21 +1,23 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/restrict-template-expressions */ -import React, { Component } from 'react'; -import { noop } from 'lodash-es'; -import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; import { Button, Modal } from '@freecodecamp/react-bootstrap'; import { useStaticQuery, graphql } from 'gatsby'; +import { noop } from 'lodash-es'; +import React, { Component } from 'react'; import { TFunction, withTranslation } from 'react-i18next'; +import { connect } from 'react-redux'; import { Dispatch } from 'redux'; +import { createSelector } from 'reselect'; -import Login from '../../../components/Header/components/Login'; -import CompletionModalBody from './completion-modal-body'; import { dasherize } from '../../../../../utils/slugs'; +import Login from '../../../components/Header/components/Login'; +import { + isSignedInSelector, + executeGA, + allowBlockDonationRequests +} from '../../../redux'; import { AllChallengeNodeType } from '../../../redux/prop-types'; -import './completion-modal.css'; - import { closeModal, submitChallenge, @@ -25,12 +27,9 @@ import { challengeFilesSelector, challengeMetaSelector } from '../redux'; +import CompletionModalBody from './completion-modal-body'; -import { - isSignedInSelector, - executeGA, - allowBlockDonationRequests -} from '../../../redux'; +import './completion-modal.css'; const mapStateToProps = createSelector( challengeFilesSelector, diff --git a/client/src/templates/Challenges/components/output.tsx b/client/src/templates/Challenges/components/output.tsx index 10af12bf36..7c49938817 100644 --- a/client/src/templates/Challenges/components/output.tsx +++ b/client/src/templates/Challenges/components/output.tsx @@ -1,8 +1,8 @@ +import { isEmpty } from 'lodash-es'; import React, { Component } from 'react'; import sanitizeHtml from 'sanitize-html'; import './output.css'; -import { isEmpty } from 'lodash-es'; interface OutputProps { defaultOutput: string; diff --git a/client/src/templates/Challenges/components/prism-formatted.tsx b/client/src/templates/Challenges/components/prism-formatted.tsx index d9026c2a34..89a117eebc 100644 --- a/client/src/templates/Challenges/components/prism-formatted.tsx +++ b/client/src/templates/Challenges/components/prism-formatted.tsx @@ -1,5 +1,5 @@ -import React, { Component } from 'react'; import Prism from 'prismjs'; +import React, { Component } from 'react'; interface PrismFormattedProps { className?: string; diff --git a/client/src/templates/Challenges/projects/backend/Show.tsx b/client/src/templates/Challenges/projects/backend/Show.tsx index b67d49557a..9df8b2e481 100644 --- a/client/src/templates/Challenges/projects/backend/Show.tsx +++ b/client/src/templates/Challenges/projects/backend/Show.tsx @@ -2,15 +2,30 @@ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ // Package Utilities -import React, { Component } from 'react'; import { Grid, Col, Row } from '@freecodecamp/react-bootstrap'; -import { createSelector } from 'reselect'; -import { connect } from 'react-redux'; import { graphql } from 'gatsby'; +import React, { Component } from 'react'; import Helmet from 'react-helmet'; import { TFunction, withTranslation } from 'react-i18next'; +import { connect } from 'react-redux'; +import { createSelector } from 'reselect'; // Local Utilities +import Spacer from '../../../../components/helpers/spacer'; +import LearnLayout from '../../../../components/layouts/learn'; +import { isSignedInSelector } from '../../../../redux'; +import { + ChallengeNodeType, + ChallengeMetaType, + TestType +} from '../../../../redux/prop-types'; +import ChallengeDescription from '../../components/Challenge-Description'; +import HelpModal from '../../components/HelpModal'; +import Hotkeys from '../../components/Hotkeys'; +import TestSuite from '../../components/Test-Suite'; +import ChallengeTitle from '../../components/challenge-title'; +import CompletionModal from '../../components/completion-modal'; +import Output from '../../components/output'; import { executeChallenge, challengeMounted, @@ -23,23 +38,8 @@ import { updateSolutionFormValues } from '../../redux'; import { getGuideUrl } from '../../utils'; -import LearnLayout from '../../../../components/layouts/learn'; -import ChallengeTitle from '../../components/challenge-title'; -import ChallengeDescription from '../../components/Challenge-Description'; -import TestSuite from '../../components/Test-Suite'; -import Output from '../../components/output'; -import CompletionModal from '../../components/completion-modal'; -import HelpModal from '../../components/HelpModal'; -import ProjectToolPanel from '../tool-panel'; import SolutionForm from '../solution-form'; -import Spacer from '../../../../components/helpers/spacer'; -import { - ChallengeNodeType, - ChallengeMetaType, - TestType -} from '../../../../redux/prop-types'; -import { isSignedInSelector } from '../../../../redux'; -import Hotkeys from '../../components/Hotkeys'; +import ProjectToolPanel from '../tool-panel'; // Styles import '../../components/test-frame.css'; diff --git a/client/src/templates/Challenges/projects/frontend/Show.tsx b/client/src/templates/Challenges/projects/frontend/Show.tsx index 88b44f6e9f..9b8d912a42 100644 --- a/client/src/templates/Challenges/projects/frontend/Show.tsx +++ b/client/src/templates/Challenges/projects/frontend/Show.tsx @@ -1,21 +1,28 @@ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ // Package Utilities -import React, { Component } from 'react'; import { Grid, Col, Row } from '@freecodecamp/react-bootstrap'; -import { connect } from 'react-redux'; -import { bindActionCreators } from 'redux'; import { graphql } from 'gatsby'; +import React, { Component } from 'react'; import Helmet from 'react-helmet'; import { TFunction, withTranslation } from 'react-i18next'; -import { createSelector } from 'reselect'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; import type { Dispatch } from 'redux'; +import { createSelector } from 'reselect'; // Local Utilities +import Spacer from '../../../../components/helpers/spacer'; +import LearnLayout from '../../../../components/layouts/learn'; import { ChallengeNodeType, ChallengeMetaType } from '../../../../redux/prop-types'; +import ChallengeDescription from '../../components/Challenge-Description'; +import HelpModal from '../../components/HelpModal'; +import Hotkeys from '../../components/Hotkeys'; +import ChallengeTitle from '../../components/challenge-title'; +import CompletionModal from '../../components/completion-modal'; import { challengeMounted, isChallengeCompletedSelector, @@ -24,15 +31,8 @@ import { updateSolutionFormValues } from '../../redux'; import { getGuideUrl } from '../../utils'; -import LearnLayout from '../../../../components/layouts/learn'; -import ChallengeTitle from '../../components/challenge-title'; -import ChallengeDescription from '../../components/Challenge-Description'; -import Spacer from '../../../../components/helpers/spacer'; import SolutionForm from '../solution-form'; import ProjectToolPanel from '../tool-panel'; -import CompletionModal from '../../components/completion-modal'; -import HelpModal from '../../components/HelpModal'; -import Hotkeys from '../../components/Hotkeys'; // Redux Setup const mapStateToProps = createSelector( diff --git a/client/src/templates/Challenges/projects/solution-form.tsx b/client/src/templates/Challenges/projects/solution-form.tsx index 0de27a09b0..f3cfe8f06e 100644 --- a/client/src/templates/Challenges/projects/solution-form.tsx +++ b/client/src/templates/Challenges/projects/solution-form.tsx @@ -2,13 +2,13 @@ import React, { Component } from 'react'; import { withTranslation } from 'react-i18next'; import type { WithTranslation } from 'react-i18next'; -import { Form } from '../../../components/formHelpers'; import { backend, backEndProject, frontEndProject, pythonProject } from '../../../../utils/challengeTypes'; +import { Form } from '../../../components/formHelpers'; interface SubmitProps { isShouldCompletionModalOpen: boolean; diff --git a/client/src/templates/Challenges/projects/tool-panel.tsx b/client/src/templates/Challenges/projects/tool-panel.tsx index 17ed31727a..b058ef5032 100644 --- a/client/src/templates/Challenges/projects/tool-panel.tsx +++ b/client/src/templates/Challenges/projects/tool-panel.tsx @@ -1,8 +1,8 @@ -import React, { Component } from 'react'; -import { bindActionCreators, Dispatch } from 'redux'; -import { connect } from 'react-redux'; import { Button } from '@freecodecamp/react-bootstrap'; +import React, { Component } from 'react'; import { TFunction, withTranslation } from 'react-i18next'; +import { connect } from 'react-redux'; +import { bindActionCreators, Dispatch } from 'redux'; import { openModal } from '../redux'; diff --git a/client/src/templates/Challenges/rechallenge/transformers.js b/client/src/templates/Challenges/rechallenge/transformers.js index a3926f9b8a..ba1a5d2cdb 100644 --- a/client/src/templates/Challenges/rechallenge/transformers.js +++ b/client/src/templates/Challenges/rechallenge/transformers.js @@ -1,3 +1,4 @@ +import protect from '@freecodecamp/loop-protect'; import { attempt, cond, @@ -10,8 +11,9 @@ import { stubTrue } from 'lodash-es'; -import protect from '@freecodecamp/loop-protect'; - +// the config files are created during the build, but not before linting +// eslint-disable-next-line import/no-unresolved +import sassData from '../../../../../config/client/sass-compile.json'; import { transformContents, transformHeadTailAndContents, @@ -21,10 +23,6 @@ import { } from '../../../../../utils/polyvinyl'; import createWorker from '../utils/worker-executor'; -// the config files are created during the build, but not before linting -// eslint-disable-next-line import/no-unresolved -import sassData from '../../../../../config/client/sass-compile.json'; - const { filename: sassCompile } = sassData; const protectTimeout = 100; diff --git a/client/src/templates/Challenges/redux/action-types.js b/client/src/templates/Challenges/redux/action-types.js new file mode 100644 index 0000000000..156b9e40db --- /dev/null +++ b/client/src/templates/Challenges/redux/action-types.js @@ -0,0 +1,48 @@ +import { createTypes } from '../../../utils/create-types'; + +export const ns = 'challenge'; + +export const actionTypes = createTypes( + [ + 'createFiles', + 'createQuestion', + 'initTests', + 'initConsole', + 'initLogs', + 'updateConsole', + 'updateChallengeMeta', + 'updateFile', + 'updateJSEnabled', + 'updateSolutionFormValues', + 'updateSuccessMessage', + 'updateTests', + 'updateLogs', + 'cancelTests', + + 'logsToConsole', + + 'lockCode', + 'unlockCode', + 'disableBuildOnError', + 'storedCodeFound', + 'noStoredCodeFound', + 'saveEditorContent', + + 'closeModal', + 'openModal', + + 'previewMounted', + 'challengeMounted', + 'checkChallenge', + 'executeChallenge', + 'resetChallenge', + 'submitChallenge', + + 'moveToTab', + + 'setEditorFocusability', + 'toggleVisibleEditor', + 'setAccessibilityMode' + ], + ns +); diff --git a/client/src/templates/Challenges/redux/code-lock-epic.js b/client/src/templates/Challenges/redux/code-lock-epic.js index e9a2e4b362..5ae2372018 100644 --- a/client/src/templates/Challenges/redux/code-lock-epic.js +++ b/client/src/templates/Challenges/redux/code-lock-epic.js @@ -1,9 +1,10 @@ -import { map } from 'rxjs/operators'; import { ofType } from 'redux-observable'; -import { types, unlockCode } from './'; +import { map } from 'rxjs/operators'; +import { actionTypes } from './action-types'; +import { unlockCode } from './'; function codeLockEpic(action$) { - return action$.pipe(ofType(types.updateFile), map(unlockCode)); + return action$.pipe(ofType(actionTypes.updateFile), map(unlockCode)); } export default codeLockEpic; diff --git a/client/src/templates/Challenges/redux/code-storage-epic.js b/client/src/templates/Challenges/redux/code-storage-epic.js index 0cd2163f98..e5c307ce11 100644 --- a/client/src/templates/Challenges/redux/code-storage-epic.js +++ b/client/src/templates/Challenges/redux/code-storage-epic.js @@ -1,10 +1,14 @@ +import { combineEpics, ofType } from 'redux-observable'; import { of } from 'rxjs'; import { filter, switchMap, map, tap, ignoreElements } from 'rxjs/operators'; -import { combineEpics, ofType } from 'redux-observable'; import store from 'store'; +import { setContent, isPoly } from '../../../../../utils/polyvinyl'; +import { createFlashMessage } from '../../../components/Flash/redux'; +import { actionTypes as appTypes } from '../../../redux/action-types'; + +import { actionTypes } from './action-types'; import { - types, storedCodeFound, noStoredCodeFound, isCodeLockedSelector, @@ -12,12 +16,6 @@ import { challengeMetaSelector } from './'; -import { types as appTypes } from '../../../redux'; - -import { setContent, isPoly } from '../../../../../utils/polyvinyl'; - -import { createFlashMessage } from '../../../components/Flash/redux'; - const legacyPrefixes = [ 'Bonfire: ', 'Waypoint: ', @@ -64,7 +62,7 @@ function isFilesAllPoly(files) { function clearCodeEpic(action$, state$) { return action$.pipe( - ofType(appTypes.submitComplete, types.resetChallenge), + ofType(appTypes.submitComplete, actionTypes.resetChallenge), tap(() => { const { id } = challengeMetaSelector(state$.value); store.remove(id); @@ -75,7 +73,7 @@ function clearCodeEpic(action$, state$) { function saveCodeEpic(action$, state$) { return action$.pipe( - ofType(types.executeChallenge, types.saveEditorContent), + ofType(actionTypes.executeChallenge, actionTypes.saveEditorContent), // do not save challenge if code is locked filter(() => !isCodeLockedSelector(state$.value)), map(action => { @@ -95,7 +93,7 @@ function saveCodeEpic(action$, state$) { return { ...action, error: true }; } }), - ofType(types.saveEditorContent), + ofType(actionTypes.saveEditorContent), switchMap(({ error }) => of( createFlashMessage({ @@ -112,7 +110,7 @@ function saveCodeEpic(action$, state$) { function loadCodeEpic(action$, state$) { return action$.pipe( - ofType(types.challengeMounted), + ofType(actionTypes.challengeMounted), filter(() => { const files = challengeFilesSelector(state$.value); return Object.keys(files).length > 0; diff --git a/client/src/templates/Challenges/redux/completion-epic.js b/client/src/templates/Challenges/redux/completion-epic.js index 699d353fc6..0ba44814f2 100644 --- a/client/src/templates/Challenges/redux/completion-epic.js +++ b/client/src/templates/Challenges/redux/completion-epic.js @@ -1,3 +1,5 @@ +import { navigate } from 'gatsby'; +import { ofType } from 'redux-observable'; import { of, empty } from 'rxjs'; import { switchMap, @@ -7,18 +9,8 @@ import { filter, finalize } from 'rxjs/operators'; -import { ofType } from 'redux-observable'; -import { navigate } from 'gatsby'; -import { - projectFormValuesSelector, - types, - challengeMetaSelector, - challengeTestsSelector, - closeModal, - challengeFilesSelector, - updateSolutionFormValues -} from './'; +import { challengeTypes, submitTypes } from '../../../../utils/challengeTypes'; import { userSelector, isSignedInSelector, @@ -28,9 +20,17 @@ import { usernameSelector } from '../../../redux'; -import postUpdate$ from '../utils/postUpdate$'; -import { challengeTypes, submitTypes } from '../../../../utils/challengeTypes'; import { getVerifyCanClaimCert } from '../../../utils/ajax'; +import postUpdate$ from '../utils/postUpdate$'; +import { actionTypes } from './action-types'; +import { + projectFormValuesSelector, + challengeMetaSelector, + challengeTestsSelector, + closeModal, + challengeFilesSelector, + updateSolutionFormValues +} from './'; function postChallenge(update, username) { const saveChallenge = postUpdate$(update).pipe( @@ -57,11 +57,11 @@ function submitModern(type, state) { challengeType === 11 || (tests.length > 0 && tests.every(test => test.pass && !test.err)) ) { - if (type === types.checkChallenge) { + if (type === actionTypes.checkChallenge) { return of({ type: 'this was a check challenge' }); } - if (type === types.submitChallenge) { + if (type === actionTypes.submitChallenge) { const { id } = challengeMetaSelector(state); const files = challengeFilesSelector(state); const { username } = userSelector(state); @@ -80,7 +80,7 @@ function submitModern(type, state) { } function submitProject(type, state) { - if (type === types.checkChallenge) { + if (type === actionTypes.checkChallenge) { return empty(); } @@ -104,7 +104,7 @@ function submitProject(type, state) { function submitBackendChallenge(type, state) { const tests = challengeTestsSelector(state); if (tests.length > 0 && tests.every(test => test.pass && !test.err)) { - if (type === types.submitChallenge) { + if (type === actionTypes.submitChallenge) { const { id } = challengeMetaSelector(state); const { username } = userSelector(state); const { @@ -131,7 +131,7 @@ const submitters = { export default function completionEpic(action$, state$) { return action$.pipe( - ofType(types.submitChallenge), + ofType(actionTypes.submitChallenge), switchMap(({ type }) => { const state = state$.value; const meta = challengeMetaSelector(state); diff --git a/client/src/templates/Challenges/redux/create-question-epic.js b/client/src/templates/Challenges/redux/create-question-epic.js index c5c230fdb5..143c7fc6ac 100644 --- a/client/src/templates/Challenges/redux/create-question-epic.js +++ b/client/src/templates/Challenges/redux/create-question-epic.js @@ -1,16 +1,16 @@ import dedent from 'dedent'; +import i18next from 'i18next'; import { ofType } from 'redux-observable'; +import { tap, mapTo } from 'rxjs/operators'; +import envData from '../../../../../config/env.json'; import { - types, closeModal, challengeFilesSelector, challengeMetaSelector, projectFormValuesSelector } from '../redux'; -import { tap, mapTo } from 'rxjs/operators'; import { transformEditorLink } from '../utils'; -import envData from '../../../../../config/env.json'; -import i18next from 'i18next'; +import { actionTypes } from './action-types'; const { forumLocation } = envData; @@ -29,7 +29,7 @@ function filesToMarkdown(files = {}) { function createQuestionEpic(action$, state$, { window }) { return action$.pipe( - ofType(types.createQuestion), + ofType(actionTypes.createQuestion), tap(() => { const state = state$.value; const files = challengeFilesSelector(state); diff --git a/client/src/templates/Challenges/redux/execute-challenge-saga.js b/client/src/templates/Challenges/redux/execute-challenge-saga.js index 0c4e643cef..a9aae70d5a 100644 --- a/client/src/templates/Challenges/redux/execute-challenge-saga.js +++ b/client/src/templates/Challenges/redux/execute-challenge-saga.js @@ -1,3 +1,6 @@ +import i18next from 'i18next'; +import { escape } from 'lodash-es'; +import { channel } from 'redux-saga'; import { delay, put, @@ -10,10 +13,17 @@ import { take, cancel } from 'redux-saga/effects'; -import { channel } from 'redux-saga'; -import { escape } from 'lodash-es'; -import i18next from 'i18next'; +import { + buildChallenge, + canBuildChallenge, + getTestRunner, + challengeHasPreview, + updatePreview, + isJavaScriptChallenge, + isLoopProtected +} from '../utils/build'; +import { actionTypes } from './action-types'; import { challengeDataSelector, challengeMetaSelector, @@ -26,20 +36,9 @@ import { updateTests, openModal, isBuildEnabledSelector, - disableBuildOnError, - types + disableBuildOnError } from './'; -import { - buildChallenge, - canBuildChallenge, - getTestRunner, - challengeHasPreview, - updatePreview, - isJavaScriptChallenge, - isLoopProtected -} from '../utils/build'; - // How long before bailing out of a preview. const previewTimeout = 2500; let previewTask; @@ -52,7 +51,7 @@ export function* executeCancellableChallengeSaga(payload) { const task = yield fork(executeChallengeSaga, payload); previewTask = yield fork(previewChallengeSaga, { flushLogs: false }); - yield take(types.cancelTests); + yield take(actionTypes.cancelTests); yield cancel(task); } diff --git a/client/src/templates/Challenges/redux/index.js b/client/src/templates/Challenges/redux/index.js index b63d31af36..351c60e052 100644 --- a/client/src/templates/Challenges/redux/index.js +++ b/client/src/templates/Challenges/redux/index.js @@ -1,22 +1,20 @@ -import { createAction, handleActions } from 'redux-actions'; import { isEmpty } from 'lodash-es'; +import { createAction, handleActions } from 'redux-actions'; -import { createTypes } from '../../../utils/create-types'; -import { createPoly } from '../../../../../utils/polyvinyl'; import { getLines } from '../../../../../utils/get-lines'; -import completionEpic from './completion-epic'; -import codeLockEpic from './code-lock-epic'; -import createQuestionEpic from './create-question-epic'; -import codeStorageEpic from './code-storage-epic'; - -import { createExecuteChallengeSaga } from './execute-challenge-saga'; -import { createCurrentChallengeSaga } from './current-challenge-saga'; +import { createPoly } from '../../../../../utils/polyvinyl'; import { challengeTypes } from '../../../../utils/challengeTypes'; -import { getTargetEditor } from '../utils/getTargetEditor'; import { completedChallengesSelector } from '../../../redux'; +import { getTargetEditor } from '../utils/getTargetEditor'; +import { actionTypes, ns } from './action-types'; +import codeLockEpic from './code-lock-epic'; +import codeStorageEpic from './code-storage-epic'; +import completionEpic from './completion-epic'; +import createQuestionEpic from './create-question-epic'; +import { createCurrentChallengeSaga } from './current-challenge-saga'; +import { createExecuteChallengeSaga } from './execute-challenge-saga'; -export const ns = 'challenge'; -export const backendNS = 'backendChallenge'; +export { ns }; const initialState = { canFocusEditor: true, @@ -47,51 +45,6 @@ const initialState = { successMessage: 'Happy Coding!' }; -export const types = createTypes( - [ - 'createFiles', - 'createQuestion', - 'initTests', - 'initConsole', - 'initLogs', - 'updateConsole', - 'updateChallengeMeta', - 'updateFile', - 'updateJSEnabled', - 'updateSolutionFormValues', - 'updateSuccessMessage', - 'updateTests', - 'updateLogs', - 'cancelTests', - - 'logsToConsole', - - 'lockCode', - 'unlockCode', - 'disableBuildOnError', - 'storedCodeFound', - 'noStoredCodeFound', - 'saveEditorContent', - - 'closeModal', - 'openModal', - - 'previewMounted', - 'challengeMounted', - 'checkChallenge', - 'executeChallenge', - 'resetChallenge', - 'submitChallenge', - - 'moveToTab', - - 'setEditorFocusability', - 'toggleVisibleEditor', - 'setAccessibilityMode' - ], - ns -); - export const epics = [ codeLockEpic, completionEpic, @@ -100,73 +53,87 @@ export const epics = [ ]; export const sagas = [ - ...createExecuteChallengeSaga(types), - ...createCurrentChallengeSaga(types) + ...createExecuteChallengeSaga(actionTypes), + ...createCurrentChallengeSaga(actionTypes) ]; // TODO: can createPoly handle editable region, rather than separating it? -export const createFiles = createAction(types.createFiles, challengeFiles => - Object.keys(challengeFiles) - .filter(key => challengeFiles[key]) - .map(key => challengeFiles[key]) - .reduce( - (challengeFiles, file) => ({ - ...challengeFiles, - [file.key]: { - ...createPoly(file), - seed: file.contents.slice(), - editableContents: getLines( - file.contents, - file.editableRegionBoundaries - ), - seedEditableRegionBoundaries: file.editableRegionBoundaries.slice() - } - }), - {} - ) +export const createFiles = createAction( + actionTypes.createFiles, + challengeFiles => + Object.keys(challengeFiles) + .filter(key => challengeFiles[key]) + .map(key => challengeFiles[key]) + .reduce( + (challengeFiles, file) => ({ + ...challengeFiles, + [file.key]: { + ...createPoly(file), + seed: file.contents.slice(), + editableContents: getLines( + file.contents, + file.editableRegionBoundaries + ), + seedEditableRegionBoundaries: file.editableRegionBoundaries.slice() + } + }), + {} + ) ); -export const createQuestion = createAction(types.createQuestion); -export const initTests = createAction(types.initTests); -export const updateTests = createAction(types.updateTests); -export const cancelTests = createAction(types.cancelTests); +export const createQuestion = createAction(actionTypes.createQuestion); +export const initTests = createAction(actionTypes.initTests); +export const updateTests = createAction(actionTypes.updateTests); +export const cancelTests = createAction(actionTypes.cancelTests); -export const initConsole = createAction(types.initConsole); -export const initLogs = createAction(types.initLogs); -export const updateChallengeMeta = createAction(types.updateChallengeMeta); -export const updateFile = createAction(types.updateFile); -export const updateConsole = createAction(types.updateConsole); -export const updateLogs = createAction(types.updateLogs); -export const updateJSEnabled = createAction(types.updateJSEnabled); +export const initConsole = createAction(actionTypes.initConsole); +export const initLogs = createAction(actionTypes.initLogs); +export const updateChallengeMeta = createAction( + actionTypes.updateChallengeMeta +); +export const updateFile = createAction(actionTypes.updateFile); +export const updateConsole = createAction(actionTypes.updateConsole); +export const updateLogs = createAction(actionTypes.updateLogs); +export const updateJSEnabled = createAction(actionTypes.updateJSEnabled); export const updateSolutionFormValues = createAction( - types.updateSolutionFormValues + actionTypes.updateSolutionFormValues +); +export const updateSuccessMessage = createAction( + actionTypes.updateSuccessMessage ); -export const updateSuccessMessage = createAction(types.updateSuccessMessage); -export const logsToConsole = createAction(types.logsToConsole); +export const logsToConsole = createAction(actionTypes.logsToConsole); -export const lockCode = createAction(types.lockCode); -export const unlockCode = createAction(types.unlockCode); -export const disableBuildOnError = createAction(types.disableBuildOnError); -export const storedCodeFound = createAction(types.storedCodeFound); -export const noStoredCodeFound = createAction(types.noStoredCodeFound); -export const saveEditorContent = createAction(types.saveEditorContent); +export const lockCode = createAction(actionTypes.lockCode); +export const unlockCode = createAction(actionTypes.unlockCode); +export const disableBuildOnError = createAction( + actionTypes.disableBuildOnError +); +export const storedCodeFound = createAction(actionTypes.storedCodeFound); +export const noStoredCodeFound = createAction(actionTypes.noStoredCodeFound); +export const saveEditorContent = createAction(actionTypes.saveEditorContent); -export const closeModal = createAction(types.closeModal); -export const openModal = createAction(types.openModal); +export const closeModal = createAction(actionTypes.closeModal); +export const openModal = createAction(actionTypes.openModal); -export const previewMounted = createAction(types.previewMounted); -export const challengeMounted = createAction(types.challengeMounted); -export const checkChallenge = createAction(types.checkChallenge); -export const executeChallenge = createAction(types.executeChallenge); -export const resetChallenge = createAction(types.resetChallenge); -export const submitChallenge = createAction(types.submitChallenge); +export const previewMounted = createAction(actionTypes.previewMounted); +export const challengeMounted = createAction(actionTypes.challengeMounted); +export const checkChallenge = createAction(actionTypes.checkChallenge); +export const executeChallenge = createAction(actionTypes.executeChallenge); +export const resetChallenge = createAction(actionTypes.resetChallenge); +export const submitChallenge = createAction(actionTypes.submitChallenge); -export const moveToTab = createAction(types.moveToTab); +export const moveToTab = createAction(actionTypes.moveToTab); -export const setEditorFocusability = createAction(types.setEditorFocusability); -export const toggleVisibleEditor = createAction(types.toggleVisibleEditor); -export const setAccessibilityMode = createAction(types.setAccessibilityMode); +export const setEditorFocusability = createAction( + actionTypes.setEditorFocusability +); +export const toggleVisibleEditor = createAction( + actionTypes.toggleVisibleEditor +); +export const setAccessibilityMode = createAction( + actionTypes.setAccessibilityMode +); export const currentTabSelector = state => state[ns].currentTab; export const challengeFilesSelector = state => state[ns].challengeFiles; @@ -249,12 +216,12 @@ export const inAccessibilityModeSelector = state => export const reducer = handleActions( { - [types.createFiles]: (state, { payload }) => ({ + [actionTypes.createFiles]: (state, { payload }) => ({ ...state, challengeFiles: payload, visibleEditors: { [getTargetEditor(payload)]: true } }), - [types.updateFile]: ( + [actionTypes.updateFile]: ( state, { payload: { key, editorValue, editableRegionBoundaries } } ) => ({ @@ -269,47 +236,47 @@ export const reducer = handleActions( } } }), - [types.storedCodeFound]: (state, { payload }) => ({ + [actionTypes.storedCodeFound]: (state, { payload }) => ({ ...state, challengeFiles: payload }), - [types.initTests]: (state, { payload }) => ({ + [actionTypes.initTests]: (state, { payload }) => ({ ...state, challengeTests: payload }), - [types.updateTests]: (state, { payload }) => ({ + [actionTypes.updateTests]: (state, { payload }) => ({ ...state, challengeTests: payload }), - [types.initConsole]: (state, { payload }) => ({ + [actionTypes.initConsole]: (state, { payload }) => ({ ...state, consoleOut: payload ? [payload] : [] }), - [types.updateConsole]: (state, { payload }) => ({ + [actionTypes.updateConsole]: (state, { payload }) => ({ ...state, consoleOut: state.consoleOut.concat(payload) }), - [types.initLogs]: state => ({ + [actionTypes.initLogs]: state => ({ ...state, logsOut: [] }), - [types.updateLogs]: (state, { payload }) => ({ + [actionTypes.updateLogs]: (state, { payload }) => ({ ...state, logsOut: state.logsOut.concat(payload) }), - [types.logsToConsole]: (state, { payload }) => ({ + [actionTypes.logsToConsole]: (state, { payload }) => ({ ...state, consoleOut: isEmpty(state.logsOut) ? state.consoleOut : state.consoleOut.concat(payload, state.logsOut) }), - [types.updateChallengeMeta]: (state, { payload }) => ({ + [actionTypes.updateChallengeMeta]: (state, { payload }) => ({ ...state, challengeMeta: { ...payload } }), - [types.resetChallenge]: state => ({ + [actionTypes.resetChallenge]: state => ({ ...state, currentTab: 2, challengeFiles: { @@ -337,56 +304,56 @@ export const reducer = handleActions( })), consoleOut: [] }), - [types.updateSolutionFormValues]: (state, { payload }) => ({ + [actionTypes.updateSolutionFormValues]: (state, { payload }) => ({ ...state, projectFormValues: payload }), - [types.lockCode]: state => ({ + [actionTypes.lockCode]: state => ({ ...state, isCodeLocked: true }), - [types.unlockCode]: state => ({ + [actionTypes.unlockCode]: state => ({ ...state, isBuildEnabled: true, isCodeLocked: false }), - [types.disableBuildOnError]: state => ({ + [actionTypes.disableBuildOnError]: state => ({ ...state, isBuildEnabled: false }), - [types.updateSuccessMessage]: (state, { payload }) => ({ + [actionTypes.updateSuccessMessage]: (state, { payload }) => ({ ...state, successMessage: payload }), - [types.closeModal]: (state, { payload }) => ({ + [actionTypes.closeModal]: (state, { payload }) => ({ ...state, modal: { ...state.modal, [payload]: false } }), - [types.openModal]: (state, { payload }) => ({ + [actionTypes.openModal]: (state, { payload }) => ({ ...state, modal: { ...state.modal, [payload]: true } }), - [types.moveToTab]: (state, { payload }) => ({ + [actionTypes.moveToTab]: (state, { payload }) => ({ ...state, currentTab: payload }), - [types.executeChallenge]: state => ({ + [actionTypes.executeChallenge]: state => ({ ...state, currentTab: 3 }), - [types.setEditorFocusability]: (state, { payload }) => ({ + [actionTypes.setEditorFocusability]: (state, { payload }) => ({ ...state, canFocusEditor: payload }), - [types.toggleVisibleEditor]: (state, { payload }) => { + [actionTypes.toggleVisibleEditor]: (state, { payload }) => { return { ...state, visibleEditors: { @@ -395,7 +362,7 @@ export const reducer = handleActions( } }; }, - [types.setAccessibilityMode]: (state, { payload }) => ({ + [actionTypes.setAccessibilityMode]: (state, { payload }) => ({ ...state, inAccessibilityMode: payload }) diff --git a/client/src/templates/Challenges/utils/build.js b/client/src/templates/Challenges/utils/build.js index 9cc8632737..b7c307bb36 100644 --- a/client/src/templates/Challenges/utils/build.js +++ b/client/src/templates/Challenges/utils/build.js @@ -1,18 +1,17 @@ -import { getTransformers } from '../rechallenge/transformers'; -import { cssToHtml, jsToHtml, concatHtml } from '../rechallenge/builders.js'; -import { challengeTypes } from '../../../../utils/challengeTypes'; -import createWorker from './worker-executor'; -import { - createTestFramer, - runTestInTestFrame, - createMainFramer -} from './frame'; - // the config files are created during the build, but not before linting // eslint-disable-next-line import/no-unresolved import frameRunnerData from '../../../../../config/client/frame-runner.json'; // eslint-disable-next-line import/no-unresolved import testEvaluatorData from '../../../../../config/client/test-evaluator.json'; +import { challengeTypes } from '../../../../utils/challengeTypes'; +import { cssToHtml, jsToHtml, concatHtml } from '../rechallenge/builders.js'; +import { getTransformers } from '../rechallenge/transformers'; +import { + createTestFramer, + runTestInTestFrame, + createMainFramer +} from './frame'; +import createWorker from './worker-executor'; const { filename: runner } = frameRunnerData; const { filename: testEvaluator } = testEvaluatorData; diff --git a/client/src/templates/Challenges/utils/getTargetEditor.js b/client/src/templates/Challenges/utils/getTargetEditor.js index 9c9972ddb7..e52a18015c 100644 --- a/client/src/templates/Challenges/utils/getTargetEditor.js +++ b/client/src/templates/Challenges/utils/getTargetEditor.js @@ -1,5 +1,5 @@ -import { toSortedArray } from '../../../../../utils/sort-files'; import { isEmpty } from 'lodash-es'; +import { toSortedArray } from '../../../../../utils/sort-files'; export function getTargetEditor(challengeFiles) { if (isEmpty(challengeFiles)) return null; diff --git a/client/src/templates/Challenges/video/Show.tsx b/client/src/templates/Challenges/video/Show.tsx index 9788d609a6..340fe88d89 100644 --- a/client/src/templates/Challenges/video/Show.tsx +++ b/client/src/templates/Challenges/video/Show.tsx @@ -1,29 +1,29 @@ // Package Utilities -import React, { Component } from 'react'; import { Button, Grid, Col, Row } from '@freecodecamp/react-bootstrap'; -import { connect } from 'react-redux'; -import { bindActionCreators } from 'redux'; import { graphql } from 'gatsby'; +import React, { Component } from 'react'; import Helmet from 'react-helmet'; -import YouTube from 'react-youtube'; -import { createSelector } from 'reselect'; import { ObserveKeys } from 'react-hotkeys'; import { TFunction, withTranslation } from 'react-i18next'; +import { connect } from 'react-redux'; +import YouTube from 'react-youtube'; +import { bindActionCreators } from 'redux'; import type { Dispatch } from 'redux'; +import { createSelector } from 'reselect'; // Local Utilities -import PrismFormatted from '../components/prism-formatted'; +import Loader from '../../../components/helpers/loader'; +import Spacer from '../../../components/helpers/spacer'; +import LearnLayout from '../../../components/layouts/learn'; import { ChallengeNodeType, ChallengeMetaType } from '../../../redux/prop-types'; -import LearnLayout from '../../../components/layouts/learn'; -import ChallengeTitle from '../components/challenge-title'; import ChallengeDescription from '../components/Challenge-Description'; -import Spacer from '../../../components/helpers/spacer'; -import CompletionModal from '../components/completion-modal'; import Hotkeys from '../components/Hotkeys'; -import Loader from '../../../components/helpers/loader'; +import ChallengeTitle from '../components/challenge-title'; +import CompletionModal from '../components/completion-modal'; +import PrismFormatted from '../components/prism-formatted'; import { isChallengeCompletedSelector, challengeMounted, diff --git a/client/src/templates/Introduction/Intro.js b/client/src/templates/Introduction/Intro.js index 1090077bde..6c64051a9f 100644 --- a/client/src/templates/Introduction/Intro.js +++ b/client/src/templates/Introduction/Intro.js @@ -1,13 +1,13 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { Link, graphql } from 'gatsby'; -import Helmet from 'react-helmet'; import { Grid, ListGroup, ListGroupItem } from '@freecodecamp/react-bootstrap'; +import { Link, graphql } from 'gatsby'; +import PropTypes from 'prop-types'; +import React from 'react'; +import Helmet from 'react-helmet'; import { useTranslation } from 'react-i18next'; -import LearnLayout from '../../components/layouts/learn'; -import FullWidthRow from '../../components/helpers/full-width-row'; import ButtonSpacer from '../../components/helpers/button-spacer'; +import FullWidthRow from '../../components/helpers/full-width-row'; +import LearnLayout from '../../components/layouts/learn'; import { MarkdownRemark, AllChallengeNode } from '../../redux/prop-types'; import './intro.css'; diff --git a/client/src/templates/Introduction/SuperBlockIntro.js b/client/src/templates/Introduction/SuperBlockIntro.js index cd66cb9caa..32225f6fdd 100644 --- a/client/src/templates/Introduction/SuperBlockIntro.js +++ b/client/src/templates/Introduction/SuperBlockIntro.js @@ -1,21 +1,18 @@ -import React, { Fragment, useEffect, memo } from 'react'; -import PropTypes from 'prop-types'; -import Helmet from 'react-helmet'; +import { Grid, Row, Col } from '@freecodecamp/react-bootstrap'; import { graphql } from 'gatsby'; import { uniq } from 'lodash-es'; -import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; -import { bindActionCreators } from 'redux'; +import PropTypes from 'prop-types'; +import React, { Fragment, useEffect, memo } from 'react'; +import Helmet from 'react-helmet'; import { withTranslation } from 'react-i18next'; -import { Grid, Row, Col } from '@freecodecamp/react-bootstrap'; +import { connect } from 'react-redux'; import { configureAnchors } from 'react-scrollable-anchor'; +import { bindActionCreators } from 'redux'; +import { createSelector } from 'reselect'; +import DonateModal from '../../../../client/src/components/Donation/DonationModal'; import Login from '../../components/Header/components/Login'; import Map from '../../components/Map'; -import CertChallenge from './components/CertChallenge'; -import SuperBlockIntro from './components/SuperBlockIntro'; -import Block from './components/Block'; -import DonateModal from '../../../../client/src/components/Donation/DonationModal'; import { Spacer } from '../../components/helpers'; import { currentChallengeIdSelector, @@ -24,8 +21,11 @@ import { tryToShowDonationModal, userSelector } from '../../redux'; -import { resetExpansion, toggleBlock } from './redux'; import { MarkdownRemark, AllChallengeNode, User } from '../../redux/prop-types'; +import Block from './components/Block'; +import CertChallenge from './components/CertChallenge'; +import SuperBlockIntro from './components/SuperBlockIntro'; +import { resetExpansion, toggleBlock } from './redux'; import './intro.css'; diff --git a/client/src/templates/Introduction/components/Block.js b/client/src/templates/Introduction/components/Block.js index b55df9a13f..25eaf6a180 100644 --- a/client/src/templates/Introduction/components/Block.js +++ b/client/src/templates/Introduction/components/Block.js @@ -1,20 +1,20 @@ -import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; +import React, { Component } from 'react'; import { withTranslation } from 'react-i18next'; +import { connect } from 'react-redux'; import ScrollableAnchor from 'react-scrollable-anchor'; +import { bindActionCreators } from 'redux'; +import { createSelector } from 'reselect'; -import { makeExpandedBlockSelector, toggleBlock } from '../redux'; -import { completedChallengesSelector, executeGA } from '../../../redux'; -import Challenges from './Challenges'; -import Caret from '../../../assets/icons/caret'; -import GreenPass from '../../../assets/icons/green-pass'; -import GreenNotCompleted from '../../../assets/icons/green-not-completed'; -import { isAuditedCert } from '../../../../../utils/is-audited'; import envData from '../../../../../config/env.json'; +import { isAuditedCert } from '../../../../../utils/is-audited'; +import Caret from '../../../assets/icons/caret'; +import GreenNotCompleted from '../../../assets/icons/green-not-completed'; +import GreenPass from '../../../assets/icons/green-pass'; import { Link } from '../../../components/helpers/'; +import { completedChallengesSelector, executeGA } from '../../../redux'; +import { makeExpandedBlockSelector, toggleBlock } from '../redux'; +import Challenges from './Challenges'; const { curriculumLocale } = envData; diff --git a/client/src/templates/Introduction/components/CertChallenge.js b/client/src/templates/Introduction/components/CertChallenge.js index 762c270fbe..e1c5df7c4e 100644 --- a/client/src/templates/Introduction/components/CertChallenge.js +++ b/client/src/templates/Introduction/components/CertChallenge.js @@ -1,24 +1,24 @@ -import React, { useState, useEffect } from 'react'; +import { Button } from '@freecodecamp/react-bootstrap'; +import { navigate } from 'gatsby-link'; import PropTypes from 'prop-types'; +import React, { useState, useEffect } from 'react'; +import { withTranslation } from 'react-i18next'; import { connect } from 'react-redux'; import { createSelector } from 'reselect'; -import { withTranslation } from 'react-i18next'; -import { Button } from '@freecodecamp/react-bootstrap'; -import CertificationCard from './CertificationCard'; - -import { stepsToClaimSelector } from '../../../redux'; -import { verifyCert } from '../../../redux/settings'; -import { createFlashMessage } from '../../../components/Flash/redux'; -import { StepsType, User } from '../../../redux/prop-types'; - -import { certMap } from '../../../resources/cert-and-project-map'; import { certSlugTypeMap, superBlockCertTypeMap } from '../../../../../config/certification-settings'; +import { createFlashMessage } from '../../../components/Flash/redux'; +import { stepsToClaimSelector } from '../../../redux'; + +import { StepsType, User } from '../../../redux/prop-types'; +import { verifyCert } from '../../../redux/settings'; + +import { certMap } from '../../../resources/cert-and-project-map'; import { getVerifyCanClaimCert } from '../../../utils/ajax'; -import { navigate } from 'gatsby-link'; +import CertificationCard from './CertificationCard'; const propTypes = { createFlashMessage: PropTypes.func.isRequired, diff --git a/client/src/templates/Introduction/components/CertificationCard.js b/client/src/templates/Introduction/components/CertificationCard.js index 8660c66ef0..c5982e7ce6 100644 --- a/client/src/templates/Introduction/components/CertificationCard.js +++ b/client/src/templates/Introduction/components/CertificationCard.js @@ -1,14 +1,14 @@ -import React, { useState } from 'react'; import PropTypes from 'prop-types'; +import React, { useState } from 'react'; -import GreenNotCompleted from '../../../assets/icons/green-not-completed'; -import ScrollableAnchor from 'react-scrollable-anchor'; -// import { navigate } from 'gatsby'; import { useTranslation } from 'react-i18next'; -import ClaimCertSteps from './ClaimCertSteps'; +import ScrollableAnchor from 'react-scrollable-anchor'; +import Caret from '../../../assets/icons/caret'; +import GreenNotCompleted from '../../../assets/icons/green-not-completed'; +// import { navigate } from 'gatsby'; import GreenPass from '../../../assets/icons/green-pass'; import { StepsType } from '../../../redux/prop-types'; -import Caret from '../../../assets/icons/caret'; +import ClaimCertSteps from './ClaimCertSteps'; const propTypes = { i18nCertText: PropTypes.string, diff --git a/client/src/templates/Introduction/components/Challenges.js b/client/src/templates/Introduction/components/Challenges.js index 5f5ac31616..532ac0f6c9 100644 --- a/client/src/templates/Introduction/components/Challenges.js +++ b/client/src/templates/Introduction/components/Challenges.js @@ -1,14 +1,14 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; import { Link } from 'gatsby'; +import PropTypes from 'prop-types'; +import React, { Component } from 'react'; import { withTranslation } from 'react-i18next'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; +import { createSelector } from 'reselect'; -import { completedChallengesSelector, executeGA } from '../../../redux'; -import GreenPass from '../../../assets/icons/green-pass'; import GreenNotCompleted from '../../../assets/icons/green-not-completed'; +import GreenPass from '../../../assets/icons/green-pass'; +import { completedChallengesSelector, executeGA } from '../../../redux'; const mapStateToProps = state => { return createSelector(completedChallengesSelector, completedChallenges => ({ diff --git a/client/src/templates/Introduction/components/ClaimCertSteps.js b/client/src/templates/Introduction/components/ClaimCertSteps.js index f5ac8be78d..f02a09f9f8 100644 --- a/client/src/templates/Introduction/components/ClaimCertSteps.js +++ b/client/src/templates/Introduction/components/ClaimCertSteps.js @@ -1,11 +1,11 @@ -import React from 'react'; -import PropTypes from 'prop-types'; import { Link } from 'gatsby'; +import PropTypes from 'prop-types'; +import React from 'react'; import { withTranslation, useTranslation } from 'react-i18next'; -import { StepsType } from '../../../redux/prop-types'; -import GreenPass from '../../../assets/icons/green-pass'; import GreenNotCompleted from '../../../assets/icons/green-not-completed'; +import GreenPass from '../../../assets/icons/green-pass'; +import { StepsType } from '../../../redux/prop-types'; const mapIconStyle = { height: '15px', marginRight: '10px', width: '15px' }; diff --git a/client/src/templates/Introduction/components/SuperBlockIntro.js b/client/src/templates/Introduction/components/SuperBlockIntro.js index ca69450e38..848bba3995 100644 --- a/client/src/templates/Introduction/components/SuperBlockIntro.js +++ b/client/src/templates/Introduction/components/SuperBlockIntro.js @@ -1,9 +1,9 @@ -import React from 'react'; import PropTypes from 'prop-types'; +import React from 'react'; import { useTranslation } from 'react-i18next'; -import { Spacer } from '../../../components/helpers'; import { generateIconComponent } from '../../../assets/icons'; +import { Spacer } from '../../../components/helpers'; const propTypes = { superBlock: PropTypes.string diff --git a/client/src/utils/ajax.ts b/client/src/utils/ajax.ts index 1f15f6fa39..d13b58ceeb 100644 --- a/client/src/utils/ajax.ts +++ b/client/src/utils/ajax.ts @@ -1,5 +1,5 @@ -import envData from '../../../config/env.json'; import cookies from 'browser-cookies'; +import envData from '../../../config/env.json'; import type { UserType } from '../redux/prop-types'; diff --git a/client/src/utils/curriculum-helpers.test.ts b/client/src/utils/curriculum-helpers.test.ts index a61f2c6165..708ce3c6fc 100644 --- a/client/src/utils/curriculum-helpers.test.ts +++ b/client/src/utils/curriculum-helpers.test.ts @@ -1,8 +1,8 @@ -import __testHelpers, { removeJSComments } from './curriculum-helpers'; -import jsTestValues from './__fixtures/curriculum-helpers-javascript'; import cssTestValues from './__fixtures/curriculum-helpers-css'; import htmlTestValues from './__fixtures/curriculum-helpers-html'; +import jsTestValues from './__fixtures/curriculum-helpers-javascript'; import whiteSpaceTestValues from './__fixtures/curriculum-helpers-remove-white-space'; +import __testHelpers, { removeJSComments } from './curriculum-helpers'; const { stringWithWhiteSpaceChars, stringWithWhiteSpaceCharsRemoved } = whiteSpaceTestValues; diff --git a/client/src/utils/curriculum-helpers.ts b/client/src/utils/curriculum-helpers.ts index a5d7b12ed5..33818215d4 100644 --- a/client/src/utils/curriculum-helpers.ts +++ b/client/src/utils/curriculum-helpers.ts @@ -1,5 +1,5 @@ -import CSSHelp from './css-help'; import strip from '@freecodecamp/strip-comments'; +import CSSHelp from './css-help'; const removeHtmlComments = (str: string): string => str.replace(/|$)/g, ''); diff --git a/client/src/utils/handled-error.test.ts b/client/src/utils/handled-error.test.ts index 42e59cb839..dd96a9783b 100644 --- a/client/src/utils/handled-error.test.ts +++ b/client/src/utils/handled-error.test.ts @@ -1,5 +1,5 @@ -import { isObject } from 'lodash-es'; import type { AxiosError } from 'axios'; +import { isObject } from 'lodash-es'; import { isHandledError, diff --git a/client/src/utils/handled-error.ts b/client/src/utils/handled-error.ts index 178074b5e6..c1068ca363 100644 --- a/client/src/utils/handled-error.ts +++ b/client/src/utils/handled-error.ts @@ -1,10 +1,9 @@ -import { has } from 'lodash-es'; import type { AxiosError, AxiosResponse } from 'axios'; - -import standardErrorMessage from './standard-error-message'; -import reportedErrorMessage from './reported-error-message'; +import { has } from 'lodash-es'; import { reportClientSideError } from './report-error'; +import reportedErrorMessage from './reported-error-message'; +import standardErrorMessage from './standard-error-message'; interface ErrorData { type?: string; diff --git a/client/utils/buildChallenges.js b/client/utils/buildChallenges.js index b16d445914..c64bd1c029 100644 --- a/client/utils/buildChallenges.js +++ b/client/utils/buildChallenges.js @@ -1,13 +1,13 @@ -const _ = require('lodash'); const path = require('path'); +const _ = require('lodash'); +const envData = require('../../config/env.json'); const { getChallengesForLang, createChallenge, challengesDir, getChallengesDirForLang } = require('../../curriculum/getChallenges'); -const envData = require('../../config/env.json'); const { curriculumLocale } = envData; diff --git a/client/utils/gatsby/layoutSelector.test.js b/client/utils/gatsby/layoutSelector.test.js index e4da7366d5..64f515a49b 100644 --- a/client/utils/gatsby/layoutSelector.test.js +++ b/client/utils/gatsby/layoutSelector.test.js @@ -2,11 +2,11 @@ import React from 'react'; import { Provider } from 'react-redux'; import ShallowRenderer from 'react-test-renderer/shallow'; -import layoutSelector from './layout-selector'; -import { createStore } from '../../src/redux/createStore'; import FourOhFourPage from '../../src/pages/404'; -import Learn from '../../src/pages/learn'; import Certification from '../../src/pages/certification'; +import Learn from '../../src/pages/learn'; +import { createStore } from '../../src/redux/createStore'; +import layoutSelector from './layout-selector'; jest.mock('../../src/analytics'); diff --git a/client/utils/tags.js b/client/utils/tags.js index 7c69fcc21d..dd1c1e5738 100644 --- a/client/utils/tags.js +++ b/client/utils/tags.js @@ -1,7 +1,7 @@ -import React from 'react'; import { withPrefix } from 'gatsby'; import i18next from 'i18next'; import psl from 'psl'; +import React from 'react'; import env from '../../config/env.json'; const { homeLocation } = env; diff --git a/client/webpack-workers.js b/client/webpack-workers.js index 6dbd8b585d..a4e7f70792 100644 --- a/client/webpack-workers.js +++ b/client/webpack-workers.js @@ -1,7 +1,7 @@ -const path = require('path'); -const webpack = require('webpack'); -const CopyWebpackPlugin = require('copy-webpack-plugin'); const { writeFileSync } = require('fs'); +const path = require('path'); +const CopyWebpackPlugin = require('copy-webpack-plugin'); +const webpack = require('webpack'); module.exports = (env = {}) => { const __DEV__ = env.production !== true; diff --git a/curriculum/getChallenges.js b/curriculum/getChallenges.js index 3980b90380..8db7f86226 100644 --- a/curriculum/getChallenges.js +++ b/curriculum/getChallenges.js @@ -1,10 +1,14 @@ +const fs = require('fs'); const path = require('path'); +const util = require('util'); +const yaml = require('js-yaml'); const { findIndex, reduce, toString } = require('lodash'); const readDirP = require('readdirp'); -const yaml = require('js-yaml'); +const { helpCategoryMap } = require('../client/utils/challengeTypes'); +const { showUpcomingChanges } = require('../config/env.json'); +const { curriculum: curriculumLangs } = + require('../config/i18n/all-langs').availableLangs; const { parseMD } = require('../tools/challenge-parser/parser'); -const fs = require('fs'); -const util = require('util'); /* eslint-disable max-len */ const { translateCommentsInChallenge @@ -12,13 +16,8 @@ const { /* eslint-enable max-len*/ const { isAuditedCert } = require('../utils/is-audited'); -const { dasherize } = require('../utils/slugs'); const { createPoly } = require('../utils/polyvinyl'); -const { helpCategoryMap } = require('../client/utils/challengeTypes'); -const { curriculum: curriculumLangs } = - require('../config/i18n/all-langs').availableLangs; - -const { showUpcomingChanges } = require('../config/env.json'); +const { dasherize } = require('../utils/slugs'); const access = util.promisify(fs.access); diff --git a/curriculum/gulpfile.js b/curriculum/gulpfile.js index 3224606020..2128d2b14e 100644 --- a/curriculum/gulpfile.js +++ b/curriculum/gulpfile.js @@ -1,8 +1,8 @@ const gulp = require('gulp'); const through2 = require('through2'); -const { testedLang } = require('./utils'); const lintMarkdown = require('../tools/scripts/lint'); +const { testedLang } = require('./utils'); /** * Tasks diff --git a/curriculum/test/test-challenges.js b/curriculum/test/test-challenges.js index 2ca81404b3..9c3239a73f 100644 --- a/curriculum/test/test-challenges.js +++ b/curriculum/test/test-challenges.js @@ -1,10 +1,20 @@ /* eslint-disable no-loop-func */ const path = require('path'); +const { inspect } = require('util'); +const vm = require('vm'); +const { assert, AssertionError } = require('chai'); +const jsdom = require('jsdom'); const liveServer = require('live-server'); -const stringSimilarity = require('string-similarity'); -const { isAuditedCert } = require('../../utils/is-audited'); - +const lodash = require('lodash'); +const Mocha = require('mocha'); +const mockRequire = require('mock-require'); const spinner = require('ora')(); +const puppeteer = require('puppeteer'); +const stringSimilarity = require('string-similarity'); + +// lodash-es can't easily be used in node environments, so we just mock it out +// for the original lodash in testing. +mockRequire('lodash-es', lodash); const clientPath = path.resolve(__dirname, '../../client'); require('@babel/polyfill'); @@ -16,50 +26,34 @@ require('@babel/register')({ ignore: [/node_modules/], only: [clientPath] }); - -const mockRequire = require('mock-require'); -const lodash = require('lodash'); - -// lodash-es can't easily be used in node environments, so we just mock it out -// for the original lodash in testing. -mockRequire('lodash-es', lodash); - -const createPseudoWorker = require('./utils/pseudo-worker'); +const { + buildDOMChallenge, + buildJSChallenge +} = require('../../client/src/templates/Challenges/utils/build'); const { default: createWorker } = require('../../client/src/templates/Challenges/utils/worker-executor'); +const { challengeTypes } = require('../../client/utils/challengeTypes'); +// the config files are created during the build, but not before linting +/* eslint-disable import/no-unresolved */ +const testEvaluator = + require('../../config/client/test-evaluator.json').filename; +/* eslint-enable import/no-unresolved */ -const { assert, AssertionError } = require('chai'); -const Mocha = require('mocha'); - -const { flatten, isEmpty, cloneDeep, isEqual } = lodash; const { getLines } = require('../../utils/get-lines'); +const { isAuditedCert } = require('../../utils/is-audited'); -const jsdom = require('jsdom'); - -const vm = require('vm'); - -const puppeteer = require('puppeteer'); - +const { toSortedArray } = require('../../utils/sort-files'); const { getChallengesForLang, getMetaForBlock, getTranslatableComments } = require('../getChallenges'); - -const MongoIds = require('./utils/mongoIds'); -const ChallengeTitles = require('./utils/challengeTitles'); const { challengeSchemaValidator } = require('../schema/challengeSchema'); -const { challengeTypes } = require('../../client/utils/challengeTypes'); - -const { toSortedArray } = require('../../utils/sort-files'); - const { testedLang } = require('../utils'); - -const { - buildDOMChallenge, - buildJSChallenge -} = require('../../client/src/templates/Challenges/utils/build'); +const ChallengeTitles = require('./utils/challengeTitles'); +const MongoIds = require('./utils/mongoIds'); +const createPseudoWorker = require('./utils/pseudo-worker'); const { sortChallenges } = require('./utils/sort-challenges'); @@ -67,13 +61,6 @@ const TRANSLATABLE_COMMENTS = getTranslatableComments( path.resolve(__dirname, '..', 'dictionaries') ); -// the config files are created during the build, but not before linting -/* eslint-disable import/no-unresolved */ -const testEvaluator = - require('../../config/client/test-evaluator.json').filename; -/* eslint-enable import/no-unresolved */ -const { inspect } = require('util'); - const commentExtractors = { html: require('./utils/extract-html-comments'), js: require('./utils/extract-js-comments'), @@ -82,6 +69,8 @@ const commentExtractors = { scriptJs: require('./utils/extract-script-js-comments') }; +const { flatten, isEmpty, cloneDeep, isEqual } = lodash; + // rethrow unhandled rejections to make sure the tests exit with -1 process.on('unhandledRejection', err => handleRejection(err)); diff --git a/curriculum/test/utils/mongoIds.js b/curriculum/test/utils/mongoIds.js index 3148581acd..efa01ff36b 100644 --- a/curriculum/test/utils/mongoIds.js +++ b/curriculum/test/utils/mongoIds.js @@ -1,5 +1,5 @@ -const findIndex = require('lodash/findIndex'); const Joi = require('joi'); +const findIndex = require('lodash/findIndex'); Joi.objectId = require('joi-objectid')(Joi); const schema = Joi.objectId(); diff --git a/curriculum/test/utils/plugins/get-css-comments.js b/curriculum/test/utils/plugins/get-css-comments.js index 355faf9a3c..212d141c69 100644 --- a/curriculum/test/utils/plugins/get-css-comments.js +++ b/curriculum/test/utils/plugins/get-css-comments.js @@ -1,6 +1,6 @@ +var css = require('css'); const { isEmpty } = require('lodash'); const visit = require('unist-util-visit'); -var css = require('css'); const { commentToData } = require('../comment-to-data'); function visitComments(node, cb) { diff --git a/curriculum/test/utils/plugins/get-script-js-comments.js b/curriculum/test/utils/plugins/get-script-js-comments.js index d50edaf87e..5cb45471f5 100644 --- a/curriculum/test/utils/plugins/get-script-js-comments.js +++ b/curriculum/test/utils/plugins/get-script-js-comments.js @@ -1,6 +1,6 @@ +const acorn = require('acorn'); const { isEmpty } = require('lodash'); const visit = require('unist-util-visit'); -const acorn = require('acorn'); const { commentToData } = require('../comment-to-data'); const parser = acorn.Parser; diff --git a/tools/challenge-helper-scripts/create-next-step.js b/tools/challenge-helper-scripts/create-next-step.js index 4483f55002..b25b940a60 100644 --- a/tools/challenge-helper-scripts/create-next-step.js +++ b/tools/challenge-helper-scripts/create-next-step.js @@ -1,7 +1,7 @@ -const { getProjectPath } = require('./helpers/get-project-path'); const { getLastStepFileContent } = require('./helpers/get-last-step-file-content'); +const { getProjectPath } = require('./helpers/get-project-path'); const { reorderSteps, createStepFile } = require('./utils'); const projectPath = getProjectPath(); diff --git a/tools/challenge-helper-scripts/create-project.ts b/tools/challenge-helper-scripts/create-project.ts index a8e01c73f5..58fc3ba39b 100644 --- a/tools/challenge-helper-scripts/create-project.ts +++ b/tools/challenge-helper-scripts/create-project.ts @@ -1,11 +1,11 @@ -import fs from 'fs/promises'; import { existsSync } from 'fs'; +import fs from 'fs/promises'; import path from 'path'; -import { format } from 'prettier'; import { prompt } from 'inquirer'; +import { format } from 'prettier'; -import { createStepFile } from './utils.js'; import { blockNameify } from '../../utils/block-nameify'; +import { createStepFile } from './utils.js'; const superBlocks = [ 'responsive-web-design', diff --git a/tools/challenge-helper-scripts/utils.js b/tools/challenge-helper-scripts/utils.js index b6cdc68df1..ecf8677d9d 100644 --- a/tools/challenge-helper-scripts/utils.js +++ b/tools/challenge-helper-scripts/utils.js @@ -1,14 +1,14 @@ const fs = require('fs'); const path = require('path'); -const matter = require('gray-matter'); const ObjectID = require('bson-objectid'); -const { parseMDSync } = require('../challenge-parser/parser'); +const matter = require('gray-matter'); const { getMetaData } = require('../challenge-helper-scripts/helpers/get-project-path-metadata'); -const { getStepTemplate } = require('./helpers/get-step-template'); +const { parseMDSync } = require('../challenge-parser/parser'); const { getProjectMetaPath } = require('./helpers/get-project-meta-path'); const { getProjectPath } = require('./helpers/get-project-path'); +const { getStepTemplate } = require('./helpers/get-step-template'); const { padWithLeadingZeros } = require('./helpers/pad-with-leading-zeros'); const createStepFile = ({ diff --git a/tools/challenge-helper-scripts/utils.test.js b/tools/challenge-helper-scripts/utils.test.js index c49797785e..489a80c884 100644 --- a/tools/challenge-helper-scripts/utils.test.js +++ b/tools/challenge-helper-scripts/utils.test.js @@ -1,6 +1,7 @@ const fs = require('fs'); -const mock = require('mock-fs'); +const ObjectID = require('bson-objectid'); const glob = require('glob'); +const mock = require('mock-fs'); // NOTE: // Use `console.log()` before mocking the filesystem or use @@ -48,7 +49,6 @@ jest.mock( ); const mockChallengeId = '60d35cf3fe32df2ce8e31b03'; -const ObjectID = require('bson-objectid'); const { getStepTemplate } = require('./helpers/get-step-template'); const { createStepFile, reorderSteps } = require('./utils'); diff --git a/tools/challenge-parser/parser/index.js b/tools/challenge-parser/parser/index.js index 60ad65fbcb..17fa9e6d78 100644 --- a/tools/challenge-parser/parser/index.js +++ b/tools/challenge-parser/parser/index.js @@ -1,17 +1,17 @@ -const { readSync } = require('to-vfile'); -const remark = require('remark-parse'); const directive = require('remark-directive'); const frontmatter = require('remark-frontmatter'); -const addTests = require('./plugins/add-tests'); -const restoreDirectives = require('./plugins/restore-directives'); -const replaceImports = require('./plugins/replace-imports'); +const remark = require('remark-parse'); +const { readSync } = require('to-vfile'); +const unified = require('unified'); const addFrontmatter = require('./plugins/add-frontmatter'); -const addText = require('./plugins/add-text'); -const addVideoQuestion = require('./plugins/add-video-question'); const addSeed = require('./plugins/add-seed'); const addSolution = require('./plugins/add-solution'); +const addTests = require('./plugins/add-tests'); +const addText = require('./plugins/add-text'); +const addVideoQuestion = require('./plugins/add-video-question'); +const replaceImports = require('./plugins/replace-imports'); +const restoreDirectives = require('./plugins/restore-directives'); const tableAndStrikeThrough = require('./plugins/table-and-strikethrough'); -const unified = require('unified'); // by convention, anything that adds to file.data has the name add. const processor = unified() diff --git a/tools/challenge-parser/parser/plugins/add-frontmatter.js b/tools/challenge-parser/parser/plugins/add-frontmatter.js index 5a23cc12a0..fac8d4b2ec 100644 --- a/tools/challenge-parser/parser/plugins/add-frontmatter.js +++ b/tools/challenge-parser/parser/plugins/add-frontmatter.js @@ -1,5 +1,5 @@ -const visit = require('unist-util-visit'); const YAML = require('js-yaml'); +const visit = require('unist-util-visit'); function plugin() { return transformer; diff --git a/tools/challenge-parser/parser/plugins/add-seed.js b/tools/challenge-parser/parser/plugins/add-seed.js index 337d58c621..e4f8900083 100644 --- a/tools/challenge-parser/parser/plugins/add-seed.js +++ b/tools/challenge-parser/parser/plugins/add-seed.js @@ -1,9 +1,9 @@ +const { isEmpty } = require('lodash'); +const { root } = require('mdast-builder'); +const visitChildren = require('unist-util-visit-children'); const getAllBetween = require('./utils/between-headings'); // const visit = require('unist-util-visit'); -const visitChildren = require('unist-util-visit-children'); -const { root } = require('mdast-builder'); const { getFileVisitor } = require('./utils/get-file-visitor'); -const { isEmpty } = require('lodash'); const editableRegionMarker = '--fcc-editable-region--'; diff --git a/tools/challenge-parser/parser/plugins/add-seed.test.js b/tools/challenge-parser/parser/plugins/add-seed.test.js index 6489856729..a448ef8679 100644 --- a/tools/challenge-parser/parser/plugins/add-seed.test.js +++ b/tools/challenge-parser/parser/plugins/add-seed.test.js @@ -1,24 +1,24 @@ +const { isObject } = require('lodash'); const isArray = require('lodash/isArray'); -const simpleAST = require('../__fixtures__/ast-simple.json'); -const withEditableAST = require('../__fixtures__/ast-with-markers.json'); -const withSeedKeysAST = require('../__fixtures__/ast-seed-keys.json'); -const withExtraLinesAST = require('../__fixtures__/ast-with-extra-lines.json'); -const orphanKeyAST = require('../__fixtures__/ast-orphan-key.json'); const adjacentKeysAST = require('../__fixtures__/ast-adjacent-keys.json'); const withBeforeAfterAST = require('../__fixtures__/ast-before-after.json'); -const emptyBeforeAST = require('../__fixtures__/ast-empty-before.json'); +const cCodeAST = require('../__fixtures__/ast-c-code.json'); +const doubleMarkerAST = require('../__fixtures__/ast-double-marker.json'); const emptyAfterAST = require('../__fixtures__/ast-empty-after.json'); +const emptyBeforeAST = require('../__fixtures__/ast-empty-before.json'); +const emptyContentAST = require('../__fixtures__/ast-empty-contents.json'); const emptyCSSAST = require('../__fixtures__/ast-empty-css.json'); const emptyHTMLAST = require('../__fixtures__/ast-empty-html.json'); -const doubleMarkerAST = require('../__fixtures__/ast-double-marker.json'); -const jsxSeedAST = require('../__fixtures__/ast-jsx-seed.json'); -const cCodeAST = require('../__fixtures__/ast-c-code.json'); const explodedMarkerAST = require('../__fixtures__/ast-exploded-marker.json'); -const emptyContentAST = require('../__fixtures__/ast-empty-contents.json'); +const jsxSeedAST = require('../__fixtures__/ast-jsx-seed.json'); +const orphanKeyAST = require('../__fixtures__/ast-orphan-key.json'); +const withSeedKeysAST = require('../__fixtures__/ast-seed-keys.json'); +const simpleAST = require('../__fixtures__/ast-simple.json'); +const withExtraLinesAST = require('../__fixtures__/ast-with-extra-lines.json'); +const withEditableAST = require('../__fixtures__/ast-with-markers.json'); const addSeed = require('./add-seed'); -const { isObject } = require('lodash'); describe('add-seed plugin', () => { const plugin = addSeed(); diff --git a/tools/challenge-parser/parser/plugins/add-solution.js b/tools/challenge-parser/parser/plugins/add-solution.js index f4540a5795..6a373f3a67 100644 --- a/tools/challenge-parser/parser/plugins/add-solution.js +++ b/tools/challenge-parser/parser/plugins/add-solution.js @@ -1,10 +1,10 @@ -const visitChildren = require('unist-util-visit-children'); const { root } = require('mdast-builder'); +const visitChildren = require('unist-util-visit-children'); +const { editableRegionMarker } = require('./add-seed'); +const getAllBetween = require('./utils/between-headings'); const { getFileVisitor } = require('./utils/get-file-visitor'); const { splitOnThematicBreak } = require('./utils/split-on-thematic-break'); -const getAllBetween = require('./utils/between-headings'); -const { editableRegionMarker } = require('./add-seed'); function validateMarkers({ value }) { const lines = value.split('\n'); diff --git a/tools/challenge-parser/parser/plugins/add-solution.test.js b/tools/challenge-parser/parser/plugins/add-solution.test.js index 4618798024..281b313991 100644 --- a/tools/challenge-parser/parser/plugins/add-solution.test.js +++ b/tools/challenge-parser/parser/plugins/add-solution.test.js @@ -1,9 +1,9 @@ -const mockAST = require('../__fixtures__/ast-simple.json'); +const { isObject } = require('lodash'); const editableSolutionAST = require('../__fixtures__/ast-erm-in-solution.json'); const multiSolnsAST = require('../__fixtures__/ast-multiple-solutions.json'); +const mockAST = require('../__fixtures__/ast-simple.json'); const addSolution = require('./add-solution'); -const { isObject } = require('lodash'); describe('add solution plugin', () => { const plugin = addSolution(); diff --git a/tools/challenge-parser/parser/plugins/add-tests.test.js b/tools/challenge-parser/parser/plugins/add-tests.test.js index cdb7d1cc6a..c5abae0aed 100644 --- a/tools/challenge-parser/parser/plugins/add-tests.test.js +++ b/tools/challenge-parser/parser/plugins/add-tests.test.js @@ -1,5 +1,5 @@ -const simpleAST = require('../__fixtures__/ast-simple.json'); const brokenHintsAST = require('../__fixtures__/ast-broken-hints.json'); +const simpleAST = require('../__fixtures__/ast-simple.json'); const addTests = require('./add-tests'); describe('add-tests plugin', () => { diff --git a/tools/challenge-parser/parser/plugins/add-text.test.js b/tools/challenge-parser/parser/plugins/add-text.test.js index d020a954d2..45752e24c4 100644 --- a/tools/challenge-parser/parser/plugins/add-text.test.js +++ b/tools/challenge-parser/parser/plugins/add-text.test.js @@ -1,7 +1,7 @@ -const mockAST = require('../__fixtures__/ast-simple.json'); // eslint-disable-next-line max-len const incorrectMarkersAST = require('../__fixtures__/ast-incorrect-markers.json'); const realisticAST = require('../__fixtures__/ast-realistic.json'); +const mockAST = require('../__fixtures__/ast-simple.json'); const addText = require('./add-text'); describe('add-text', () => { diff --git a/tools/challenge-parser/parser/plugins/replace-imports.js b/tools/challenge-parser/parser/plugins/replace-imports.js index 1925672e8a..0c855c5afb 100644 --- a/tools/challenge-parser/parser/plugins/replace-imports.js +++ b/tools/challenge-parser/parser/plugins/replace-imports.js @@ -1,11 +1,11 @@ const path = require('path'); +const { isEmpty } = require('lodash'); +const remark = require('remark'); const { read } = require('to-vfile'); const modifyChildren = require('unist-util-modify-children'); -const remark = require('remark'); const remove = require('unist-util-remove'); -const visit = require('unist-util-visit'); const { selectAll } = require('unist-util-select'); -const { isEmpty } = require('lodash'); +const visit = require('unist-util-visit'); const { editableRegionMarker } = require('./add-seed'); const tableAndStrikeThrough = require('./table-and-strikethrough'); diff --git a/tools/challenge-parser/parser/plugins/replace-imports.test.js b/tools/challenge-parser/parser/plugins/replace-imports.test.js index 6d638e577b..b3baf78f46 100644 --- a/tools/challenge-parser/parser/plugins/replace-imports.test.js +++ b/tools/challenge-parser/parser/plugins/replace-imports.test.js @@ -3,12 +3,12 @@ const cloneDeep = require('lodash/cloneDeep'); const toVfile = require('to-vfile'); const selectAll = require('unist-util-select').selectAll; -const addImports = require('./replace-imports'); -const originalImportsAST = require('../__fixtures__/ast-imports.json'); -const originalImportsTwoAST = require('../__fixtures__/ast-imports-two.json'); const originalImportsExtraAST = require('../__fixtures__/ast-imports-extra.json'); -const originalSimpleAST = require('../__fixtures__/ast-simple.json'); +const originalImportsTwoAST = require('../__fixtures__/ast-imports-two.json'); +const originalImportsAST = require('../__fixtures__/ast-imports.json'); const originalMarkerAST = require('../__fixtures__/ast-marker-imports.json'); +const originalSimpleAST = require('../__fixtures__/ast-simple.json'); +const addImports = require('./replace-imports'); describe('replace-imports', () => { let importsAST; diff --git a/tools/challenge-parser/parser/plugins/restore-directives.js b/tools/challenge-parser/parser/plugins/restore-directives.js index 1771c97205..0b4d33d122 100644 --- a/tools/challenge-parser/parser/plugins/restore-directives.js +++ b/tools/challenge-parser/parser/plugins/restore-directives.js @@ -1,7 +1,7 @@ -const visit = require('unist-util-visit'); -const { matches } = require('unist-util-select'); const directive = require('mdast-util-directive'); var toMarkdown = require('mdast-util-to-markdown'); +const { matches } = require('unist-util-select'); +const visit = require('unist-util-visit'); function plugin() { return transformer; diff --git a/tools/challenge-parser/parser/plugins/restore-directives.test.js b/tools/challenge-parser/parser/plugins/restore-directives.test.js index f65dbed02f..c2ee139f5e 100644 --- a/tools/challenge-parser/parser/plugins/restore-directives.test.js +++ b/tools/challenge-parser/parser/plugins/restore-directives.test.js @@ -1,9 +1,9 @@ const cloneDeep = require('lodash/cloneDeep'); -const { selectAll } = require('unist-util-select'); const find = require('unist-util-find'); +const { selectAll } = require('unist-util-select'); -const restoreDirectives = require('./restore-directives'); const directivesOriginalAST = require('../__fixtures__/ast-directives.json'); +const restoreDirectives = require('./restore-directives'); describe('restore-directives', () => { let directivesAST; diff --git a/tools/challenge-parser/parser/plugins/table-and-strikethrough.js b/tools/challenge-parser/parser/plugins/table-and-strikethrough.js index 1911163004..e1a1472f1e 100644 --- a/tools/challenge-parser/parser/plugins/table-and-strikethrough.js +++ b/tools/challenge-parser/parser/plugins/table-and-strikethrough.js @@ -1,8 +1,8 @@ 'use strict'; +var fromMarkdown = require('mdast-util-gfm/from-markdown'); var strikethrough = require('micromark-extension-gfm-strikethrough'); var table = require('micromark-extension-gfm-table'); -var fromMarkdown = require('mdast-util-gfm/from-markdown'); module.exports = tableAndStrikethrough; diff --git a/tools/challenge-parser/parser/plugins/utils/between-headings.js b/tools/challenge-parser/parser/plugins/utils/between-headings.js index a8c920db78..ecea2493fa 100644 --- a/tools/challenge-parser/parser/plugins/utils/between-headings.js +++ b/tools/challenge-parser/parser/plugins/utils/between-headings.js @@ -1,7 +1,7 @@ -const between = require('unist-util-find-all-between'); const find = require('unist-util-find'); const findAfter = require('unist-util-find-after'); const findAllAfter = require('unist-util-find-all-after'); +const between = require('unist-util-find-all-between'); function getAllBetween(tree, marker) { const start = find(tree, { diff --git a/tools/challenge-parser/parser/plugins/utils/between-headings.test.js b/tools/challenge-parser/parser/plugins/utils/between-headings.test.js index 164c401e61..07fe93c198 100644 --- a/tools/challenge-parser/parser/plugins/utils/between-headings.test.js +++ b/tools/challenge-parser/parser/plugins/utils/between-headings.test.js @@ -1,10 +1,10 @@ const isArray = require('lodash/isArray'); -const find = require('unist-util-find'); const { root } = require('mdast-builder'); +const find = require('unist-util-find'); -const getAllBetween = require('./between-headings'); -const simpleAst = require('../../__fixtures__/ast-simple.json'); const extraHeadingAst = require('../../__fixtures__/ast-extra-heading.json'); +const simpleAst = require('../../__fixtures__/ast-simple.json'); +const getAllBetween = require('./between-headings'); describe('between-headings', () => { it('should return an array', () => { diff --git a/tools/challenge-parser/parser/plugins/utils/get-file-visitor.js b/tools/challenge-parser/parser/plugins/utils/get-file-visitor.js index ac8ed23f7f..4f263a55e2 100644 --- a/tools/challenge-parser/parser/plugins/utils/get-file-visitor.js +++ b/tools/challenge-parser/parser/plugins/utils/get-file-visitor.js @@ -1,6 +1,6 @@ +const { isEmpty } = require('lodash'); const is = require('unist-util-is'); const position = require('unist-util-position'); -const { isEmpty } = require('lodash'); const getId = require('./get-id'); diff --git a/tools/challenge-parser/parser/plugins/utils/get-id.test.js b/tools/challenge-parser/parser/plugins/utils/get-id.test.js index affbbc8f8c..26c7647e84 100644 --- a/tools/challenge-parser/parser/plugins/utils/get-id.test.js +++ b/tools/challenge-parser/parser/plugins/utils/get-id.test.js @@ -1,8 +1,8 @@ -const getId = require('./get-id'); const idNode = require('./__fixtures__/id-node.json'); const imageNode = require('./__fixtures__/image-node.json'); const multipleChildrenNode = require('./__fixtures__/multiple-children.json'); const nonIdNode = require('./__fixtures__/non-id-node.json'); +const getId = require('./get-id'); describe('get-id', () => { it('should return a string', () => { diff --git a/tools/challenge-parser/parser/plugins/utils/mdast-to-html.js b/tools/challenge-parser/parser/plugins/utils/mdast-to-html.js index 5fee1f1817..a4034d43f4 100644 --- a/tools/challenge-parser/parser/plugins/utils/mdast-to-html.js +++ b/tools/challenge-parser/parser/plugins/utils/mdast-to-html.js @@ -1,6 +1,6 @@ const hastToHTML = require('hast-util-to-html'); -const mdastToHast = require('mdast-util-to-hast'); const { root } = require('mdast-builder'); +const mdastToHast = require('mdast-util-to-hast'); function mdastToHTML(nodes) { if (!Array.isArray(nodes)) diff --git a/tools/challenge-parser/parser/plugins/utils/mdast-to-html.test.js b/tools/challenge-parser/parser/plugins/utils/mdast-to-html.test.js index 3177adea29..c7b3d1e61b 100644 --- a/tools/challenge-parser/parser/plugins/utils/mdast-to-html.test.js +++ b/tools/challenge-parser/parser/plugins/utils/mdast-to-html.test.js @@ -1,8 +1,8 @@ -const mdastToHTML = require('./mdast-to-html'); +const leadingInlineHTMLNode = require('./__fixtures__/leading-html-node.json'); const mdastMixedNodes = require('./__fixtures__/mdast-mixed-nodes.json'); const mdastWithEmNode = require('./__fixtures__/mdast-with-em.json'); const singleNode = require('./__fixtures__/non-id-node.json'); -const leadingInlineHTMLNode = require('./__fixtures__/leading-html-node.json'); +const mdastToHTML = require('./mdast-to-html'); describe('mdast-to-html', () => { it('should return a string', () => { diff --git a/tools/challenge-parser/parser/tools/full-parse.js b/tools/challenge-parser/parser/tools/full-parse.js index 06bdbb5e47..6e562f9681 100644 --- a/tools/challenge-parser/parser/tools/full-parse.js +++ b/tools/challenge-parser/parser/tools/full-parse.js @@ -1,5 +1,5 @@ -const { inspect } = require('util'); const path = require('path'); +const { inspect } = require('util'); const { parseMD } = require('../index'); diff --git a/tools/challenge-parser/parser/tools/generate-ast.js b/tools/challenge-parser/parser/tools/generate-ast.js index da91fe052f..c777aed35a 100644 --- a/tools/challenge-parser/parser/tools/generate-ast.js +++ b/tools/challenge-parser/parser/tools/generate-ast.js @@ -1,7 +1,7 @@ -const { read } = require('to-vfile'); const remark = require('remark'); const directive = require('remark-directive'); const frontmatter = require('remark-frontmatter'); +const { read } = require('to-vfile'); (async () => { const path = './example.md'; diff --git a/tools/challenge-parser/parser/tools/inspect-ast.js b/tools/challenge-parser/parser/tools/inspect-ast.js index 6024dbff04..6a87201e84 100644 --- a/tools/challenge-parser/parser/tools/inspect-ast.js +++ b/tools/challenge-parser/parser/tools/inspect-ast.js @@ -1,7 +1,7 @@ -const { read } = require('to-vfile'); +const { inspect } = require('util'); const remark = require('remark'); const html = require('remark-html'); -const { inspect } = require('util'); +const { read } = require('to-vfile'); (async () => { const path = './example.md'; diff --git a/tools/challenge-parser/parser/tools/parse-md.js b/tools/challenge-parser/parser/tools/parse-md.js index b656434d0e..43d9b7b134 100644 --- a/tools/challenge-parser/parser/tools/parse-md.js +++ b/tools/challenge-parser/parser/tools/parse-md.js @@ -1,7 +1,7 @@ -const { read } = require('to-vfile'); const remark = require('remark'); const directives = require('remark-directive'); const stringify = require('remark-stringify'); +const { read } = require('to-vfile'); (async () => { const path = './example.md'; diff --git a/tools/challenge-parser/translation-parser/index.test.js b/tools/challenge-parser/translation-parser/index.test.js index 98f097fbc8..74904d322f 100644 --- a/tools/challenge-parser/translation-parser/index.test.js +++ b/tools/challenge-parser/translation-parser/index.test.js @@ -1,12 +1,12 @@ +const { + ENGLISH_CHALLENGE_NO_FILES +} = require('./__fixtures__/challenge-objects'); +const { SIMPLE_TRANSLATION } = require('./__mocks__/mock-comments'); const { translateComments, translateCommentsInChallenge, translateGeneric } = require('.'); -const { - ENGLISH_CHALLENGE_NO_FILES -} = require('./__fixtures__/challenge-objects'); -const { SIMPLE_TRANSLATION } = require('./__mocks__/mock-comments'); let logSpy; diff --git a/tools/crowdin/actions/convert-chinese/index.js b/tools/crowdin/actions/convert-chinese/index.js index 1fb7f49764..9704013507 100644 --- a/tools/crowdin/actions/convert-chinese/index.js +++ b/tools/crowdin/actions/convert-chinese/index.js @@ -1,6 +1,6 @@ /* eslint-disable import/no-unresolved */ -const fs = require('fs-extra'); const path = require('path'); +const fs = require('fs-extra'); const opencc = require('node-opencc'); const getFiles = async (directory, fileList = []) => { diff --git a/tools/crowdin/actions/hide-specific-string/index.js b/tools/crowdin/actions/hide-specific-string/index.js index 1eccdfd103..a3faed2015 100644 --- a/tools/crowdin/actions/hide-specific-string/index.js +++ b/tools/crowdin/actions/hide-specific-string/index.js @@ -1,8 +1,8 @@ require('dotenv').config({ path: `${__dirname}/../../.env` }); +const core = require('@actions/core'); const { getFiles } = require('../../utils/files'); const { getStrings, changeHiddenStatus } = require('../../utils/strings'); // eslint-disable-next-line import/no-unresolved -const core = require('@actions/core'); const filename = core.getInput('filename'); const stringContent = core.getInput('string-content'); diff --git a/tools/crowdin/utils/dirs.js b/tools/crowdin/utils/dirs.js index e853b7a053..e7138555e1 100644 --- a/tools/crowdin/utils/dirs.js +++ b/tools/crowdin/utils/dirs.js @@ -1,6 +1,6 @@ -const makeRequest = require('./make-request'); -const delay = require('./delay'); const authHeader = require('./auth-header'); +const delay = require('./delay'); +const makeRequest = require('./make-request'); const getDirs = async projectId => { let headers = { ...authHeader }; diff --git a/tools/crowdin/utils/files.js b/tools/crowdin/utils/files.js index a380079028..740fd0771c 100644 --- a/tools/crowdin/utils/files.js +++ b/tools/crowdin/utils/files.js @@ -1,5 +1,5 @@ -const makeRequest = require('./make-request'); const authHeader = require('./auth-header'); +const makeRequest = require('./make-request'); const addFile = async (projectId, filename, fileContent, directoryId) => { let headers = { ...authHeader }; diff --git a/tools/scripts/build/build-curriculum.js b/tools/scripts/build/build-curriculum.js index ecba913b2e..7a1c18be94 100644 --- a/tools/scripts/build/build-curriculum.js +++ b/tools/scripts/build/build-curriculum.js @@ -1,5 +1,5 @@ -const path = require('path'); const fs = require('fs'); +const path = require('path'); const { getChallengesForLang } = require('../../../curriculum/getChallenges'); diff --git a/tools/scripts/build/ensure-env.js b/tools/scripts/build/ensure-env.js index f006f01fd3..9704aa5ffd 100644 --- a/tools/scripts/build/ensure-env.js +++ b/tools/scripts/build/ensure-env.js @@ -1,8 +1,8 @@ const fs = require('fs'); const path = require('path'); -const env = require('../../../config/read-env'); const { availableLangs } = require('../../../config/i18n/all-langs'); +const env = require('../../../config/read-env'); const globalConfigPath = path.resolve(__dirname, '../../../config'); diff --git a/tools/scripts/lint/index.js b/tools/scripts/lint/index.js index b29e082118..ebddce8361 100644 --- a/tools/scripts/lint/index.js +++ b/tools/scripts/lint/index.js @@ -1,6 +1,6 @@ -const YAML = require('js-yaml'); const fs = require('fs'); const path = require('path'); +const YAML = require('js-yaml'); const argv = require('yargs').argv; const linter = require('./linter'); diff --git a/tools/scripts/seed/seedAuthUser.js b/tools/scripts/seed/seedAuthUser.js index 4052e61a82..5f9c5fa87b 100644 --- a/tools/scripts/seed/seedAuthUser.js +++ b/tools/scripts/seed/seedAuthUser.js @@ -1,18 +1,15 @@ -const fullyCertifiedUser = require('./certifiedUserData'); - const path = require('path'); -require('dotenv').config({ path: path.resolve(__dirname, '../../../.env') }); -const MongoClient = require('mongodb').MongoClient; -const ObjectId = require('mongodb').ObjectID; const debug = require('debug'); +require('dotenv').config({ path: path.resolve(__dirname, '../../../.env') }); +const { MongoClient, ObjectId } = require('mongodb'); +const defaultUserImage = require('../../../config/misc').defaultUserImage; +const fullyCertifiedUser = require('./certifiedUserData'); const envVariables = process.argv; const log = debug('fcc:tools:seedLocalAuthUser'); const { MONGOHQ_URL } = process.env; -const defaultUserImage = require('../../../config/misc').defaultUserImage; - function handleError(err, client) { if (err) { console.error('Oh noes!! Error seeding local auth user.'); diff --git a/tools/ui-components/rollup.config.js b/tools/ui-components/rollup.config.js index b35ce13170..da488ac6bf 100644 --- a/tools/ui-components/rollup.config.js +++ b/tools/ui-components/rollup.config.js @@ -1,7 +1,7 @@ -import { nodeResolve } from '@rollup/plugin-node-resolve'; import babel from '@rollup/plugin-babel'; -import postcss from 'rollup-plugin-postcss'; import commonjs from '@rollup/plugin-commonjs'; +import { nodeResolve } from '@rollup/plugin-node-resolve'; +import postcss from 'rollup-plugin-postcss'; import { terser } from 'rollup-plugin-terser'; const production = process.env.NODE_ENV !== 'development'; diff --git a/tools/ui-components/src/button.js b/tools/ui-components/src/button.js index 7a7b95c32e..a708187338 100644 --- a/tools/ui-components/src/button.js +++ b/tools/ui-components/src/button.js @@ -1,5 +1,5 @@ -import React from 'react'; import PropTypes from 'prop-types'; +import React from 'react'; import './button.css'; diff --git a/tools/ui-components/src/button.test.js b/tools/ui-components/src/button.test.js index 31f57bb2a0..6c222b4d01 100644 --- a/tools/ui-components/src/button.test.js +++ b/tools/ui-components/src/button.test.js @@ -1,6 +1,6 @@ -import React from 'react'; import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; +import React from 'react'; import { Button } from './button'; diff --git a/tools/ui-components/src/color-system.js b/tools/ui-components/src/color-system.js index 28f906499c..f4540aaa5c 100644 --- a/tools/ui-components/src/color-system.js +++ b/tools/ui-components/src/color-system.js @@ -1,5 +1,5 @@ -import React from 'react'; import PropTypes from 'prop-types'; +import React from 'react'; import colorList from './colors.css'; diff --git a/utils/sort-files.test.js b/utils/sort-files.test.js index 19955ab04d..234aff6979 100644 --- a/utils/sort-files.test.js +++ b/utils/sort-files.test.js @@ -1,5 +1,5 @@ -const { toSortedArray } = require('./sort-files'); const { challengeFiles } = require('./__fixtures__/challenges'); +const { toSortedArray } = require('./sort-files'); describe('sort-files', () => { describe('toSortedArray', () => {