From bed3811952941059488dc19e803128a2b248065b Mon Sep 17 00:00:00 2001 From: Oliver Eyton-Williams Date: Fri, 19 Sep 2025 09:58:06 +0200 Subject: [PATCH] feat(api): migrate to esm (#61915) --- .gitignore | 5 +- .prettierignore | 1 + api/__mocks__/exam-environment-exam.ts | 4 +- api/package.json | 9 +- api/src/app.ts | 50 +++++------ .../routes/daily-coding-challenge.test.ts | 4 +- .../routes/daily-coding-challenge.ts | 4 +- .../daily-coding-challenge/schemas/index.ts | 2 +- api/src/db/extensions.test.ts | 5 +- api/src/db/prisma.ts | 2 +- .../routes/exam-environment.test.ts | 17 ++-- .../routes/exam-environment.ts | 14 +-- .../schemas/exam-environment-exam-attempt.ts | 2 +- .../exam-environment-exam-generated-exam.ts | 2 +- .../schemas/exam-environment-exams.ts | 2 +- api/src/exam-environment/schemas/index.ts | 10 +-- .../exam-environment/schemas/token-meta.ts | 2 +- .../utils/exam-environment.test.ts | 8 +- .../utils/exam-environment.ts | 8 +- api/src/instrument.ts | 2 +- api/src/plugins/__fixtures__/user.ts | 6 +- api/src/plugins/auth-dev.test.ts | 14 +-- api/src/plugins/auth-dev.ts | 6 +- api/src/plugins/auth.test.ts | 11 ++- api/src/plugins/auth.ts | 6 +- api/src/plugins/auth0.test.ts | 20 ++--- api/src/plugins/auth0.ts | 10 +-- api/src/plugins/bouncer.test.ts | 10 +-- api/src/plugins/bouncer.ts | 2 +- api/src/plugins/cookie-update.test.ts | 6 +- api/src/plugins/cookie-update.ts | 2 +- api/src/plugins/cookies.test.ts | 6 +- api/src/plugins/cookies.ts | 4 +- api/src/plugins/cors.test.ts | 2 +- api/src/plugins/cors.ts | 4 +- api/src/plugins/csrf.test.ts | 8 +- api/src/plugins/error-handling.test.ts | 8 +- api/src/plugins/error-handling.ts | 2 +- api/src/plugins/growth-book.test.ts | 4 +- api/src/plugins/growth-book.ts | 2 +- api/src/plugins/mail-providers/nodemailer.ts | 4 +- api/src/plugins/mail-providers/ses.ts | 4 +- api/src/plugins/mailer.test.ts | 2 +- api/src/plugins/not-found.test.ts | 4 +- api/src/plugins/not-found.ts | 2 +- api/src/plugins/redirect-with-message.test.ts | 2 +- api/src/plugins/security.ts | 2 +- api/src/plugins/shadow-capture.ts | 8 +- api/src/routes/helpers/auth-helpers.test.ts | 8 +- api/src/routes/helpers/auth-helpers.ts | 2 +- .../routes/helpers/certificate-utils.test.ts | 3 +- api/src/routes/helpers/certificate-utils.ts | 4 +- .../routes/helpers/challenge-helpers.test.ts | 4 +- api/src/routes/helpers/is-restricted.ts | 2 +- api/src/routes/helpers/user-utils.ts | 4 +- api/src/routes/protected/certificate.test.ts | 5 +- api/src/routes/protected/certificate.ts | 16 ++-- api/src/routes/protected/challenge.test.ts | 28 ++++-- api/src/routes/protected/challenge.ts | 36 ++++---- api/src/routes/protected/donate.test.ts | 4 +- api/src/routes/protected/donate.ts | 6 +- api/src/routes/protected/index.ts | 10 +-- api/src/routes/protected/settings.test.ts | 10 +-- api/src/routes/protected/settings.ts | 16 ++-- api/src/routes/protected/user.test.ts | 18 ++-- api/src/routes/protected/user.ts | 31 ++++--- api/src/routes/public/auth-dev.ts | 2 +- api/src/routes/public/auth.test.ts | 8 +- api/src/routes/public/auth.ts | 12 +-- api/src/routes/public/certificate.test.ts | 4 +- api/src/routes/public/certificate.ts | 10 +-- .../public/deprecated-endpoints.test.ts | 4 +- api/src/routes/public/deprecated-endpoints.ts | 2 +- .../public/deprecated-unsubscribe.test.ts | 4 +- .../routes/public/deprecated-unsubscribe.ts | 2 +- api/src/routes/public/donate.test.ts | 2 +- api/src/routes/public/donate.ts | 10 +-- .../routes/public/email-subscription.test.ts | 6 +- api/src/routes/public/email-subscription.ts | 4 +- api/src/routes/public/index.ts | 22 ++--- api/src/routes/public/sentry.ts | 4 +- api/src/routes/public/signout.test.ts | 4 +- api/src/routes/public/signout.ts | 2 +- api/src/routes/public/status.test.ts | 4 +- api/src/routes/public/status.ts | 2 +- api/src/routes/public/user.test.ts | 14 ++- api/src/routes/public/user.ts | 16 ++-- api/src/schema.test.ts | 6 +- api/src/schemas.ts | 90 +++++++++---------- api/src/schemas/certificate/cert-slug.ts | 4 +- .../schemas/certificate/certificate-verify.ts | 2 +- .../challenge/backend-challenge-completed.ts | 2 +- .../challenge/exam-challenge-completed.ts | 2 +- api/src/schemas/challenge/exam.ts | 2 +- .../challenge/modern-challenge-completed.ts | 2 +- .../schemas/challenge/project-completed.ts | 2 +- api/src/schemas/challenge/save-challenge.ts | 2 +- .../schemas/challenge/submit-quiz-attempt.ts | 2 +- api/src/schemas/donate/update-stripe-card.ts | 2 +- .../settings/update-my-classroom-mode.ts | 2 +- api/src/schemas/user/delete-my-account.ts | 2 +- api/src/schemas/user/delete-user-token.ts | 2 +- .../schemas/user/exam-environment-token.ts | 2 +- api/src/schemas/user/get-session-user.ts | 2 +- api/src/schemas/user/post-ms-username.ts | 2 +- api/src/schemas/user/report-user.ts | 2 +- api/src/schemas/user/reset-my-progress.ts | 2 +- api/src/schemas/user/submit-survey.ts | 2 +- api/src/schemas/users/get-public-profile.ts | 2 +- api/src/server.test.ts | 6 +- api/src/server.ts | 6 +- api/src/utils/allowed-origins.ts | 2 +- api/src/utils/common-challenge-functions.ts | 8 +- api/src/utils/env.ts | 2 + api/src/utils/exam-schemas.ts | 2 +- api/src/utils/exam.test.ts | 6 +- api/src/utils/exam.ts | 4 +- api/src/utils/get-challenges.test.ts | 4 +- api/src/utils/get-challenges.ts | 5 +- api/src/utils/index.test.ts | 2 +- api/src/utils/logger.ts | 7 +- api/src/utils/normalize.test.ts | 2 +- api/src/utils/normalize.ts | 6 +- api/src/utils/progress.test.ts | 2 +- api/src/utils/redirection.test.ts | 4 +- api/src/utils/redirection.ts | 6 +- api/src/utils/tokens.test.ts | 2 +- api/src/utils/tokens.ts | 4 +- api/src/utils/validate-donation.test.ts | 2 +- api/src/utils/validation.test.ts | 2 +- .../exam-environment/generate/deprecate.ts | 2 +- api/tools/exam-environment/generate/index.ts | 4 +- api/tools/exam-environment/generate/insert.ts | 2 +- api/tools/exam-environment/seed/index.ts | 4 +- api/tools/exam-environment/test/index.ts | 4 +- api/tsconfig.json | 5 +- api/vitest.utils.test.ts | 2 +- api/vitest.utils.ts | 8 +- client/config/cert-and-project-map.ts | 2 +- client/i18n/config.js | 2 +- client/i18n/locales.test.ts | 8 +- client/i18n/schema-validation.ts | 2 +- client/src/analytics/call-ga.ts | 2 +- client/src/assets/chapter-icon.tsx | 2 +- client/src/assets/superblock-icon.tsx | 2 +- .../client-only-routes/show-certification.tsx | 6 +- .../client-only-routes/show-project-links.tsx | 4 +- .../src/components/Donation/donate-form.tsx | 2 +- .../Donation/donation-modal-body.tsx | 2 +- .../Donation/multi-tier-donation-form.tsx | 2 +- .../components/Donation/patreon-button.tsx | 2 +- .../src/components/Donation/paypal-button.tsx | 2 +- .../components/Donation/stripe-card-form.tsx | 2 +- client/src/components/Donation/types.ts | 2 +- .../components/Donation/wallets-button.tsx | 2 +- .../Header/components/language-list.tsx | 2 +- client/src/components/Map/index.tsx | 2 +- client/src/components/Progress/progress.tsx | 2 +- .../components/app-mount-notifier.test.tsx | 2 +- .../growth-book/codeally-button.tsx | 2 +- .../src/components/growth-book/ona-note.tsx | 2 +- .../profile/components/heat-map.test.tsx | 2 +- .../profile/components/heat-map.tsx | 2 +- .../profile/components/time-line.tsx | 6 +- .../profile/components/username.tsx | 2 +- .../profile/components/utils/certification.ts | 2 +- .../profile/components/utils/utils.ts | 2 +- client/src/components/seo/index.tsx | 2 +- .../src/components/settings/certification.tsx | 4 +- client/src/html.tsx | 2 +- client/src/pages/catalog.tsx | 2 +- client/src/pages/donate.tsx | 2 +- client/src/redux/donation-saga.js | 2 +- client/src/redux/failed-updates-epic.js | 2 +- client/src/redux/index.js | 2 +- client/src/redux/prop-types.ts | 6 +- client/src/redux/save-challenge-saga.js | 2 +- client/src/redux/settings/settings-sagas.js | 2 +- .../Challenges/classic/desktop-layout.tsx | 2 +- .../templates/Challenges/classic/editor.tsx | 2 +- .../src/templates/Challenges/classic/show.tsx | 2 +- .../templates/Challenges/codeally/show.tsx | 4 +- .../Challenges/components/reset-modal.tsx | 2 +- .../Challenges/components/tool-panel.tsx | 2 +- .../src/templates/Challenges/generic/show.tsx | 2 +- .../Challenges/ms-trophy/link-ms-user.tsx | 2 +- .../Challenges/projects/solution-form.tsx | 2 +- client/src/templates/Challenges/quiz/show.tsx | 2 +- .../Challenges/rechallenge/transformers.js | 2 +- .../Challenges/redux/code-storage-epic.js | 4 +- .../Challenges/redux/completion-epic.js | 4 +- .../Challenges/redux/create-question-epic.js | 2 +- .../redux/execute-challenge-saga.js | 2 +- .../src/templates/Challenges/redux/index.js | 2 +- .../templates/Challenges/redux/selectors.js | 2 +- .../src/templates/Challenges/utils/build.ts | 2 +- .../Introduction/components/block-header.tsx | 2 +- .../Introduction/components/block-label.tsx | 2 +- .../Introduction/components/block.test.tsx | 11 ++- .../Introduction/components/block.tsx | 11 ++- .../components/cert-challenge.tsx | 4 +- .../Introduction/components/challenges.tsx | 4 +- .../components/help-translate.tsx | 4 +- .../Introduction/components/legacy-links.tsx | 2 +- .../components/super-block-accordion.tsx | 13 +-- .../components/super-block-intro.tsx | 2 +- .../Introduction/super-block-intro.tsx | 9 +- .../__fixtures__/completed-challenges.ts | 2 +- client/src/utils/ajax.ts | 2 +- client/src/utils/curriculum-layout.ts | 2 +- client/src/utils/is-a-cert.ts | 2 +- client/src/utils/math-jax.ts | 2 +- client/src/utils/path-parsers.ts | 2 +- client/src/utils/solution-display-type.ts | 2 +- client/tools/create-env.ts | 6 +- client/tools/generate-search-placeholder.ts | 2 +- client/utils/gatsby/challenge-page-creator.js | 2 +- client/utils/index.ts | 2 +- curriculum/build-superblock.js | 4 +- curriculum/build-superblock.test.js | 2 +- curriculum/get-challenges.js | 2 +- curriculum/schema/challenge-schema.js | 4 +- curriculum/test/utils/sort-challenges.test.js | 2 +- curriculum/utils.js | 8 +- curriculum/utils.test.ts | 2 +- docker/api/Dockerfile | 10 +-- eslint.config.mjs | 3 +- jest.config.js | 3 +- pnpm-lock.yaml | 58 +++++++----- shared/config/certification-settings.ts | 2 +- shared/config/curriculum.ts | 2 +- shared/package.json | 1 + shared/tsconfig.json | 2 + shared/utils/is-audited.ts | 5 +- .../build-external-curricula-data-v1.test.ts | 2 +- .../build/build-external-curricula-data-v1.ts | 4 +- .../build-external-curricula-data-v2.test.ts | 2 +- .../build/build-external-curricula-data-v2.ts | 6 +- .../scripts/build/external-data-schema-v1.js | 2 +- .../scripts/build/external-data-schema-v2.js | 2 +- 240 files changed, 664 insertions(+), 610 deletions(-) diff --git a/.gitignore b/.gitignore index 761af5be87d..2c727bbe7ad 100644 --- a/.gitignore +++ b/.gitignore @@ -153,10 +153,6 @@ jspm_packages/ ### Generated config files ### shared/config/curriculum.json -shared/config/*.js - -### Generated utils files ### -shared/utils/*.js ### Old Generated files ### # These files are no longer generated by the client, but can @@ -200,6 +196,7 @@ curriculum/curricula.json curriculum/dist curriculum/build curriculum/test/blocks-generated +shared-dist ### Playwright ### diff --git a/.prettierignore b/.prettierignore index b940bf181fd..19047f4dd3d 100644 --- a/.prettierignore +++ b/.prettierignore @@ -19,3 +19,4 @@ shared/utils/get-lines.test.js shared/utils/is-audited.js shared/utils/validate.js shared/utils/validate.test.js +shared-dist diff --git a/api/__mocks__/exam-environment-exam.ts b/api/__mocks__/exam-environment-exam.ts index a91d83e4397..3f924469260 100644 --- a/api/__mocks__/exam-environment-exam.ts +++ b/api/__mocks__/exam-environment-exam.ts @@ -9,8 +9,8 @@ import { ExamEnvironmentChallenge } from '@prisma/client'; import { ObjectId } from 'mongodb'; -import { defaultUserId } from '../vitest.utils'; -import { examEnvironmentPostExamAttempt } from '../src/exam-environment/schemas'; +import { defaultUserId } from '../vitest.utils.js'; +import { examEnvironmentPostExamAttempt } from '../src/exam-environment/schemas/index.js'; export const oid = () => new ObjectId().toString(); diff --git a/api/package.json b/api/package.json index fe1bd14f6e0..60dcab7afa2 100644 --- a/api/package.json +++ b/api/package.json @@ -17,8 +17,8 @@ "@sentry/node": "9.1.0", "@sinclair/typebox": "^0.34.33", "@types/pino": "^7.0.5", - "ajv": "8.12.0", - "ajv-formats": "2.1.1", + "ajv": "8.17.1", + "ajv-formats": "3.0.1", "date-fns": "4.1.0", "date-fns-tz": "3.2.0", "dotenv": "16.4.5", @@ -28,6 +28,7 @@ "joi": "17.12.2", "jsonwebtoken": "9.0.2", "lodash": "4.17.21", + "lodash-es": "4.17.21", "mongodb": "6.10.0", "nanoid": "3", "no-profanity": "1.5.1", @@ -36,12 +37,13 @@ "pino-pretty": "10.2.3", "query-string": "7.1.3", "stripe": "16.0.0", - "validator": "13.11.0" + "validator": "13.15.15" }, "description": "The freeCodeCamp.org open-source codebase and curriculum", "devDependencies": { "@total-typescript/ts-reset": "0.5.1", "@types/jsonwebtoken": "9.0.5", + "@types/lodash-es": "^4.17.12", "@types/nodemailer": "6.4.14", "@types/supertest": "2.0.16", "@types/validator": "13.11.2", @@ -62,6 +64,7 @@ "license": "BSD-3-Clause", "main": "none", "name": "@freecodecamp/api", + "type": "module", "private": true, "repository": { "type": "git", diff --git a/api/src/app.ts b/api/src/app.ts index dff2cdfbc93..7bc931a8cdc 100644 --- a/api/src/app.ts +++ b/api/src/app.ts @@ -3,8 +3,6 @@ import fastifyAccepts from '@fastify/accepts'; import fastifySwagger from '@fastify/swagger'; import fastifySwaggerUI from '@fastify/swagger-ui'; import type { TypeBoxTypeProvider } from '@fastify/type-provider-typebox'; -import Ajv from 'ajv'; -import addFormats from 'ajv-formats'; import uriResolver from 'fast-uri'; import Fastify, { FastifyBaseLogger, @@ -14,25 +12,27 @@ import Fastify, { RawRequestDefaultExpression, RawServerDefault } from 'fastify'; +import { Ajv } from 'ajv'; +import addFormats from 'ajv-formats'; -import prismaPlugin from './db/prisma'; -import cookies from './plugins/cookies'; -import cors from './plugins/cors'; -import { NodemailerProvider } from './plugins/mail-providers/nodemailer'; -import { SESProvider } from './plugins/mail-providers/ses'; -import mailer from './plugins/mailer'; -import redirectWithMessage from './plugins/redirect-with-message'; -import security from './plugins/security'; -import auth from './plugins/auth'; -import bouncer from './plugins/bouncer'; -import errorHandling from './plugins/error-handling'; -import csrf from './plugins/csrf'; -import notFound from './plugins/not-found'; -import shadowCapture from './plugins/shadow-capture'; -import growthBook from './plugins/growth-book'; +import prismaPlugin from './db/prisma.js'; +import cookies from './plugins/cookies.js'; +import cors from './plugins/cors.js'; +import { NodemailerProvider } from './plugins/mail-providers/nodemailer.js'; +import { SESProvider } from './plugins/mail-providers/ses.js'; +import mailer from './plugins/mailer.js'; +import redirectWithMessage from './plugins/redirect-with-message.js'; +import security from './plugins/security.js'; +import auth from './plugins/auth.js'; +import bouncer from './plugins/bouncer.js'; +import errorHandling from './plugins/error-handling.js'; +import csrf from './plugins/csrf.js'; +import notFound from './plugins/not-found.js'; +import shadowCapture from './plugins/shadow-capture.js'; +import growthBook from './plugins/growth-book.js'; -import * as publicRoutes from './routes/public'; -import * as protectedRoutes from './routes/protected'; +import * as publicRoutes from './routes/public/index.js'; +import * as protectedRoutes from './routes/protected/index.js'; import { API_LOCATION, @@ -44,14 +44,14 @@ import { FCC_ENABLE_SENTRY_ROUTES, GROWTHBOOK_FASTIFY_API_HOST, GROWTHBOOK_FASTIFY_CLIENT_KEY -} from './utils/env'; -import { isObjectID } from './utils/validation'; -import { getLogger } from './utils/logger'; +} from './utils/env.js'; +import { isObjectID } from './utils/validation.js'; +import { getLogger } from './utils/logger.js'; import { examEnvironmentOpenRoutes, examEnvironmentValidatedTokenRoutes -} from './exam-environment/routes/exam-environment'; -import { dailyCodingChallengeRoutes } from './daily-coding-challenge/routes/daily-coding-challenge'; +} from './exam-environment/routes/exam-environment.js'; +import { dailyCodingChallengeRoutes } from './daily-coding-challenge/routes/daily-coding-challenge.js'; type FastifyInstanceWithTypeProvider = FastifyInstance< RawServerDefault, @@ -74,7 +74,7 @@ const ajv = new Ajv({ }); // add the default formatters from avj-formats -addFormats(ajv); +addFormats.default(ajv); ajv.addFormat('objectid', { type: 'string', validate: (str: string) => isObjectID(str) diff --git a/api/src/daily-coding-challenge/routes/daily-coding-challenge.test.ts b/api/src/daily-coding-challenge/routes/daily-coding-challenge.test.ts index 422c69e620c..ad903d77d10 100644 --- a/api/src/daily-coding-challenge/routes/daily-coding-challenge.test.ts +++ b/api/src/daily-coding-challenge/routes/daily-coding-challenge.test.ts @@ -1,8 +1,8 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { addDays } from 'date-fns'; -import { setupServer, superRequest } from '../../../vitest.utils'; -import { getNowUsCentral, getUtcMidnight } from '../utils/helpers'; +import { setupServer, superRequest } from '../../../vitest.utils.js'; +import { getNowUsCentral, getUtcMidnight } from '../utils/helpers.js'; function dateToDateParam(date: Date): string { return date.toISOString().split('T')[0] as string; diff --git a/api/src/daily-coding-challenge/routes/daily-coding-challenge.ts b/api/src/daily-coding-challenge/routes/daily-coding-challenge.ts index 07c05759ebd..88d080112fc 100644 --- a/api/src/daily-coding-challenge/routes/daily-coding-challenge.ts +++ b/api/src/daily-coding-challenge/routes/daily-coding-challenge.ts @@ -1,11 +1,11 @@ import { type FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox'; -import * as schemas from '../schemas'; +import * as schemas from '../schemas/index.js'; import { getNowUsCentral, getUtcMidnight, dateStringToUtcMidnight -} from '../utils/helpers'; +} from '../utils/helpers.js'; /** * Plugin containing public GET routes for the daily coding challenges. diff --git a/api/src/daily-coding-challenge/schemas/index.ts b/api/src/daily-coding-challenge/schemas/index.ts index cd034dec6ac..2d5f34f91dd 100644 --- a/api/src/daily-coding-challenge/schemas/index.ts +++ b/api/src/daily-coding-challenge/schemas/index.ts @@ -1 +1 @@ -export { dailyCodingChallenge } from './daily-coding-challenge'; +export { dailyCodingChallenge } from './daily-coding-challenge.js'; diff --git a/api/src/db/extensions.test.ts b/api/src/db/extensions.test.ts index 7eee91cfb5c..9f0a3bceb1b 100644 --- a/api/src/db/extensions.test.ts +++ b/api/src/db/extensions.test.ts @@ -1,6 +1,7 @@ import { describe, it, expect, beforeEach, afterAll } from 'vitest'; -import { defaultUserEmail, setupServer } from '../../vitest.utils'; -import { createUserInput } from '../utils/create-user'; +import { defaultUserEmail, setupServer } from '../../vitest.utils.js'; + +import { createUserInput } from '../utils/create-user.js'; describe('prisma client extensions', () => { setupServer(); diff --git a/api/src/db/prisma.ts b/api/src/db/prisma.ts index 397df5b78b1..506fdcaed6f 100644 --- a/api/src/db/prisma.ts +++ b/api/src/db/prisma.ts @@ -3,7 +3,7 @@ import { FastifyPluginAsync } from 'fastify'; import { PrismaClient } from '@prisma/client'; // importing MONGOHQ_URL so we can mock it in testing. -import { MONGOHQ_URL } from '../utils/env'; +import { MONGOHQ_URL } from '../utils/env.js'; declare module 'fastify' { interface FastifyInstance { diff --git a/api/src/exam-environment/routes/exam-environment.test.ts b/api/src/exam-environment/routes/exam-environment.test.ts index 9ea214794fe..59e92dc14ed 100644 --- a/api/src/exam-environment/routes/exam-environment.test.ts +++ b/api/src/exam-environment/routes/exam-environment.test.ts @@ -17,17 +17,17 @@ import { defaultUserId, devLogin, setupServer -} from '../../../vitest.utils'; +} from '../../../vitest.utils.js'; import { examEnvironmentPostExamAttempt, examEnvironmentPostExamGeneratedExam -} from '../schemas'; -import * as mock from '../../../__mocks__/exam-environment-exam'; -import { constructUserExam } from '../utils/exam-environment'; -import { JWT_SECRET } from '../../utils/env'; +} from '../schemas/index.js'; +import * as mock from '../../../__mocks__/exam-environment-exam.js'; +import { constructUserExam } from '../utils/exam-environment.js'; +import { JWT_SECRET } from '../../utils/env.js'; vi.mock('../../utils/env', async importOriginal => { - const actual = await importOriginal(); + const actual = await importOriginal(); return { ...actual, FCC_ENABLE_EXAM_ENVIRONMENT: 'true', @@ -531,7 +531,10 @@ describe('/exam-environment/', () => { it('should unwind (delete) the exam attempt if the user exam cannot be constructed', async () => { const _mockConstructUserExam = vi - .spyOn(await import('../utils/exam-environment'), 'constructUserExam') + .spyOn( + await import('../utils/exam-environment.js'), + 'constructUserExam' + ) .mockImplementationOnce(() => { throw new Error('Test error'); }); diff --git a/api/src/exam-environment/routes/exam-environment.ts b/api/src/exam-environment/routes/exam-environment.ts index a607523b56a..50bdc85000b 100644 --- a/api/src/exam-environment/routes/exam-environment.ts +++ b/api/src/exam-environment/routes/exam-environment.ts @@ -1,22 +1,22 @@ /* eslint-disable jsdoc/require-returns, jsdoc/require-param */ import { type FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox'; -import { PrismaClientValidationError } from '@prisma/client/runtime/library'; +import { PrismaClientValidationError } from '@prisma/client/runtime/library.js'; import { type FastifyInstance, type FastifyReply } from 'fastify'; import { ExamEnvironmentExamModerationStatus } from '@prisma/client'; import jwt from 'jsonwebtoken'; -import * as schemas from '../schemas'; -import { mapErr, syncMapErr, UpdateReqType } from '../../utils'; -import { JWT_SECRET } from '../../utils/env'; +import * as schemas from '../schemas/index.js'; +import { mapErr, syncMapErr, UpdateReqType } from '../../utils/index.js'; +import { JWT_SECRET } from '../../utils/env.js'; import { checkPrerequisites, constructEnvExamAttempt, constructUserExam, userAttemptToDatabaseAttemptQuestionSets, validateAttempt -} from '../utils/exam-environment'; -import { ERRORS } from '../utils/errors'; -import { isObjectID } from '../../utils/validation'; +} from '../utils/exam-environment.js'; +import { ERRORS } from '../utils/errors.js'; +import { isObjectID } from '../../utils/validation.js'; /** * Wrapper for endpoints related to the exam environment desktop app. diff --git a/api/src/exam-environment/schemas/exam-environment-exam-attempt.ts b/api/src/exam-environment/schemas/exam-environment-exam-attempt.ts index b296f234ef7..5ad85a310e2 100644 --- a/api/src/exam-environment/schemas/exam-environment-exam-attempt.ts +++ b/api/src/exam-environment/schemas/exam-environment-exam-attempt.ts @@ -1,5 +1,5 @@ import { Type } from '@fastify/type-provider-typebox'; -import { STANDARD_ERROR } from '../utils/errors'; +import { STANDARD_ERROR } from '../utils/errors.js'; export const examEnvironmentPostExamAttempt = { body: Type.Object({ diff --git a/api/src/exam-environment/schemas/exam-environment-exam-generated-exam.ts b/api/src/exam-environment/schemas/exam-environment-exam-generated-exam.ts index 4e3ba3926d5..18cfdcd6688 100644 --- a/api/src/exam-environment/schemas/exam-environment-exam-generated-exam.ts +++ b/api/src/exam-environment/schemas/exam-environment-exam-generated-exam.ts @@ -1,5 +1,5 @@ import { Type } from '@fastify/type-provider-typebox'; -import { STANDARD_ERROR } from '../utils/errors'; +import { STANDARD_ERROR } from '../utils/errors.js'; export const examEnvironmentPostExamGeneratedExam = { body: Type.Object({ diff --git a/api/src/exam-environment/schemas/exam-environment-exams.ts b/api/src/exam-environment/schemas/exam-environment-exams.ts index 7189f49cf1e..5bf153bf6eb 100644 --- a/api/src/exam-environment/schemas/exam-environment-exams.ts +++ b/api/src/exam-environment/schemas/exam-environment-exams.ts @@ -1,5 +1,5 @@ import { Type } from '@fastify/type-provider-typebox'; -import { STANDARD_ERROR } from '../utils/errors'; +import { STANDARD_ERROR } from '../utils/errors.js'; export const examEnvironmentExams = { headers: Type.Object({ 'exam-environment-authorization-token': Type.String() diff --git a/api/src/exam-environment/schemas/index.ts b/api/src/exam-environment/schemas/index.ts index 294fb3846ca..779426dfe85 100644 --- a/api/src/exam-environment/schemas/index.ts +++ b/api/src/exam-environment/schemas/index.ts @@ -3,8 +3,8 @@ export { examEnvironmentGetExamAttempts, examEnvironmentGetExamAttempt, examEnvironmentGetExamAttemptsByExamId -} from './exam-environment-exam-attempt'; -export { examEnvironmentPostExamGeneratedExam } from './exam-environment-exam-generated-exam'; -export { examEnvironmentTokenMeta } from './token-meta'; -export { examEnvironmentExams } from './exam-environment-exams'; -export { examEnvironmentGetExamMappingsByChallengeId } from './challenges'; +} from './exam-environment-exam-attempt.js'; +export { examEnvironmentPostExamGeneratedExam } from './exam-environment-exam-generated-exam.js'; +export { examEnvironmentTokenMeta } from './token-meta.js'; +export { examEnvironmentExams } from './exam-environment-exams.js'; +export { examEnvironmentGetExamMappingsByChallengeId } from './challenges.js'; diff --git a/api/src/exam-environment/schemas/token-meta.ts b/api/src/exam-environment/schemas/token-meta.ts index d98095045a8..0df2f718d19 100644 --- a/api/src/exam-environment/schemas/token-meta.ts +++ b/api/src/exam-environment/schemas/token-meta.ts @@ -1,5 +1,5 @@ import { Type } from '@fastify/type-provider-typebox'; -import { STANDARD_ERROR } from '../utils/errors'; +import { STANDARD_ERROR } from '../utils/errors.js'; export const examEnvironmentTokenMeta = { headers: Type.Object({ diff --git a/api/src/exam-environment/utils/exam-environment.test.ts b/api/src/exam-environment/utils/exam-environment.test.ts index 111dbe68370..c368fbeb147 100644 --- a/api/src/exam-environment/utils/exam-environment.test.ts +++ b/api/src/exam-environment/utils/exam-environment.test.ts @@ -9,9 +9,9 @@ import { examAttempt, generatedExam, oid -} from '../../../__mocks__/exam-environment-exam'; -import * as schemas from '../schemas'; -import { setupServer } from '../../../vitest.utils'; +} from '../../../__mocks__/exam-environment-exam.js'; +import * as schemas from '../schemas/index.js'; +import { setupServer } from '../../../vitest.utils.js'; import { checkAttemptAgainstGeneratedExam, checkPrerequisites, @@ -21,7 +21,7 @@ import { validateAttempt, compareAnswers, shuffleArray -} from './exam-environment'; +} from './exam-environment.js'; // NOTE: Whilst the tests could be run against a single generation of exam, // it is more useful to run the tests against a new generation each time. diff --git a/api/src/exam-environment/utils/exam-environment.ts b/api/src/exam-environment/utils/exam-environment.ts index 1054de17877..1e2772254c0 100644 --- a/api/src/exam-environment/utils/exam-environment.ts +++ b/api/src/exam-environment/utils/exam-environment.ts @@ -16,10 +16,10 @@ import { } from '@prisma/client'; import type { FastifyBaseLogger, FastifyInstance } from 'fastify'; import { type Static } from '@fastify/type-provider-typebox'; -import { omit } from 'lodash'; -import * as schemas from '../schemas'; -import { mapErr } from '../../utils'; -import { ERRORS } from './errors'; +import { omit } from 'lodash-es'; +import * as schemas from '../schemas/index.js'; +import { mapErr } from '../../utils/index.js'; +import { ERRORS } from './errors.js'; interface CompletedChallengeId { completedChallenges: { diff --git a/api/src/instrument.ts b/api/src/instrument.ts index 8f88fd39133..05de30f2561 100644 --- a/api/src/instrument.ts +++ b/api/src/instrument.ts @@ -5,7 +5,7 @@ import { DEPLOYMENT_VERSION, SENTRY_DSN, SENTRY_ENVIRONMENT -} from './utils/env'; +} from './utils/env.js'; const shouldIgnoreError = (error: FastifyError): boolean => { return !!error.statusCode && error.statusCode < 500; diff --git a/api/src/plugins/__fixtures__/user.ts b/api/src/plugins/__fixtures__/user.ts index 0d79851410c..9b53ecc246a 100644 --- a/api/src/plugins/__fixtures__/user.ts +++ b/api/src/plugins/__fixtures__/user.ts @@ -1,13 +1,12 @@ import { expect } from 'vitest'; -import { nanoidCharSet } from '../../utils/create-user'; +import { nanoidCharSet } from '../../utils/create-user.js'; const uuidRe = /^[a-f0-9]{8}-([a-f0-9]{4}-){3}[a-f0-9]{12}$/; const fccUuidRe = /^fcc-[a-f0-9]{8}-([a-f0-9]{4}-){3}[a-f0-9]{12}$/; const unsubscribeIdRe = new RegExp(`^[${nanoidCharSet}]{21}$`); const mongodbIdRe = /^[a-f0-9]{24}$/; - // eslint-disable-next-line jsdoc/require-jsdoc export const newUser = (email: string) => ({ about: '', @@ -91,5 +90,4 @@ export const newUser = (email: string) => ({ verificationToken: null, website: null, yearsTopContributor: [] -} -) +}); diff --git a/api/src/plugins/auth-dev.test.ts b/api/src/plugins/auth-dev.test.ts index 811d0568ac5..04c9b28ad09 100644 --- a/api/src/plugins/auth-dev.test.ts +++ b/api/src/plugins/auth-dev.test.ts @@ -8,14 +8,14 @@ import { } from 'vitest'; import Fastify, { FastifyInstance } from 'fastify'; -import { checkCanConnectToDb, defaultUserEmail } from '../../vitest.utils'; -import { HOME_LOCATION } from '../utils/env'; -import { devAuth } from '../plugins/auth-dev'; -import prismaPlugin from '../db/prisma'; -import auth from './auth'; -import cookies from './cookies'; +import { checkCanConnectToDb, defaultUserEmail } from '../../vitest.utils.js'; +import { HOME_LOCATION } from '../utils/env.js'; +import { devAuth } from '../plugins/auth-dev.js'; +import prismaPlugin from '../db/prisma.js'; +import auth from './auth.js'; +import cookies from './cookies.js'; -import { newUser } from './__fixtures__/user'; +import { newUser } from './__fixtures__/user.js'; describe('dev login', () => { let fastify: FastifyInstance; diff --git a/api/src/plugins/auth-dev.ts b/api/src/plugins/auth-dev.ts index 5db34cc837d..247e04abb9a 100644 --- a/api/src/plugins/auth-dev.ts +++ b/api/src/plugins/auth-dev.ts @@ -4,9 +4,9 @@ import { getRedirectParams, getPrefixedLandingPath, haveSamePath -} from '../utils/redirection'; -import { findOrCreateUser } from '../routes/helpers/auth-helpers'; -import { createAccessToken } from '../utils/tokens'; +} from '../utils/redirection.js'; +import { findOrCreateUser } from '../routes/helpers/auth-helpers.js'; +import { createAccessToken } from '../utils/tokens.js'; const trimTrailingSlash = (str: string) => str.endsWith('/') ? str.slice(0, -1) : str; diff --git a/api/src/plugins/auth.test.ts b/api/src/plugins/auth.test.ts index a74eb5128c7..64ae52e024d 100644 --- a/api/src/plugins/auth.test.ts +++ b/api/src/plugins/auth.test.ts @@ -2,10 +2,13 @@ import { describe, test, expect, beforeEach, afterEach } from 'vitest'; import Fastify, { FastifyInstance } from 'fastify'; import jwt from 'jsonwebtoken'; -import { COOKIE_DOMAIN, JWT_SECRET } from '../utils/env'; -import { type Token, createAccessToken } from '../utils/tokens'; -import cookies, { sign as signCookie, unsign as unsignCookie } from './cookies'; -import auth from './auth'; +import { COOKIE_DOMAIN, JWT_SECRET } from '../utils/env.js'; +import { type Token, createAccessToken } from '../utils/tokens.js'; +import cookies, { + sign as signCookie, + unsign as unsignCookie +} from './cookies.js'; +import auth from './auth.js'; async function setupServer() { const fastify = Fastify(); diff --git a/api/src/plugins/auth.ts b/api/src/plugins/auth.ts index 14aee6cf0a8..d59baedc4d1 100644 --- a/api/src/plugins/auth.ts +++ b/api/src/plugins/auth.ts @@ -3,9 +3,9 @@ import fp from 'fastify-plugin'; import jwt from 'jsonwebtoken'; import { type user } from '@prisma/client'; -import { JWT_SECRET } from '../utils/env'; -import { type Token, isExpired } from '../utils/tokens'; -import { ERRORS } from '../exam-environment/utils/errors'; +import { JWT_SECRET } from '../utils/env.js'; +import { type Token, isExpired } from '../utils/tokens.js'; +import { ERRORS } from '../exam-environment/utils/errors.js'; declare module 'fastify' { interface FastifyReply { diff --git a/api/src/plugins/auth0.test.ts b/api/src/plugins/auth0.test.ts index 78946581c1c..cfaabd8c122 100644 --- a/api/src/plugins/auth0.test.ts +++ b/api/src/plugins/auth0.test.ts @@ -11,20 +11,20 @@ import { } from 'vitest'; import Fastify, { FastifyInstance } from 'fastify'; -import { createUserInput } from '../utils/create-user'; -import { AUTH0_DOMAIN, HOME_LOCATION } from '../utils/env'; -import prismaPlugin from '../db/prisma'; -import cookies, { sign, unsign } from './cookies'; -import { auth0Client } from './auth0'; -import redirectWithMessage, { formatMessage } from './redirect-with-message'; -import auth from './auth'; -import bouncer from './bouncer'; -import { newUser } from './__fixtures__/user'; +import { createUserInput } from '../utils/create-user.js'; +import { AUTH0_DOMAIN, HOME_LOCATION } from '../utils/env.js'; +import prismaPlugin from '../db/prisma.js'; +import cookies, { sign, unsign } from './cookies.js'; +import { auth0Client } from './auth0.js'; +import redirectWithMessage, { formatMessage } from './redirect-with-message.js'; +import auth from './auth.js'; +import bouncer from './bouncer.js'; +import { newUser } from './__fixtures__/user.js'; const COOKIE_DOMAIN = 'test.com'; vi.mock('../utils/env', async importOriginal => ({ - ...(await importOriginal()), + ...(await importOriginal()), COOKIE_DOMAIN: 'test.com' })); diff --git a/api/src/plugins/auth0.ts b/api/src/plugins/auth0.ts index 85250fb2955..e284fe2f46b 100644 --- a/api/src/plugins/auth0.ts +++ b/api/src/plugins/auth0.ts @@ -4,7 +4,7 @@ import { Type } from '@sinclair/typebox'; import { Value } from '@sinclair/typebox/value'; import fp from 'fastify-plugin'; -import { isError } from 'lodash'; +import { isError } from 'lodash-es'; import { API_LOCATION, AUTH0_CLIENT_ID, @@ -12,10 +12,10 @@ import { AUTH0_DOMAIN, COOKIE_DOMAIN, HOME_LOCATION -} from '../utils/env'; -import { findOrCreateUser } from '../routes/helpers/auth-helpers'; -import { createAccessToken } from '../utils/tokens'; -import { getLoginRedirectParams } from '../utils/redirection'; +} from '../utils/env.js'; +import { findOrCreateUser } from '../routes/helpers/auth-helpers.js'; +import { createAccessToken } from '../utils/tokens.js'; +import { getLoginRedirectParams } from '../utils/redirection.js'; declare module 'fastify' { interface FastifyInstance { diff --git a/api/src/plugins/bouncer.test.ts b/api/src/plugins/bouncer.test.ts index eb1ae329c5c..307ae569b81 100644 --- a/api/src/plugins/bouncer.test.ts +++ b/api/src/plugins/bouncer.test.ts @@ -11,11 +11,11 @@ import { import Fastify, { type FastifyInstance } from 'fastify'; import { type user } from '@prisma/client'; -import { HOME_LOCATION } from '../utils/env'; -import bouncer from './bouncer'; -import auth from './auth'; -import cookies from './cookies'; -import redirectWithMessage, { formatMessage } from './redirect-with-message'; +import { HOME_LOCATION } from '../utils/env.js'; +import bouncer from './bouncer.js'; +import auth from './auth.js'; +import cookies from './cookies.js'; +import redirectWithMessage, { formatMessage } from './redirect-with-message.js'; let authorizeSpy: MockInstance; diff --git a/api/src/plugins/bouncer.ts b/api/src/plugins/bouncer.ts index f7707e2d377..1a95461d7ed 100644 --- a/api/src/plugins/bouncer.ts +++ b/api/src/plugins/bouncer.ts @@ -4,7 +4,7 @@ import type { FastifyReply } from 'fastify'; import fp from 'fastify-plugin'; -import { getRedirectParams } from '../utils/redirection'; +import { getRedirectParams } from '../utils/redirection.js'; declare module 'fastify' { interface FastifyInstance { diff --git a/api/src/plugins/cookie-update.test.ts b/api/src/plugins/cookie-update.test.ts index c527c280cf7..1503103df2b 100644 --- a/api/src/plugins/cookie-update.test.ts +++ b/api/src/plugins/cookie-update.test.ts @@ -1,10 +1,10 @@ import { describe, test, expect, beforeEach, afterEach, vi } from 'vitest'; import Fastify, { FastifyInstance } from 'fastify'; -import cookies, { type CookieSerializeOptions, sign } from './cookies'; -import { cookieUpdate } from './cookie-update'; +import cookies, { type CookieSerializeOptions, sign } from './cookies.js'; +import { cookieUpdate } from './cookie-update.js'; vi.mock('../utils/env', async importOriginal => { - const actual = await importOriginal(); + const actual = await importOriginal(); return { ...actual, COOKIE_DOMAIN: 'www.example.com', diff --git a/api/src/plugins/cookie-update.ts b/api/src/plugins/cookie-update.ts index 4fd2fcd870b..6ac44544220 100644 --- a/api/src/plugins/cookie-update.ts +++ b/api/src/plugins/cookie-update.ts @@ -1,6 +1,6 @@ import { FastifyPluginCallback } from 'fastify'; -import type { CookieSerializeOptions } from './cookies'; +import type { CookieSerializeOptions } from './cookies.js'; type Options = { cookies: string[]; attributes: CookieSerializeOptions }; diff --git a/api/src/plugins/cookies.test.ts b/api/src/plugins/cookies.test.ts index 116d181ce0e..48cb72cf626 100644 --- a/api/src/plugins/cookies.test.ts +++ b/api/src/plugins/cookies.test.ts @@ -2,11 +2,11 @@ import { describe, test, expect, beforeEach, afterEach, vi } from 'vitest'; import Fastify, { type FastifyInstance } from 'fastify'; import fastifyCookie from '@fastify/cookie'; -import { COOKIE_SECRET } from '../utils/env'; -import cookies from './cookies'; +import { COOKIE_SECRET } from '../utils/env.js'; +import cookies from './cookies.js'; vi.mock('../utils/env', async importOriginal => { - const actual = await importOriginal(); + const actual = await importOriginal(); return { ...actual, COOKIE_DOMAIN: 'www.example.com', diff --git a/api/src/plugins/cookies.ts b/api/src/plugins/cookies.ts index 9188828119c..b33f52b9576 100644 --- a/api/src/plugins/cookies.ts +++ b/api/src/plugins/cookies.ts @@ -6,8 +6,8 @@ import { COOKIE_DOMAIN, COOKIE_SECRET, FREECODECAMP_NODE_ENV -} from '../utils/env'; -import { CSRF_COOKIE, CSRF_SECRET_COOKIE } from './csrf'; +} from '../utils/env.js'; +import { CSRF_COOKIE, CSRF_SECRET_COOKIE } from './csrf.js'; export { type CookieSerializeOptions } from '@fastify/cookie'; diff --git a/api/src/plugins/cors.test.ts b/api/src/plugins/cors.test.ts index 73bcb588d5b..f442d364fb9 100644 --- a/api/src/plugins/cors.test.ts +++ b/api/src/plugins/cors.test.ts @@ -1,6 +1,6 @@ import { describe, test, expect, beforeAll, afterAll, vi } from 'vitest'; import Fastify, { FastifyInstance, LogLevel } from 'fastify'; -import cors from './cors'; +import cors from './cors.js'; const NON_DEBUG_LOG_LEVELS: LogLevel[] = [ 'fatal', diff --git a/api/src/plugins/cors.ts b/api/src/plugins/cors.ts index faa52f8b17c..7604cc56b21 100644 --- a/api/src/plugins/cors.ts +++ b/api/src/plugins/cors.ts @@ -1,8 +1,8 @@ import { FastifyPluginCallback } from 'fastify'; import fp from 'fastify-plugin'; -import { HOME_LOCATION } from '../utils/env'; -import { allowedOrigins } from '../utils/allowed-origins'; +import { HOME_LOCATION } from '../utils/env.js'; +import { allowedOrigins } from '../utils/allowed-origins.js'; const cors: FastifyPluginCallback = (fastify, _options, done) => { fastify.options('*', (_req, reply) => { diff --git a/api/src/plugins/csrf.test.ts b/api/src/plugins/csrf.test.ts index 0229178cb38..d6e918a26a7 100644 --- a/api/src/plugins/csrf.test.ts +++ b/api/src/plugins/csrf.test.ts @@ -1,12 +1,12 @@ import { describe, test, expect, beforeEach, vi } from 'vitest'; import Fastify, { type FastifyInstance } from 'fastify'; -import { COOKIE_DOMAIN } from '../utils/env'; -import cookies from './cookies'; -import csrf, { CSRF_COOKIE, CSRF_SECRET_COOKIE } from './csrf'; +import { COOKIE_DOMAIN } from '../utils/env.js'; +import cookies from './cookies.js'; +import csrf, { CSRF_COOKIE, CSRF_SECRET_COOKIE } from './csrf.js'; vi.mock('../utils/env', async importOriginal => { - const actual = await importOriginal(); + const actual = await importOriginal(); return { ...actual, COOKIE_DOMAIN: 'www.example.com', diff --git a/api/src/plugins/error-handling.test.ts b/api/src/plugins/error-handling.test.ts index 37d59033c33..b83a81ae60c 100644 --- a/api/src/plugins/error-handling.test.ts +++ b/api/src/plugins/error-handling.test.ts @@ -13,8 +13,8 @@ import accepts from '@fastify/accepts'; import { http, HttpResponse } from 'msw'; import { setupServer } from 'msw/node'; -vi.mock('../utils/env', async importOriginal => { - const actual = await importOriginal(); +vi.mock('../utils/env.js', async importOriginal => { + const actual = await importOriginal(); return { ...actual, SENTRY_DSN: 'https://anything@goes/123' @@ -22,8 +22,8 @@ vi.mock('../utils/env', async importOriginal => { }); import '../instrument'; -import errorHandling from './error-handling'; -import redirectWithMessage, { formatMessage } from './redirect-with-message'; +import errorHandling from './error-handling.js'; +import redirectWithMessage, { formatMessage } from './redirect-with-message.js'; const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); diff --git a/api/src/plugins/error-handling.ts b/api/src/plugins/error-handling.ts index eaf812d8bfb..9cef23bb5f7 100644 --- a/api/src/plugins/error-handling.ts +++ b/api/src/plugins/error-handling.ts @@ -2,7 +2,7 @@ import type { FastifyPluginCallback } from 'fastify'; import * as Sentry from '@sentry/node'; import fp from 'fastify-plugin'; -import { getRedirectParams } from '../utils/redirection'; +import { getRedirectParams } from '../utils/redirection.js'; declare module 'fastify' { interface FastifyInstance { diff --git a/api/src/plugins/growth-book.test.ts b/api/src/plugins/growth-book.test.ts index 0740d1d8696..09138f94b3f 100644 --- a/api/src/plugins/growth-book.test.ts +++ b/api/src/plugins/growth-book.test.ts @@ -1,9 +1,9 @@ import { describe, test, expect, beforeAll, afterAll, vi } from 'vitest'; import Fastify, { type FastifyInstance } from 'fastify'; -import growthBook from './growth-book'; +import growthBook from './growth-book.js'; vi.mock('../utils/env', async importOriginal => { - const actual = await importOriginal(); + const actual = await importOriginal(); return { ...actual, // We're only interested in the production behaviour diff --git a/api/src/plugins/growth-book.ts b/api/src/plugins/growth-book.ts index 6d81bc19ad4..9b6a77a8534 100644 --- a/api/src/plugins/growth-book.ts +++ b/api/src/plugins/growth-book.ts @@ -2,7 +2,7 @@ import { GrowthBook, Options } from '@growthbook/growthbook'; import { FastifyPluginAsync } from 'fastify'; import fp from 'fastify-plugin'; -import { FREECODECAMP_NODE_ENV } from '../utils/env'; +import { FREECODECAMP_NODE_ENV } from '../utils/env.js'; declare module 'fastify' { interface FastifyInstance { diff --git a/api/src/plugins/mail-providers/nodemailer.ts b/api/src/plugins/mail-providers/nodemailer.ts index 87fdc9d9345..24ce9e82102 100644 --- a/api/src/plugins/mail-providers/nodemailer.ts +++ b/api/src/plugins/mail-providers/nodemailer.ts @@ -1,7 +1,7 @@ import nodemailer, { Transporter } from 'nodemailer'; -import { MailProvider, SendEmailArgs } from '../mailer'; -import { MAILHOG_HOST } from '../../utils/env'; +import { MailProvider, SendEmailArgs } from '../mailer.js'; +import { MAILHOG_HOST } from '../../utils/env.js'; /** * NodemailerProvider is a wrapper around nodemailer that provides a clean diff --git a/api/src/plugins/mail-providers/ses.ts b/api/src/plugins/mail-providers/ses.ts index da97e3abb43..c2e68780f79 100644 --- a/api/src/plugins/mail-providers/ses.ts +++ b/api/src/plugins/mail-providers/ses.ts @@ -4,8 +4,8 @@ import { SendEmailCommand } from '@aws-sdk/client-ses'; -import { MailProvider, SendEmailArgs } from '../mailer'; -import { SES_ID, SES_SECRET, SES_REGION } from '../../utils/env'; +import { MailProvider, SendEmailArgs } from '../mailer.js'; +import { SES_ID, SES_SECRET, SES_REGION } from '../../utils/env.js'; /** * SESProvider is a wrapper around nodemailer that provides a clean interface diff --git a/api/src/plugins/mailer.test.ts b/api/src/plugins/mailer.test.ts index fdedb7f7266..ac529196fd0 100644 --- a/api/src/plugins/mailer.test.ts +++ b/api/src/plugins/mailer.test.ts @@ -1,7 +1,7 @@ import { describe, test, expect, vi } from 'vitest'; import Fastify from 'fastify'; -import mailer from './mailer'; +import mailer from './mailer.js'; describe('mailer', () => { test('should send an email via the provider', async () => { diff --git a/api/src/plugins/not-found.test.ts b/api/src/plugins/not-found.test.ts index 6bfe01798c2..2eb533fda54 100644 --- a/api/src/plugins/not-found.test.ts +++ b/api/src/plugins/not-found.test.ts @@ -2,8 +2,8 @@ import { describe, beforeEach, afterEach, it, expect } from 'vitest'; import Fastify, { type FastifyInstance } from 'fastify'; import accepts from '@fastify/accepts'; -import notFound from './not-found'; -import redirectWithMessage, { formatMessage } from './redirect-with-message'; +import notFound from './not-found.js'; +import redirectWithMessage, { formatMessage } from './redirect-with-message.js'; describe('fourOhFour', () => { let fastify: FastifyInstance; diff --git a/api/src/plugins/not-found.ts b/api/src/plugins/not-found.ts index 19f4928646c..051ab9815b3 100644 --- a/api/src/plugins/not-found.ts +++ b/api/src/plugins/not-found.ts @@ -2,7 +2,7 @@ import type { FastifyPluginCallback } from 'fastify'; import fp from 'fastify-plugin'; -import { getRedirectParams } from '../utils/redirection'; +import { getRedirectParams } from '../utils/redirection.js'; /** * Plugin for handling missing endpoints. diff --git a/api/src/plugins/redirect-with-message.test.ts b/api/src/plugins/redirect-with-message.test.ts index 5078ac88875..a01ee397bb0 100644 --- a/api/src/plugins/redirect-with-message.test.ts +++ b/api/src/plugins/redirect-with-message.test.ts @@ -2,7 +2,7 @@ import { describe, test, expect, beforeEach } from 'vitest'; import Fastify, { FastifyInstance } from 'fastify'; import qs from 'query-string'; -import redirectWithMessage from './redirect-with-message'; +import redirectWithMessage from './redirect-with-message.js'; async function setupServer() { const fastify = Fastify(); diff --git a/api/src/plugins/security.ts b/api/src/plugins/security.ts index 73301d303c2..da9efd51cfc 100644 --- a/api/src/plugins/security.ts +++ b/api/src/plugins/security.ts @@ -1,7 +1,7 @@ import { FastifyPluginCallback } from 'fastify'; import fp from 'fastify-plugin'; -import { FREECODECAMP_NODE_ENV } from '../utils/env'; +import { FREECODECAMP_NODE_ENV } from '../utils/env.js'; const securityHeaders: FastifyPluginCallback = (fastify, _options, done) => { // OWASP recommended headers diff --git a/api/src/plugins/shadow-capture.ts b/api/src/plugins/shadow-capture.ts index 867c188efc4..7d35e8571aa 100644 --- a/api/src/plugins/shadow-capture.ts +++ b/api/src/plugins/shadow-capture.ts @@ -1,11 +1,13 @@ import { randomUUID } from 'crypto'; import { appendFileSync, mkdirSync } from 'fs'; import { join } from 'path'; -import type { FastifyPluginCallback } from 'fastify'; +import type { + FastifyPluginCallback, + FastifyReply, + FastifyRequest +} from 'fastify'; import fp from 'fastify-plugin'; -import { FastifyReply } from 'fastify/types/reply'; -import { FastifyRequest } from 'fastify/types/request'; const LOGS_DIRECTORY = 'logs'; const REQUEST_CAPTURE_FILE = 'request-capture.jsonl'; diff --git a/api/src/routes/helpers/auth-helpers.test.ts b/api/src/routes/helpers/auth-helpers.test.ts index a4f713d3e5e..7c1866d9d08 100644 --- a/api/src/routes/helpers/auth-helpers.test.ts +++ b/api/src/routes/helpers/auth-helpers.test.ts @@ -1,10 +1,10 @@ import { describe, test, expect, beforeAll, afterEach, vi } from 'vitest'; import Fastify, { FastifyInstance } from 'fastify'; -import db from '../../db/prisma'; -import { createUserInput } from '../../utils/create-user'; -import { checkCanConnectToDb } from '../../../vitest.utils'; -import { findOrCreateUser } from './auth-helpers'; +import db from '../../db/prisma.js'; +import { createUserInput } from '../../utils/create-user.js'; +import { checkCanConnectToDb } from '../../../vitest.utils.js'; +import { findOrCreateUser } from './auth-helpers.js'; const captureException = vi.fn(); diff --git a/api/src/routes/helpers/auth-helpers.ts b/api/src/routes/helpers/auth-helpers.ts index 741bcb2998c..f8b3b62853a 100644 --- a/api/src/routes/helpers/auth-helpers.ts +++ b/api/src/routes/helpers/auth-helpers.ts @@ -1,5 +1,5 @@ import { FastifyInstance } from 'fastify'; -import { createUserInput } from '../../utils/create-user'; +import { createUserInput } from '../../utils/create-user.js'; /** * Finds an existing user with the given email or creates a new user if none exists. diff --git a/api/src/routes/helpers/certificate-utils.test.ts b/api/src/routes/helpers/certificate-utils.test.ts index bace35e7b08..84863c4c972 100644 --- a/api/src/routes/helpers/certificate-utils.test.ts +++ b/api/src/routes/helpers/certificate-utils.test.ts @@ -1,5 +1,6 @@ import { describe, test, expect } from 'vitest'; -import { getFallbackFullStackDate } from './certificate-utils'; + +import { getFallbackFullStackDate } from './certificate-utils.js'; const fullStackChallenges = [ { diff --git a/api/src/routes/helpers/certificate-utils.ts b/api/src/routes/helpers/certificate-utils.ts index 01b14f71f7a..28a34d03017 100644 --- a/api/src/routes/helpers/certificate-utils.ts +++ b/api/src/routes/helpers/certificate-utils.ts @@ -2,8 +2,8 @@ import { Prisma } from '@prisma/client'; import { certSlugTypeMap, certIds -} from '../../../../shared/config/certification-settings'; -import { normalizeDate } from '../../utils/normalize'; +} from '../../../../shared/config/certification-settings.js'; +import { normalizeDate } from '../../utils/normalize.js'; const { legacyInfosecQaId, diff --git a/api/src/routes/helpers/challenge-helpers.test.ts b/api/src/routes/helpers/challenge-helpers.test.ts index 65ce015d0cc..0b3c6c0f7f2 100644 --- a/api/src/routes/helpers/challenge-helpers.test.ts +++ b/api/src/routes/helpers/challenge-helpers.test.ts @@ -4,14 +4,14 @@ import type { CompletedChallenge } from '@prisma/client'; -import { createFetchMock } from '../../../vitest.utils'; +import { createFetchMock } from '../../../vitest.utils.js'; import { canSubmitCodeRoadCertProject, verifyTrophyWithMicrosoft, decodeFiles, decodeBase64, encodeBase64 -} from './challenge-helpers'; +} from './challenge-helpers.js'; const id = 'abc'; diff --git a/api/src/routes/helpers/is-restricted.ts b/api/src/routes/helpers/is-restricted.ts index 6e843a64718..3a7c02289c0 100644 --- a/api/src/routes/helpers/is-restricted.ts +++ b/api/src/routes/helpers/is-restricted.ts @@ -1,6 +1,6 @@ import { isProfane } from 'no-profanity'; -import { blocklistedUsernames } from '../../../../shared/config/constants'; +import { blocklistedUsernames } from '../../../../shared/config/constants.js'; /** * Checks if a username is restricted (i.e. It's profane or reserved). diff --git a/api/src/routes/helpers/user-utils.ts b/api/src/routes/helpers/user-utils.ts index 29cb6777bee..3d5ee9d8d60 100644 --- a/api/src/routes/helpers/user-utils.ts +++ b/api/src/routes/helpers/user-utils.ts @@ -1,4 +1,4 @@ -import _ from 'lodash'; +import { pick, omit } from 'lodash-es'; // user flags that the api-server returns as false if they're missing in the // user document. Since Prisma returns null for missing fields, we need to @@ -45,5 +45,5 @@ type NullableFlags = (typeof nullableFlags)[number]; export function splitUser>( user: U ): [Pick, Omit] { - return [_.pick(user, nullableFlags), _.omit(user, nullableFlags)]; + return [pick(user, nullableFlags), omit(user, nullableFlags)]; } diff --git a/api/src/routes/protected/certificate.test.ts b/api/src/routes/protected/certificate.test.ts index c0c58811363..a959088c91f 100644 --- a/api/src/routes/protected/certificate.test.ts +++ b/api/src/routes/protected/certificate.test.ts @@ -7,14 +7,15 @@ import { beforeEach, vi } from 'vitest'; -import { Certification } from '../../../../shared/config/certification-settings'; + +import { Certification } from '../../../../shared/config/certification-settings.js'; import { defaultUserEmail, defaultUserId, devLogin, setupServer, superRequest -} from '../../../vitest.utils'; +} from '../../../vitest.utils.js'; describe('certificate routes', () => { setupServer(); diff --git a/api/src/routes/protected/certificate.ts b/api/src/routes/protected/certificate.ts index 1f81998bdb0..bd9514da38e 100644 --- a/api/src/routes/protected/certificate.ts +++ b/api/src/routes/protected/certificate.ts @@ -1,8 +1,8 @@ import type { CompletedChallenge } from '@prisma/client'; -import isEmail from 'validator/lib/isEmail'; +import validator from 'validator'; import type { FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox'; -import { getChallenges } from '../../utils/get-challenges'; +import { getChallenges } from '../../utils/get-challenges.js'; import { certIds, certSlugTypeMap, @@ -12,13 +12,13 @@ import { legacyCertifications, legacyFullStackCertification, upcomingCertifications -} from '../../../../shared/config/certification-settings'; +} from '../../../../shared/config/certification-settings.js'; -import * as schemas from '../../schemas'; -import { normalizeChallenges, removeNulls } from '../../utils/normalize'; +import * as schemas from '../../schemas.js'; +import { normalizeChallenges, removeNulls } from '../../utils/normalize.js'; -import { SHOW_UPCOMING_CHANGES } from '../../utils/env'; -import { isKnownCertSlug } from '../helpers/certificate-utils'; +import { SHOW_UPCOMING_CHANGES } from '../../utils/env.js'; +import { isKnownCertSlug } from '../helpers/certificate-utils.js'; const { legacyFrontEndChallengeId, @@ -403,7 +403,7 @@ export const protectedCertificateRoutes: FastifyPluginCallbackTypebox = ( .map(x => certSlugTypeMap[x]) .every(certType => updatedIsCertMap[certType]); const shouldSendCertifiedEmailToCamper = - email && isEmail(email) && hasCompletedAllCerts; + email && validator.default.isEmail(email) && hasCompletedAllCerts; if (shouldSendCertifiedEmailToCamper) { const notifyUser = { diff --git a/api/src/routes/protected/challenge.test.ts b/api/src/routes/protected/challenge.test.ts index d3dd44c7604..b78eb875cee 100644 --- a/api/src/routes/protected/challenge.test.ts +++ b/api/src/routes/protected/challenge.test.ts @@ -11,7 +11,7 @@ import { vi.mock('../helpers/challenge-helpers', async () => { const originalModule = await vi.importActual< - typeof import('../helpers/challenge-helpers') + typeof import('../helpers/challenge-helpers.js') >('../helpers/challenge-helpers'); return { @@ -23,12 +23,12 @@ vi.mock('../helpers/challenge-helpers', async () => { /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ -import { omit } from 'lodash'; +import { omit } from 'lodash-es'; import { Static } from '@fastify/type-provider-typebox'; import { DailyCodingChallengeLanguage } from '@prisma/client'; import request from 'supertest'; -import { challengeTypes } from '../../../../shared/config/challenge-types'; +import { challengeTypes } from '../../../../shared/config/challenge-types.js'; import { defaultUserId, devLogin, @@ -38,7 +38,7 @@ import { defaultUserEmail, createSuperRequest, defaultUsername -} from '../../../vitest.utils'; +} from '../../../vitest.utils.js'; import { completedExamChallengeOneCorrect, completedExamChallengeTwoCorrect, @@ -53,16 +53,28 @@ import { examWithTwoCorrect, examWithAllCorrect, type ExamSubmission -} from '../../../__mocks__/exam'; -import { Answer } from '../../utils/exam-types'; -import type { getSessionUser } from '../../schemas/user/get-session-user'; -import { verifyTrophyWithMicrosoft } from '../helpers/challenge-helpers'; +} from '../../../__mocks__/exam.js'; +import { Answer } from '../../utils/exam-types.js'; +import type { getSessionUser } from '../../schemas/user/get-session-user.js'; +import { verifyTrophyWithMicrosoft } from '../helpers/challenge-helpers.js'; const mockVerifyTrophyWithMicrosoft = vi.mocked(verifyTrophyWithMicrosoft); const EXISTING_COMPLETED_DATE = new Date('2024-11-08').getTime(); const DATE_NOW = Date.now(); +vi.mock('../helpers/challenge-helpers.js', async () => { + const originalModule = await vi.importActual< + typeof import('../helpers/challenge-helpers.js') + >('../helpers/challenge-helpers'); + + return { + __esModule: true, + ...originalModule, + verifyTrophyWithMicrosoft: vi.fn() + }; +}); + const isValidChallengeCompletionErrorMsg = { type: 'error', message: 'That does not appear to be a valid challenge submission.' diff --git a/api/src/routes/protected/challenge.ts b/api/src/routes/protected/challenge.ts index 9c553210acd..b68491468c9 100644 --- a/api/src/routes/protected/challenge.ts +++ b/api/src/routes/protected/challenge.ts @@ -1,12 +1,13 @@ import { type FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox'; import jwt from 'jsonwebtoken'; -import { uniqBy, matches } from 'lodash'; import { CompletedExam, ExamResults, SavedChallengeFile } from '@prisma/client'; -import isURL from 'validator/lib/isURL'; import type { FastifyBaseLogger, FastifyInstance, FastifyReply } from 'fastify'; +import { uniqBy, matches } from 'lodash-es'; -import { challengeTypes } from '../../../../shared/config/challenge-types'; -import * as schemas from '../../schemas'; +import validator from 'validator'; + +import { challengeTypes } from '../../../../shared/config/challenge-types.js'; +import * as schemas from '../../schemas.js'; import { jsCertProjectIds, multifileCertProjectIds, @@ -15,28 +16,31 @@ import { type CompletedChallenge, saveUserChallengeData, msTrophyChallenges -} from '../../utils/common-challenge-functions'; -import { JWT_SECRET } from '../../utils/env'; +} from '../../utils/common-challenge-functions.js'; +import { JWT_SECRET } from '../../utils/env.js'; import { formatCoderoadChallengeCompletedValidation, formatProjectCompletedValidation -} from '../../utils/error-formatting'; -import { getChallenges } from '../../utils/get-challenges'; -import { ProgressTimestamp, getPoints } from '../../utils/progress'; +} from '../../utils/error-formatting.js'; +import { getChallenges } from '../../utils/get-challenges.js'; +import { ProgressTimestamp, getPoints } from '../../utils/progress.js'; import { validateExamFromDbSchema, validateGeneratedExamSchema, validateUserCompletedExamSchema, validateExamResultsSchema -} from '../../utils/exam-schemas'; -import { generateRandomExam, createExamResults } from '../../utils/exam'; +} from '../../utils/exam-schemas.js'; +import { generateRandomExam, createExamResults } from '../../utils/exam.js'; import { canSubmitCodeRoadCertProject, decodeFiles, verifyTrophyWithMicrosoft -} from '../helpers/challenge-helpers'; -import { UpdateReqType } from '../../utils'; -import { normalizeChallengeType, normalizeDate } from '../../utils/normalize'; +} from '../helpers/challenge-helpers.js'; +import { UpdateReqType } from '../../utils/index.js'; +import { + normalizeChallengeType, + normalizeDate +} from '../../utils/normalize.js'; interface JwtPayload { userToken: string; @@ -94,7 +98,7 @@ export const challengeRoutes: FastifyPluginCallbackTypebox = ( // - `solution` needs to exist, but does not have to be valid URL // - `githubLink` needs to exist and be valid URL if (challengeType === challengeTypes.backEndProject) { - if (!solution || !isURL(githubLink + '')) { + if (!solution || !validator.default.isURL(githubLink + '')) { logger.warn( { solution, githubLink }, 'Invalid backEndProject submission' @@ -104,7 +108,7 @@ export const challengeRoutes: FastifyPluginCallbackTypebox = ( message: 'That does not appear to be a valid challenge submission.' }); } - } else if (solution && !isURL(solution + '')) { + } else if (solution && !validator.default.isURL(solution + '')) { logger.warn({ solution }, 'Invalid solution URL'); return void reply.code(403).send({ type: 'error', diff --git a/api/src/routes/protected/donate.test.ts b/api/src/routes/protected/donate.test.ts index aa59ee06100..6dfc789062f 100644 --- a/api/src/routes/protected/donate.test.ts +++ b/api/src/routes/protected/donate.test.ts @@ -5,8 +5,8 @@ import { setupServer, defaultUserEmail, defaultUserId -} from '../../../vitest.utils'; -import { createUserInput } from '../../utils/create-user'; +} from '../../../vitest.utils.js'; +import { createUserInput } from '../../utils/create-user.js'; const testEWalletEmail = 'baz@bar.com'; const testSubscriptionId = 'sub_test_id'; diff --git a/api/src/routes/protected/donate.ts b/api/src/routes/protected/donate.ts index c64c7aa6fbb..275daeb6126 100644 --- a/api/src/routes/protected/donate.ts +++ b/api/src/routes/protected/donate.ts @@ -1,9 +1,9 @@ import { type FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox'; import Stripe from 'stripe'; -import * as schemas from '../../schemas'; -import { donationSubscriptionConfig } from '../../../../shared/config/donation-settings'; -import { STRIPE_SECRET_KEY, HOME_LOCATION } from '../../utils/env'; +import * as schemas from '../../schemas.js'; +import { donationSubscriptionConfig } from '../../../../shared/config/donation-settings.js'; +import { STRIPE_SECRET_KEY, HOME_LOCATION } from '../../utils/env.js'; /** * Plugin for the donation endpoints requiring auth. diff --git a/api/src/routes/protected/index.ts b/api/src/routes/protected/index.ts index a09f3664d7e..01c1fb9208e 100644 --- a/api/src/routes/protected/index.ts +++ b/api/src/routes/protected/index.ts @@ -1,5 +1,5 @@ -export * from './certificate'; -export * from './challenge'; -export * from './donate'; -export * from './settings'; -export * from './user'; +export * from './certificate.js'; +export * from './challenge.js'; +export * from './donate.js'; +export * from './settings.js'; +export * from './user.js'; diff --git a/api/src/routes/protected/settings.test.ts b/api/src/routes/protected/settings.test.ts index f1c7297db06..b5b9ee27c42 100644 --- a/api/src/routes/protected/settings.test.ts +++ b/api/src/routes/protected/settings.test.ts @@ -16,15 +16,15 @@ import { createSuperRequest, defaultUserId, defaultUserEmail -} from '../../../vitest.utils'; -import { formatMessage } from '../../plugins/redirect-with-message'; -import { createUserInput } from '../../utils/create-user'; -import { API_LOCATION, HOME_LOCATION } from '../../utils/env'; +} from '../../../vitest.utils.js'; +import { formatMessage } from '../../plugins/redirect-with-message.js'; +import { createUserInput } from '../../utils/create-user.js'; +import { API_LOCATION, HOME_LOCATION } from '../../utils/env.js'; import { isPictureWithProtocol, getWaitMessage, validateSocialUrl -} from './settings'; +} from './settings.js'; const baseProfileUI = { isLocked: false, diff --git a/api/src/routes/protected/settings.ts b/api/src/routes/protected/settings.ts index ef7c4ac911b..8c3fb501ae1 100644 --- a/api/src/routes/protected/settings.ts +++ b/api/src/routes/protected/settings.ts @@ -3,14 +3,12 @@ import type { FastifyInstance } from 'fastify'; import { differenceInMinutes } from 'date-fns'; import validator from 'validator'; -import { isValidUsername } from '../../../../shared/utils/validate'; -import * as schemas from '../../schemas'; -import { createAuthToken, isExpired } from '../../utils/tokens'; -import { API_LOCATION } from '../../utils/env'; -import { getRedirectParams } from '../../utils/redirection'; -import { isRestricted } from '../helpers/is-restricted'; - -const { isEmail } = validator; +import { isValidUsername } from '../../../../shared/utils/validate.js'; +import * as schemas from '../../schemas.js'; +import { createAuthToken, isExpired } from '../../utils/tokens.js'; +import { API_LOCATION } from '../../utils/env.js'; +import { getRedirectParams } from '../../utils/redirection.js'; +import { isRestricted } from '../helpers/is-restricted.js'; type WaitMesssageArgs = { sentAt: Date | null; @@ -768,7 +766,7 @@ export const settingRedirectRoutes: FastifyPluginCallbackTypebox = ( const email = Buffer.from(req.query.email, 'base64').toString(); const { origin } = getRedirectParams(req); - if (!isEmail(email)) { + if (!validator.default.isEmail(email)) { logger.warn(`Invalid email ${email}`); return reply.redirectWithMessage(origin, redirectMessage); } diff --git a/api/src/routes/protected/user.test.ts b/api/src/routes/protected/user.test.ts index 606896162e1..a654b9f47d4 100644 --- a/api/src/routes/protected/user.test.ts +++ b/api/src/routes/protected/user.test.ts @@ -15,9 +15,9 @@ import { import jwt, { JwtPayload } from 'jsonwebtoken'; import { DailyCodingChallengeLanguage, type Prisma } from '@prisma/client'; import { ObjectId } from 'mongodb'; -import _ from 'lodash'; +import { omit } from 'lodash-es'; -import { createUserInput } from '../../utils/create-user'; +import { createUserInput } from '../../utils/create-user.js'; import { defaultUserId, defaultUserEmail, @@ -27,15 +27,15 @@ import { createSuperRequest, defaultUsername, resetDefaultUser -} from '../../../vitest.utils'; -import { JWT_SECRET } from '../../utils/env'; +} from '../../../vitest.utils.js'; +import { JWT_SECRET } from '../../utils/env.js'; import { clearEnvExam, seedEnvExam, seedEnvExamAttempt, seedExamEnvExamAuthToken -} from '../../../__mocks__/exam-environment-exam'; -import { getMsTranscriptApiUrl } from './user'; +} from '../../../__mocks__/exam-environment-exam.js'; +import { getMsTranscriptApiUrl } from './user.js'; const mockedFetch = vi.fn(); vi.spyOn(globalThis, 'fetch').mockImplementation(mockedFetch); @@ -43,7 +43,9 @@ vi.spyOn(globalThis, 'fetch').mockImplementation(mockedFetch); let mockDeploymentEnv = 'staging'; vi.mock('../../utils/env', async () => { const actualEnv = - await vi.importActual('../../utils/env'); + await vi.importActual( + '../../utils/env' + ); return { ...actualEnv, get DEPLOYMENT_ENV() { @@ -828,7 +830,7 @@ describe('userRoutes', () => { const setCookies = res.get('Set-Cookie'); const publicUser = { - ..._.omit(minimalUserData, ['externalId', 'unsubscribeId']), + ...omit(minimalUserData, ['externalId', 'unsubscribeId']), ...computedProperties, id: testUser.id, joinDate: new ObjectId(testUser.id).getTimestamp().toISOString(), diff --git a/api/src/routes/protected/user.ts b/api/src/routes/protected/user.ts index c35cdc8663d..f3fb24d4dfc 100644 --- a/api/src/routes/protected/user.ts +++ b/api/src/routes/protected/user.ts @@ -1,18 +1,17 @@ import type { FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox'; import { ObjectId } from 'mongodb'; -import _ from 'lodash'; import { FastifyInstance, FastifyReply } from 'fastify'; import jwt from 'jsonwebtoken'; -import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library'; +import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library.js'; -import * as schemas from '../../schemas'; -import * as examEnvironmentSchemas from '../../exam-environment/schemas'; -import { createResetProperties } from '../../utils/create-user'; -import { customNanoid } from '../../utils/ids'; -import { encodeUserToken } from '../../utils/tokens'; -import { trimTags } from '../../utils/validation'; -import { generateReportEmail } from '../../utils/email-templates'; -import { splitUser } from '../helpers/user-utils'; +import * as schemas from '../../schemas.js'; +import * as examEnvironmentSchemas from '../../exam-environment/schemas/index.js'; +import { createResetProperties } from '../../utils/create-user.js'; +import { customNanoid } from '../../utils/ids.js'; +import { encodeUserToken } from '../../utils/tokens.js'; +import { trimTags } from '../../utils/validation.js'; +import { generateReportEmail } from '../../utils/email-templates.js'; +import { splitUser } from '../helpers/user-utils.js'; import { normalizeChallenges, normalizeFlags, @@ -20,20 +19,20 @@ import { normalizeSurveys, normalizeTwitter, removeNulls -} from '../../utils/normalize'; -import { mapErr, type UpdateReqType } from '../../utils'; +} from '../../utils/normalize.js'; +import { mapErr, type UpdateReqType } from '../../utils/index.js'; import { getCalendar, getPoints, ProgressTimestamp -} from '../../utils/progress'; -import { DEPLOYMENT_ENV, JWT_SECRET } from '../../utils/env'; +} from '../../utils/progress.js'; +import { DEPLOYMENT_ENV, JWT_SECRET } from '../../utils/env.js'; import { getExamAttemptHandler, getExamAttemptsByExamIdHandler, getExamAttemptsHandler -} from '../../exam-environment/routes/exam-environment'; -import { ERRORS } from '../../exam-environment/utils/errors'; +} from '../../exam-environment/routes/exam-environment.js'; +import { ERRORS } from '../../exam-environment/utils/errors.js'; /** * Helper function to get the api url from the shared transcript link. diff --git a/api/src/routes/public/auth-dev.ts b/api/src/routes/public/auth-dev.ts index 04ec983ff19..81780992edc 100644 --- a/api/src/routes/public/auth-dev.ts +++ b/api/src/routes/public/auth-dev.ts @@ -1,6 +1,6 @@ import type { FastifyPluginCallback } from 'fastify'; -import { devAuth } from '../../plugins/auth-dev'; +import { devAuth } from '../../plugins/auth-dev.js'; /** * Route handler for development login. diff --git a/api/src/routes/public/auth.test.ts b/api/src/routes/public/auth.test.ts index 90f103045d4..3a0ebc0f6f6 100644 --- a/api/src/routes/public/auth.test.ts +++ b/api/src/routes/public/auth.test.ts @@ -4,8 +4,8 @@ import { setupServer, superRequest, createSuperRequest -} from '../../../vitest.utils'; -import { AUTH0_DOMAIN } from '../../utils/env'; +} from '../../../vitest.utils.js'; +import { AUTH0_DOMAIN } from '../../utils/env.js'; const mockedFetch = vi.fn(); vi.spyOn(globalThis, 'fetch').mockImplementation(mockedFetch); @@ -28,7 +28,9 @@ const mockAuth0ValidEmail = () => ({ vi.mock('../../utils/env', async () => { const actual = - await vi.importActual('../../utils/env'); + await vi.importActual( + '../../utils/env' + ); return { ...actual, FCC_ENABLE_DEV_LOGIN_MODE: false diff --git a/api/src/routes/public/auth.ts b/api/src/routes/public/auth.ts index 545b51086ac..55f59417d28 100644 --- a/api/src/routes/public/auth.ts +++ b/api/src/routes/public/auth.ts @@ -1,10 +1,10 @@ import type { FastifyPluginCallback, FastifyRequest } from 'fastify'; -import isEmail from 'validator/lib/isEmail'; +import validator from 'validator'; -import { AUTH0_DOMAIN } from '../../utils/env'; -import { auth0Client } from '../../plugins/auth0'; -import { createAccessToken } from '../../utils/tokens'; -import { findOrCreateUser } from '../helpers/auth-helpers'; +import { AUTH0_DOMAIN } from '../../utils/env.js'; +import { auth0Client } from '../../plugins/auth0.js'; +import { createAccessToken } from '../../utils/tokens.js'; +import { findOrCreateUser } from '../helpers/auth-helpers.js'; const getEmailFromAuth0 = async ( req: FastifyRequest @@ -55,7 +55,7 @@ export const mobileAuth0Routes: FastifyPluginCallback = ( type: 'danger' }); } - if (!isEmail(email)) { + if (!validator.default.isEmail(email)) { logger.error('Email is incorrectly formatted for login'); return reply.status(400).send({ diff --git a/api/src/routes/public/certificate.test.ts b/api/src/routes/public/certificate.test.ts index 8b183aefdf0..6026f7d8e49 100644 --- a/api/src/routes/public/certificate.test.ts +++ b/api/src/routes/public/certificate.test.ts @@ -14,8 +14,8 @@ import { resetDefaultUser, setupServer, superRequest -} from '../../../vitest.utils'; -import { getFallbackFullStackDate } from '../helpers/certificate-utils'; +} from '../../../vitest.utils.js'; +import { getFallbackFullStackDate } from '../helpers/certificate-utils.js'; const DATE_NOW = Date.now(); diff --git a/api/src/routes/public/certificate.ts b/api/src/routes/public/certificate.ts index f9027ec9d43..64923c6e715 100644 --- a/api/src/routes/public/certificate.ts +++ b/api/src/routes/public/certificate.ts @@ -1,19 +1,19 @@ import { type FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox'; -import { find } from 'lodash'; -import * as schemas from '../../schemas'; +import { find } from 'lodash-es'; +import * as schemas from '../../schemas.js'; import { certSlugTypeMap, certTypeTitleMap, certTypeIdMap, completionHours, oldDataVizId -} from '../../../../shared/config/certification-settings'; +} from '../../../../shared/config/certification-settings.js'; import { getFallbackFullStackDate, isKnownCertSlug -} from '../helpers/certificate-utils'; -import { normalizeDate } from '../../utils/normalize'; +} from '../helpers/certificate-utils.js'; +import { normalizeDate } from '../../utils/normalize.js'; /** * Plugin for the unprotected certificate endpoints. diff --git a/api/src/routes/public/deprecated-endpoints.test.ts b/api/src/routes/public/deprecated-endpoints.test.ts index 18f4687c48a..caf1aab2635 100644 --- a/api/src/routes/public/deprecated-endpoints.test.ts +++ b/api/src/routes/public/deprecated-endpoints.test.ts @@ -1,8 +1,8 @@ import request from 'supertest'; import { describe, test, expect } from 'vitest'; -import { setupServer } from '../../../vitest.utils'; -import { endpoints } from './deprecated-endpoints'; +import { setupServer } from '../../../vitest.utils.js'; +import { endpoints } from './deprecated-endpoints.js'; describe('Deprecated endpoints', () => { setupServer(); diff --git a/api/src/routes/public/deprecated-endpoints.ts b/api/src/routes/public/deprecated-endpoints.ts index 757a140eaa2..ba40a9ae8df 100644 --- a/api/src/routes/public/deprecated-endpoints.ts +++ b/api/src/routes/public/deprecated-endpoints.ts @@ -1,6 +1,6 @@ import { type FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox'; -import * as schemas from '../../schemas'; +import * as schemas from '../../schemas.js'; type Endpoints = [string, 'GET' | 'POST'][]; diff --git a/api/src/routes/public/deprecated-unsubscribe.test.ts b/api/src/routes/public/deprecated-unsubscribe.test.ts index 34ddaf1098d..e5e0464b6d6 100644 --- a/api/src/routes/public/deprecated-unsubscribe.test.ts +++ b/api/src/routes/public/deprecated-unsubscribe.test.ts @@ -1,7 +1,7 @@ import { describe, test, expect } from 'vitest'; -import { setupServer, superRequest } from '../../../vitest.utils'; +import { setupServer, superRequest } from '../../../vitest.utils.js'; -import { unsubscribeEndpoints } from './deprecated-unsubscribe'; +import { unsubscribeEndpoints } from './deprecated-unsubscribe.js'; const urlEncodedMessage = '?messages=info%5B0%5D%3DWe%2520are%2520no%2520longer%2520able%2520to%2520process%2520this%2520unsubscription%2520request.%2520Please%2520go%2520to%2520your%2520settings%2520to%2520update%2520your%2520email%2520preferences'; diff --git a/api/src/routes/public/deprecated-unsubscribe.ts b/api/src/routes/public/deprecated-unsubscribe.ts index 5d8a0a65b95..92cd9315ee3 100644 --- a/api/src/routes/public/deprecated-unsubscribe.ts +++ b/api/src/routes/public/deprecated-unsubscribe.ts @@ -1,6 +1,6 @@ import { FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox'; -import { getRedirectParams } from '../../utils/redirection'; +import { getRedirectParams } from '../../utils/redirection.js'; type Endpoint = [string, 'GET' | 'POST']; diff --git a/api/src/routes/public/donate.test.ts b/api/src/routes/public/donate.test.ts index d3f25d01512..867d9faf300 100644 --- a/api/src/routes/public/donate.test.ts +++ b/api/src/routes/public/donate.test.ts @@ -1,5 +1,5 @@ import { describe, test, expect, beforeAll, vi } from 'vitest'; -import { setupServer, superRequest } from '../../../vitest.utils'; +import { setupServer, superRequest } from '../../../vitest.utils.js'; const testEWalletEmail = 'baz@bar.com'; const testSubscriptionId = 'sub_test_id'; diff --git a/api/src/routes/public/donate.ts b/api/src/routes/public/donate.ts index 3af0a1c2bd2..de14e35e28f 100644 --- a/api/src/routes/public/donate.ts +++ b/api/src/routes/public/donate.ts @@ -1,14 +1,14 @@ import { type FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox'; import Stripe from 'stripe'; -import { STRIPE_SECRET_KEY } from '../../utils/env'; +import { STRIPE_SECRET_KEY } from '../../utils/env.js'; import { donationSubscriptionConfig, allStripeProductIdsArray -} from '../../../../shared/config/donation-settings'; -import * as schemas from '../../schemas'; -import { inLastFiveMinutes } from '../../utils/validate-donation'; -import { findOrCreateUser } from '../helpers/auth-helpers'; +} from '../../../../shared/config/donation-settings.js'; +import * as schemas from '../../schemas.js'; +import { inLastFiveMinutes } from '../../utils/validate-donation.js'; +import { findOrCreateUser } from '../helpers/auth-helpers.js'; /** * Plugin for public donation endpoints. diff --git a/api/src/routes/public/email-subscription.test.ts b/api/src/routes/public/email-subscription.test.ts index 5865a16dc6e..22a60bd4c8c 100644 --- a/api/src/routes/public/email-subscription.test.ts +++ b/api/src/routes/public/email-subscription.test.ts @@ -1,9 +1,9 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ import type { Prisma } from '@prisma/client'; import { describe, test, expect } from 'vitest'; -import { setupServer, superRequest } from '../../../vitest.utils'; -import { HOME_LOCATION } from '../../utils/env'; -import { createUserInput } from '../../utils/create-user'; +import { setupServer, superRequest } from '../../../vitest.utils.js'; +import { HOME_LOCATION } from '../../utils/env.js'; +import { createUserInput } from '../../utils/create-user.js'; const urlEncodedInfoMessage1 = '?messages=info%5B0%5D%3DWe%2520could%2520not%2520find%2520an%2520account%2520to%2520unsubscribe.'; diff --git a/api/src/routes/public/email-subscription.ts b/api/src/routes/public/email-subscription.ts index f867c76df8e..18ef2c7e3ed 100644 --- a/api/src/routes/public/email-subscription.ts +++ b/api/src/routes/public/email-subscription.ts @@ -1,6 +1,6 @@ import { type FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox'; -import * as schemas from '../../schemas'; -import { getRedirectParams } from '../../utils/redirection'; +import * as schemas from '../../schemas.js'; +import { getRedirectParams } from '../../utils/redirection.js'; /** * Endpoints to set 'sendQuincyEmail' to true or false using 'unsubscribeId'. diff --git a/api/src/routes/public/index.ts b/api/src/routes/public/index.ts index 7bbb12f5fd1..39bdea8d914 100644 --- a/api/src/routes/public/index.ts +++ b/api/src/routes/public/index.ts @@ -1,11 +1,11 @@ -export * from './auth-dev'; -export * from './auth'; -export * from './certificate'; -export * from './deprecated-endpoints'; -export * from './deprecated-unsubscribe'; -export * from './donate'; -export * from './email-subscription'; -export * from './signout'; -export * from './status'; -export * from './user'; -export * from './sentry'; +export * from './auth-dev.js'; +export * from './auth.js'; +export * from './certificate.js'; +export * from './deprecated-endpoints.js'; +export * from './deprecated-unsubscribe.js'; +export * from './donate.js'; +export * from './email-subscription.js'; +export * from './signout.js'; +export * from './status.js'; +export * from './user.js'; +export * from './sentry.js'; diff --git a/api/src/routes/public/sentry.ts b/api/src/routes/public/sentry.ts index d9c72992adf..d4cfca6bd4a 100644 --- a/api/src/routes/public/sentry.ts +++ b/api/src/routes/public/sentry.ts @@ -1,8 +1,8 @@ import { type FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox'; import { type FastifyInstance, type FastifyReply } from 'fastify'; -import { UpdateReqType } from '../../utils'; -import * as schemas from '../../schemas'; +import { UpdateReqType } from '../../utils/index.js'; +import * as schemas from '../../schemas.js'; /** * Plugin for Sentry-related endpoints. diff --git a/api/src/routes/public/signout.test.ts b/api/src/routes/public/signout.test.ts index 0f33522a376..5f8619d1982 100644 --- a/api/src/routes/public/signout.test.ts +++ b/api/src/routes/public/signout.test.ts @@ -1,6 +1,6 @@ import { describe, it, expect, beforeEach } from 'vitest'; -import { devLogin, setupServer, superRequest } from '../../../vitest.utils'; -import { HOME_LOCATION } from '../../utils/env'; +import { devLogin, setupServer, superRequest } from '../../../vitest.utils.js'; +import { HOME_LOCATION } from '../../utils/env.js'; describe('GET /signout', () => { setupServer(); diff --git a/api/src/routes/public/signout.ts b/api/src/routes/public/signout.ts index 4958c9d31d3..183882e3661 100644 --- a/api/src/routes/public/signout.ts +++ b/api/src/routes/public/signout.ts @@ -1,6 +1,6 @@ import type { FastifyPluginCallback } from 'fastify'; -import { getRedirectParams } from '../../utils/redirection'; +import { getRedirectParams } from '../../utils/redirection.js'; /** * Route handler for signing out. diff --git a/api/src/routes/public/status.test.ts b/api/src/routes/public/status.test.ts index 5c603c768c8..1ebc3196553 100644 --- a/api/src/routes/public/status.test.ts +++ b/api/src/routes/public/status.test.ts @@ -1,6 +1,6 @@ import { describe, test, expect } from 'vitest'; -import { setupServer, superRequest } from '../../../vitest.utils'; -import { DEPLOYMENT_VERSION } from '../../utils/env'; +import { setupServer, superRequest } from '../../../vitest.utils.js'; +import { DEPLOYMENT_VERSION } from '../../utils/env.js'; describe('/status', () => { setupServer(); diff --git a/api/src/routes/public/status.ts b/api/src/routes/public/status.ts index a9b9c05567e..60a4174af57 100644 --- a/api/src/routes/public/status.ts +++ b/api/src/routes/public/status.ts @@ -1,6 +1,6 @@ import { type FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox'; -import { DEPLOYMENT_VERSION } from '../../utils/env'; +import { DEPLOYMENT_VERSION } from '../../utils/env.js'; /** * Plugin for the health check endpoint. diff --git a/api/src/routes/public/user.test.ts b/api/src/routes/public/user.test.ts index 96f7eeb803d..cb666cec1f8 100644 --- a/api/src/routes/public/user.test.ts +++ b/api/src/routes/public/user.test.ts @@ -1,6 +1,6 @@ import type { Prisma } from '@prisma/client'; import { ObjectId } from 'mongodb'; -import _ from 'lodash'; +import { omit } from 'lodash-es'; import { describe, it, @@ -12,13 +12,13 @@ import { vi } from 'vitest'; -import { createUserInput } from '../../utils/create-user'; +import { createUserInput } from '../../utils/create-user.js'; import { defaultUserEmail, setupServer, createSuperRequest -} from '../../../vitest.utils'; -import { replacePrivateData } from './user'; +} from '../../../vitest.utils.js'; +import { replacePrivateData } from './user.js'; const mockedFetch = vi.fn(); vi.spyOn(globalThis, 'fetch').mockImplementation(mockedFetch); @@ -378,7 +378,7 @@ describe('userRoutes', () => { // it should contain the entire body. const publicUser = { // TODO(Post-MVP, maybe): return completedSurveys? - ..._.omit(publicUserData, 'completedSurveys'), + ...omit(publicUserData, 'completedSurveys'), username: publicUsername, joinDate: new ObjectId(testUser.id).getTimestamp().toISOString(), profileUI: unlockedUserProfileUI @@ -592,9 +592,7 @@ describe('get-public-profile helpers', () => { }); test('returns the expected public user object if all showX flags are true', () => { - expect(replacePrivateData(user)).toEqual( - _.omit(user, ['id', 'profileUI']) - ); + expect(replacePrivateData(user)).toEqual(omit(user, ['id', 'profileUI'])); }); }); }); diff --git a/api/src/routes/public/user.ts b/api/src/routes/public/user.ts index 51fe0f70259..44a726de35e 100644 --- a/api/src/routes/public/user.ts +++ b/api/src/routes/public/user.ts @@ -1,11 +1,11 @@ import { Portfolio } from '@prisma/client'; import { type FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox'; import { ObjectId } from 'mongodb'; -import _ from 'lodash'; +import { omit } from 'lodash-es'; -import { isRestricted } from '../helpers/is-restricted'; -import * as schemas from '../../schemas'; -import { splitUser } from '../helpers/user-utils'; +import { isRestricted } from '../helpers/is-restricted.js'; +import * as schemas from '../../schemas.js'; +import { splitUser } from '../helpers/user-utils.js'; import { normalizeChallenges, NormalizedChallenge, @@ -13,14 +13,14 @@ import { normalizeProfileUI, normalizeTwitter, removeNulls -} from '../../utils/normalize'; +} from '../../utils/normalize.js'; import { Calendar, getCalendar, getPoints, ProgressTimestamp -} from '../../utils/progress'; -import { challengeTypes } from '../../../../shared/config/challenge-types'; +} from '../../utils/progress.js'; +import { challengeTypes } from '../../../../shared/config/challenge-types.js'; type ProfileUI = Partial<{ isLocked: boolean; @@ -137,7 +137,7 @@ export const userPublicGetRoutes: FastifyPluginCallbackTypebox = ( const [flags, rest] = splitUser(user); - const publicUser = _.omit(rest, [ + const publicUser = omit(rest, [ 'currentChallengeId', 'email', 'emailVerified', diff --git a/api/src/schema.test.ts b/api/src/schema.test.ts index bdaf153f2db..eef31d327fc 100644 --- a/api/src/schema.test.ts +++ b/api/src/schema.test.ts @@ -1,8 +1,8 @@ import { describe, test, expect } from 'vitest'; -import Ajv from 'ajv'; -import secureSchema from 'ajv/lib/refs/json-schema-secure.json'; +import secureSchema from 'ajv/lib/refs/json-schema-secure.json' with { type: 'json' }; +import { Ajv } from 'ajv'; -import * as schemas from './schemas'; +import * as schemas from './schemas.js'; // it's not strict, but that's okay - we're not using it to validate data const ajv = new Ajv({ strictTypes: false }); diff --git a/api/src/schemas.ts b/api/src/schemas.ts index abac46c8ce8..0005ee69681 100644 --- a/api/src/schemas.ts +++ b/api/src/schemas.ts @@ -1,45 +1,45 @@ -export { getPublicProfile } from './schemas/users/get-public-profile'; -export { userExists } from './schemas/users/exists'; -export { certSlug } from './schemas/certificate/cert-slug'; -export { certificateVerify } from './schemas/certificate/certificate-verify'; -export { backendChallengeCompleted } from './schemas/challenge/backend-challenge-completed'; -export { coderoadChallengeCompleted } from './schemas/challenge/coderoad-challenge-completed'; -export { exam } from './schemas/challenge/exam'; -export { examChallengeCompleted } from './schemas/challenge/exam-challenge-completed'; -export { dailyCodingChallengeCompleted } from './schemas/challenge/daily-coding-challenge-completed'; -export { modernChallengeCompleted } from './schemas/challenge/modern-challenge-completed'; -export { msTrophyChallengeCompleted } from './schemas/challenge/ms-trophy-challenge-completed'; -export { projectCompleted } from './schemas/challenge/project-completed'; -export { saveChallenge } from './schemas/challenge/save-challenge'; -export { submitQuizAttempt } from './schemas/challenge/submit-quiz-attempt'; -export { deprecatedEndpoints } from './schemas/deprecated'; -export { addDonation } from './schemas/donate/add-donation'; -export { chargeStripeCard } from './schemas/donate/charge-stripe-card'; -export { chargeStripe } from './schemas/donate/charge-stripe'; -export { createStripePaymentIntent } from './schemas/donate/create-stripe-payment-intent'; -export { updateStripeCard } from './schemas/donate/update-stripe-card'; -export { resubscribe } from './schemas/email-subscription/resubscribe'; -export { unsubscribe } from './schemas/email-subscription/unsubscribe'; -export { updateMyAbout } from './schemas/settings/update-my-about'; -export { confirmEmail } from './schemas/settings/confirm-email'; -export { updateMyClassroomMode } from './schemas/settings/update-my-classroom-mode'; -export { updateMyEmail } from './schemas/settings/update-my-email'; -export { updateMyHonesty } from './schemas/settings/update-my-honesty'; -export { updateMyKeyboardShortcuts } from './schemas/settings/update-my-keyboard-shortcuts'; -export { updateMyPortfolio } from './schemas/settings/update-my-portfolio'; -export { updateMyPrivacyTerms } from './schemas/settings/update-my-privacy-terms'; -export { updateMyProfileUI } from './schemas/settings/update-my-profile-ui'; -export { updateMyQuincyEmail } from './schemas/settings/update-my-quincy-email'; -export { updateMySocials } from './schemas/settings/update-my-socials'; -export { updateMyTheme } from './schemas/settings/update-my-theme'; -export { updateMyUsername } from './schemas/settings/update-my-username'; -export { deleteMsUsername } from './schemas/user/delete-ms-username'; -export { deleteMyAccount } from './schemas/user/delete-my-account'; -export { deleteUserToken } from './schemas/user/delete-user-token'; -export { getSessionUser } from './schemas/user/get-session-user'; -export { postMsUsername } from './schemas/user/post-ms-username'; -export { reportUser } from './schemas/user/report-user'; -export { resetMyProgress } from './schemas/user/reset-my-progress'; -export { submitSurvey } from './schemas/user/submit-survey'; -export { userExamEnvironmentToken } from './schemas/user/exam-environment-token'; -export { sentryPostEvent } from './schemas/sentry/event'; +export { getPublicProfile } from './schemas/users/get-public-profile.js'; +export { userExists } from './schemas/users/exists.js'; +export { certSlug } from './schemas/certificate/cert-slug.js'; +export { certificateVerify } from './schemas/certificate/certificate-verify.js'; +export { backendChallengeCompleted } from './schemas/challenge/backend-challenge-completed.js'; +export { coderoadChallengeCompleted } from './schemas/challenge/coderoad-challenge-completed.js'; +export { exam } from './schemas/challenge/exam.js'; +export { examChallengeCompleted } from './schemas/challenge/exam-challenge-completed.js'; +export { dailyCodingChallengeCompleted } from './schemas/challenge/daily-coding-challenge-completed.js'; +export { modernChallengeCompleted } from './schemas/challenge/modern-challenge-completed.js'; +export { msTrophyChallengeCompleted } from './schemas/challenge/ms-trophy-challenge-completed.js'; +export { projectCompleted } from './schemas/challenge/project-completed.js'; +export { saveChallenge } from './schemas/challenge/save-challenge.js'; +export { submitQuizAttempt } from './schemas/challenge/submit-quiz-attempt.js'; +export { deprecatedEndpoints } from './schemas/deprecated/index.js'; +export { addDonation } from './schemas/donate/add-donation.js'; +export { chargeStripeCard } from './schemas/donate/charge-stripe-card.js'; +export { chargeStripe } from './schemas/donate/charge-stripe.js'; +export { createStripePaymentIntent } from './schemas/donate/create-stripe-payment-intent.js'; +export { updateStripeCard } from './schemas/donate/update-stripe-card.js'; +export { resubscribe } from './schemas/email-subscription/resubscribe.js'; +export { unsubscribe } from './schemas/email-subscription/unsubscribe.js'; +export { updateMyAbout } from './schemas/settings/update-my-about.js'; +export { confirmEmail } from './schemas/settings/confirm-email.js'; +export { updateMyClassroomMode } from './schemas/settings/update-my-classroom-mode.js'; +export { updateMyEmail } from './schemas/settings/update-my-email.js'; +export { updateMyHonesty } from './schemas/settings/update-my-honesty.js'; +export { updateMyKeyboardShortcuts } from './schemas/settings/update-my-keyboard-shortcuts.js'; +export { updateMyPortfolio } from './schemas/settings/update-my-portfolio.js'; +export { updateMyPrivacyTerms } from './schemas/settings/update-my-privacy-terms.js'; +export { updateMyProfileUI } from './schemas/settings/update-my-profile-ui.js'; +export { updateMyQuincyEmail } from './schemas/settings/update-my-quincy-email.js'; +export { updateMySocials } from './schemas/settings/update-my-socials.js'; +export { updateMyTheme } from './schemas/settings/update-my-theme.js'; +export { updateMyUsername } from './schemas/settings/update-my-username.js'; +export { deleteMsUsername } from './schemas/user/delete-ms-username.js'; +export { deleteMyAccount } from './schemas/user/delete-my-account.js'; +export { deleteUserToken } from './schemas/user/delete-user-token.js'; +export { getSessionUser } from './schemas/user/get-session-user.js'; +export { postMsUsername } from './schemas/user/post-ms-username.js'; +export { reportUser } from './schemas/user/report-user.js'; +export { resetMyProgress } from './schemas/user/reset-my-progress.js'; +export { submitSurvey } from './schemas/user/submit-survey.js'; +export { userExamEnvironmentToken } from './schemas/user/exam-environment-token.js'; +export { sentryPostEvent } from './schemas/sentry/event.js'; diff --git a/api/src/schemas/certificate/cert-slug.ts b/api/src/schemas/certificate/cert-slug.ts index 9880b6f5232..f81df3d8f0a 100644 --- a/api/src/schemas/certificate/cert-slug.ts +++ b/api/src/schemas/certificate/cert-slug.ts @@ -1,6 +1,6 @@ import { Type } from '@fastify/type-provider-typebox'; -import { Certification } from '../../../../shared/config/certification-settings'; -import { genericError } from '../types'; +import { Certification } from '../../../../shared/config/certification-settings.js'; +import { genericError } from '../types.js'; export const certSlug = { params: Type.Object({ diff --git a/api/src/schemas/certificate/certificate-verify.ts b/api/src/schemas/certificate/certificate-verify.ts index 9825544a89b..dc1d686225d 100644 --- a/api/src/schemas/certificate/certificate-verify.ts +++ b/api/src/schemas/certificate/certificate-verify.ts @@ -1,5 +1,5 @@ import { Type } from '@fastify/type-provider-typebox'; -import { genericError, isCertMap } from '../types'; +import { genericError, isCertMap } from '../types.js'; export const certificateVerify = { // TODO(POST_MVP): Remove partial validation from route for schema validation diff --git a/api/src/schemas/challenge/backend-challenge-completed.ts b/api/src/schemas/challenge/backend-challenge-completed.ts index 65a2a2617db..a1ddba3af46 100644 --- a/api/src/schemas/challenge/backend-challenge-completed.ts +++ b/api/src/schemas/challenge/backend-challenge-completed.ts @@ -1,5 +1,5 @@ import { Type } from '@fastify/type-provider-typebox'; -import { genericError } from '../types'; +import { genericError } from '../types.js'; export const backendChallengeCompleted = { body: Type.Object({ diff --git a/api/src/schemas/challenge/exam-challenge-completed.ts b/api/src/schemas/challenge/exam-challenge-completed.ts index af161ee8447..a3f0df500b9 100644 --- a/api/src/schemas/challenge/exam-challenge-completed.ts +++ b/api/src/schemas/challenge/exam-challenge-completed.ts @@ -1,5 +1,5 @@ import { Type } from '@fastify/type-provider-typebox'; -import { examResults, genericError } from '../types'; +import { examResults, genericError } from '../types.js'; export const examChallengeCompleted = { body: Type.Object({ diff --git a/api/src/schemas/challenge/exam.ts b/api/src/schemas/challenge/exam.ts index ca48befe526..d47bec19944 100644 --- a/api/src/schemas/challenge/exam.ts +++ b/api/src/schemas/challenge/exam.ts @@ -1,5 +1,5 @@ import { Type } from '@fastify/type-provider-typebox'; -import { genericError } from '../types'; +import { genericError } from '../types.js'; export const exam = { params: Type.Object({ diff --git a/api/src/schemas/challenge/modern-challenge-completed.ts b/api/src/schemas/challenge/modern-challenge-completed.ts index da6c5d8fddb..d7c7cdb292f 100644 --- a/api/src/schemas/challenge/modern-challenge-completed.ts +++ b/api/src/schemas/challenge/modern-challenge-completed.ts @@ -1,5 +1,5 @@ import { Type } from '@fastify/type-provider-typebox'; -import { genericError, savedChallenge } from '../types'; +import { genericError, savedChallenge } from '../types.js'; export const modernChallengeCompleted = { body: Type.Object({ diff --git a/api/src/schemas/challenge/project-completed.ts b/api/src/schemas/challenge/project-completed.ts index d7230c2baad..1a099b76a53 100644 --- a/api/src/schemas/challenge/project-completed.ts +++ b/api/src/schemas/challenge/project-completed.ts @@ -1,5 +1,5 @@ import { Type } from '@fastify/type-provider-typebox'; -import { genericError } from '../types'; +import { genericError } from '../types.js'; export const projectCompleted = { body: Type.Object({ diff --git a/api/src/schemas/challenge/save-challenge.ts b/api/src/schemas/challenge/save-challenge.ts index deddb2cc01c..a1be0d739c2 100644 --- a/api/src/schemas/challenge/save-challenge.ts +++ b/api/src/schemas/challenge/save-challenge.ts @@ -1,5 +1,5 @@ import { Type } from '@fastify/type-provider-typebox'; -import { file, genericError, savedChallenge } from '../types'; +import { file, genericError, savedChallenge } from '../types.js'; export const saveChallenge = { body: Type.Object({ diff --git a/api/src/schemas/challenge/submit-quiz-attempt.ts b/api/src/schemas/challenge/submit-quiz-attempt.ts index 07ed7e1bf49..6790b51f946 100644 --- a/api/src/schemas/challenge/submit-quiz-attempt.ts +++ b/api/src/schemas/challenge/submit-quiz-attempt.ts @@ -1,5 +1,5 @@ import { Type } from '@fastify/type-provider-typebox'; -import { genericError } from '../types'; +import { genericError } from '../types.js'; export const submitQuizAttempt = { body: Type.Object({ diff --git a/api/src/schemas/donate/update-stripe-card.ts b/api/src/schemas/donate/update-stripe-card.ts index ac3eabf1191..fffdfcc5d69 100644 --- a/api/src/schemas/donate/update-stripe-card.ts +++ b/api/src/schemas/donate/update-stripe-card.ts @@ -1,5 +1,5 @@ import { Type } from '@fastify/type-provider-typebox'; -import { genericError } from '../types'; +import { genericError } from '../types.js'; export const updateStripeCard = { body: Type.Object({}), diff --git a/api/src/schemas/settings/update-my-classroom-mode.ts b/api/src/schemas/settings/update-my-classroom-mode.ts index 76d1db7520f..e3436f1f39f 100644 --- a/api/src/schemas/settings/update-my-classroom-mode.ts +++ b/api/src/schemas/settings/update-my-classroom-mode.ts @@ -1,5 +1,5 @@ import { Type } from '@fastify/type-provider-typebox'; -import { genericError } from '../types'; +import { genericError } from '../types.js'; export const updateMyClassroomMode = { body: Type.Object({ diff --git a/api/src/schemas/user/delete-my-account.ts b/api/src/schemas/user/delete-my-account.ts index 8004a7c6aaa..4a978d8d5f2 100644 --- a/api/src/schemas/user/delete-my-account.ts +++ b/api/src/schemas/user/delete-my-account.ts @@ -1,5 +1,5 @@ import { Type } from '@fastify/type-provider-typebox'; -import { genericError } from '../types'; +import { genericError } from '../types.js'; export const deleteMyAccount = { response: { diff --git a/api/src/schemas/user/delete-user-token.ts b/api/src/schemas/user/delete-user-token.ts index 5ddfa8c2248..e66c3556249 100644 --- a/api/src/schemas/user/delete-user-token.ts +++ b/api/src/schemas/user/delete-user-token.ts @@ -1,5 +1,5 @@ import { Type } from '@fastify/type-provider-typebox'; -import { genericError } from '../types'; +import { genericError } from '../types.js'; export const deleteUserToken = { response: { diff --git a/api/src/schemas/user/exam-environment-token.ts b/api/src/schemas/user/exam-environment-token.ts index 907f67f0053..db24e2f5105 100644 --- a/api/src/schemas/user/exam-environment-token.ts +++ b/api/src/schemas/user/exam-environment-token.ts @@ -1,5 +1,5 @@ import { Type } from '@fastify/type-provider-typebox'; -import { STANDARD_ERROR } from '../../exam-environment/utils/errors'; +import { STANDARD_ERROR } from '../../exam-environment/utils/errors.js'; export const userExamEnvironmentToken = { response: { diff --git a/api/src/schemas/user/get-session-user.ts b/api/src/schemas/user/get-session-user.ts index 6e4623d2907..b5f7acb447a 100644 --- a/api/src/schemas/user/get-session-user.ts +++ b/api/src/schemas/user/get-session-user.ts @@ -1,6 +1,6 @@ import { Type } from '@fastify/type-provider-typebox'; import { DailyCodingChallengeLanguage } from '@prisma/client'; -import { examResults, profileUI, savedChallenge } from '../types'; +import { examResults, profileUI, savedChallenge } from '../types.js'; const languages = Object.values(DailyCodingChallengeLanguage).map(k => Type.Literal(k) diff --git a/api/src/schemas/user/post-ms-username.ts b/api/src/schemas/user/post-ms-username.ts index 5791871f5af..e4451c77f64 100644 --- a/api/src/schemas/user/post-ms-username.ts +++ b/api/src/schemas/user/post-ms-username.ts @@ -1,5 +1,5 @@ import { Type } from '@fastify/type-provider-typebox'; -import { genericError } from '../types'; +import { genericError } from '../types.js'; export const postMsUsername = { body: Type.Object({ diff --git a/api/src/schemas/user/report-user.ts b/api/src/schemas/user/report-user.ts index 4c62198ebff..a37d5d552d4 100644 --- a/api/src/schemas/user/report-user.ts +++ b/api/src/schemas/user/report-user.ts @@ -1,5 +1,5 @@ import { Type } from '@fastify/type-provider-typebox'; -import { genericError } from '../types'; +import { genericError } from '../types.js'; export const reportUser = { body: Type.Object({ diff --git a/api/src/schemas/user/reset-my-progress.ts b/api/src/schemas/user/reset-my-progress.ts index 0dcbb5ca253..c9661b293d7 100644 --- a/api/src/schemas/user/reset-my-progress.ts +++ b/api/src/schemas/user/reset-my-progress.ts @@ -1,5 +1,5 @@ import { Type } from '@fastify/type-provider-typebox'; -import { genericError } from '../types'; +import { genericError } from '../types.js'; export const resetMyProgress = { response: { diff --git a/api/src/schemas/user/submit-survey.ts b/api/src/schemas/user/submit-survey.ts index 2c535fe9fc7..9d324f9b3df 100644 --- a/api/src/schemas/user/submit-survey.ts +++ b/api/src/schemas/user/submit-survey.ts @@ -1,5 +1,5 @@ import { Type } from '@fastify/type-provider-typebox'; -import { surveyTitles } from '../types'; +import { surveyTitles } from '../types.js'; export const submitSurvey = { body: Type.Object({ diff --git a/api/src/schemas/users/get-public-profile.ts b/api/src/schemas/users/get-public-profile.ts index 8daea0aead8..1750fbdd935 100644 --- a/api/src/schemas/users/get-public-profile.ts +++ b/api/src/schemas/users/get-public-profile.ts @@ -1,5 +1,5 @@ import { Type } from '@fastify/type-provider-typebox'; -import { profileUI, examResults, savedChallenge } from '../types'; +import { profileUI, examResults, savedChallenge } from '../types.js'; export const getPublicProfile = { querystring: Type.Object({ diff --git a/api/src/server.test.ts b/api/src/server.test.ts index 6ee428b4c0c..cc6e0761c56 100644 --- a/api/src/server.test.ts +++ b/api/src/server.test.ts @@ -1,9 +1,9 @@ import { describe, test, expect, vi } from 'vitest'; -import { setupServer, superRequest } from '../vitest.utils'; -import { HOME_LOCATION } from './utils/env'; +import { setupServer, superRequest } from '../vitest.utils.js'; +import { HOME_LOCATION } from './utils/env.js'; vi.mock('./utils/env', async importOriginal => { - const actual = await importOriginal(); + const actual = await importOriginal(); return { ...actual, COOKIE_DOMAIN: 'freecodecamp.org' diff --git a/api/src/server.ts b/api/src/server.ts index 02d21b66e30..1a5cdc024d1 100644 --- a/api/src/server.ts +++ b/api/src/server.ts @@ -1,7 +1,7 @@ -import './instrument'; +import './instrument.js'; -import { build, buildOptions } from './app'; -import { HOST, PORT } from './utils/env'; +import { build, buildOptions } from './app.js'; +import { HOST, PORT } from './utils/env.js'; const start = async () => { const fastify = await build(buildOptions); diff --git a/api/src/utils/allowed-origins.ts b/api/src/utils/allowed-origins.ts index e8f2e4ab404..d32297a1e7c 100644 --- a/api/src/utils/allowed-origins.ts +++ b/api/src/utils/allowed-origins.ts @@ -1,4 +1,4 @@ -import { HOME_LOCATION, FREECODECAMP_NODE_ENV } from './env'; +import { HOME_LOCATION, FREECODECAMP_NODE_ENV } from './env.js'; const ALLOWED_ORIGINS = [ 'https://www.freecodecamp.dev', diff --git a/api/src/utils/common-challenge-functions.ts b/api/src/utils/common-challenge-functions.ts index 2cf46fa0112..c121326b097 100644 --- a/api/src/utils/common-challenge-functions.ts +++ b/api/src/utils/common-challenge-functions.ts @@ -1,9 +1,9 @@ import type { ExamResults, user, Prisma } from '@prisma/client'; import { FastifyInstance } from 'fastify'; -import { omit, pick } from 'lodash'; -import { challengeTypes } from '../../../shared/config/challenge-types'; -import { getChallenges } from './get-challenges'; -import { normalizeDate } from './normalize'; +import { omit, pick } from 'lodash-es'; +import { challengeTypes } from '../../../shared/config/challenge-types.js'; +import { getChallenges } from './get-challenges.js'; +import { normalizeDate } from './normalize.js'; export const jsCertProjectIds = [ 'aaa48de84e1ecc7c742e1124', diff --git a/api/src/utils/env.ts b/api/src/utils/env.ts index 7e3c4bfa8ca..c089b4077a3 100644 --- a/api/src/utils/env.ts +++ b/api/src/utils/env.ts @@ -1,8 +1,10 @@ import assert from 'node:assert'; import path from 'node:path'; +import { fileURLToPath } from 'node:url'; import { config } from 'dotenv'; import { LogLevel } from 'fastify'; +const __dirname = path.dirname(fileURLToPath(import.meta.url)); const envPath = path.resolve(__dirname, '../../../.env'); const { error } = config({ path: envPath }); diff --git a/api/src/utils/exam-schemas.ts b/api/src/utils/exam-schemas.ts index 8b8092e1f75..b4861ce54b1 100644 --- a/api/src/utils/exam-schemas.ts +++ b/api/src/utils/exam-schemas.ts @@ -1,6 +1,6 @@ import { Answer, Exam, Question, ExamResults } from '@prisma/client'; import Joi from 'joi'; -import { GeneratedExam, UserExam } from './exam-types'; +import { GeneratedExam, UserExam } from './exam-types.js'; const nanoIdRE = new RegExp('[a-z0-9]{10}'); const objectIdRE = new RegExp('^[0-9a-fA-F]{24}$'); diff --git a/api/src/utils/exam.test.ts b/api/src/utils/exam.test.ts index b7433787940..cb011b85afc 100644 --- a/api/src/utils/exam.test.ts +++ b/api/src/utils/exam.test.ts @@ -10,9 +10,9 @@ import { mockResultsOneCorrect, mockResultsTwoCorrect, mockResultsAllCorrect -} from '../../__mocks__/exam'; -import { generateRandomExam, createExamResults } from './exam'; -import { GeneratedExam } from './exam-types'; +} from '../../__mocks__/exam.js'; +import { generateRandomExam, createExamResults } from './exam.js'; +import { GeneratedExam } from './exam-types.js'; describe('Exam helpers', () => { describe('generateRandomExam()', () => { diff --git a/api/src/utils/exam.ts b/api/src/utils/exam.ts index 94671fc8f85..d4851224b03 100644 --- a/api/src/utils/exam.ts +++ b/api/src/utils/exam.ts @@ -1,6 +1,6 @@ import { Exam, Question } from '@prisma/client'; -import { shuffleArray } from './../../../shared/utils/shuffle-array'; -import { UserExam, GeneratedExam } from './exam-types'; +import { shuffleArray } from './../../../shared/utils/shuffle-array.js'; +import { UserExam, GeneratedExam } from './exam-types.js'; /** * Remove objects from array with deprecated: true. diff --git a/api/src/utils/get-challenges.test.ts b/api/src/utils/get-challenges.test.ts index b7358c51a1b..5224108dbc3 100644 --- a/api/src/utils/get-challenges.test.ts +++ b/api/src/utils/get-challenges.test.ts @@ -1,6 +1,6 @@ import { describe, test, expect } from 'vitest'; -import { getChallenges } from './get-challenges'; -import { isObjectID } from './validation'; +import { getChallenges } from './get-challenges.js'; +import { isObjectID } from './validation.js'; describe('getChallenges', () => { test('returns an array of challenges', () => { diff --git a/api/src/utils/get-challenges.ts b/api/src/utils/get-challenges.ts index cb52655cb2c..14200375c06 100644 --- a/api/src/utils/get-challenges.ts +++ b/api/src/utils/get-challenges.ts @@ -4,10 +4,11 @@ // via the user object, then we should *not* store this so it can be garbage // collected. import { readFileSync } from 'fs'; -import { join } from 'path'; +import { fileURLToPath } from 'node:url'; +import { join, dirname } from 'path'; const CURRICULUM_PATH = '../../../shared/config/curriculum.json'; - +const __dirname = dirname(fileURLToPath(import.meta.url)); // Curriculum is read using fs, because it is too large for VSCode's LSP to handle type inference which causes annoying behavior. const curriculum = JSON.parse( readFileSync(join(__dirname, CURRICULUM_PATH), 'utf-8') diff --git a/api/src/utils/index.test.ts b/api/src/utils/index.test.ts index 20a5e78c925..4eb946a4e43 100644 --- a/api/src/utils/index.test.ts +++ b/api/src/utils/index.test.ts @@ -1,5 +1,5 @@ import { describe, test, expect } from 'vitest'; -import { base64URLEncode, challenge, verifier } from '.'; +import { base64URLEncode, challenge, verifier } from './index.js'; describe('utils', () => { test('base64URLEncode', () => { diff --git a/api/src/utils/logger.ts b/api/src/utils/logger.ts index 8030312ef3b..185ed0d5a9f 100644 --- a/api/src/utils/logger.ts +++ b/api/src/utils/logger.ts @@ -1,12 +1,13 @@ import { Transform, TransformCallback, TransformOptions } from 'stream'; import { FastifyRequest, FastifyReply } from 'fastify'; -import { isEmpty } from 'lodash'; -import pino, { +import { isEmpty } from 'lodash-es'; +import type { TransportTargetOptions, DestinationStream, LoggerOptions } from 'pino'; -import { FCC_API_LOG_LEVEL, FCC_API_LOG_TRANSPORT } from './env'; +import { pino } from 'pino'; +import { FCC_API_LOG_LEVEL, FCC_API_LOG_TRANSPORT } from './env.js'; const serializers = { req: (req: FastifyRequest) => { diff --git a/api/src/utils/normalize.test.ts b/api/src/utils/normalize.test.ts index 3d7a2316a62..08e501e94a7 100644 --- a/api/src/utils/normalize.test.ts +++ b/api/src/utils/normalize.test.ts @@ -6,7 +6,7 @@ import { normalizeFlags, normalizeDate, normalizeChallengeType -} from './normalize'; +} from './normalize.js'; describe('normalize', () => { describe('normalizeTwitter', () => { diff --git a/api/src/utils/normalize.ts b/api/src/utils/normalize.ts index 0c621c4eb09..9b2944b5e9a 100644 --- a/api/src/utils/normalize.ts +++ b/api/src/utils/normalize.ts @@ -7,7 +7,7 @@ import type { Survey, Prisma } from '@prisma/client'; -import _ from 'lodash'; +import { pickBy, mapValues } from 'lodash-es'; type NullToUndefined = T extends null ? undefined : T; type NullToFalse = T extends null ? false : T; @@ -123,7 +123,7 @@ export const normalizeProfileUI = ( export const removeNulls = >( obj: T ): NoNullProperties => - _.pickBy(obj, value => value !== null) as NoNullProperties; + pickBy(obj, value => value !== null) as NoNullProperties; type NormalizedFile = { contents: string; @@ -205,4 +205,4 @@ export const normalizeSurveys = ( export const normalizeFlags = >( flags: T ): DefaultToFalse => - _.mapValues(flags, flag => flag ?? false) as DefaultToFalse; + mapValues(flags, flag => flag ?? false) as DefaultToFalse; diff --git a/api/src/utils/progress.test.ts b/api/src/utils/progress.test.ts index e4e0f47b11a..5f3b4cc072e 100644 --- a/api/src/utils/progress.test.ts +++ b/api/src/utils/progress.test.ts @@ -1,5 +1,5 @@ import { describe, test, expect } from 'vitest'; -import { getCalendar, getPoints } from './progress'; +import { getCalendar, getPoints } from './progress.js'; describe('utils/progress', () => { describe('getCalendar', () => { diff --git a/api/src/utils/redirection.test.ts b/api/src/utils/redirection.test.ts index b49e5bf4a90..7185d8770f9 100644 --- a/api/src/utils/redirection.test.ts +++ b/api/src/utils/redirection.test.ts @@ -7,8 +7,8 @@ import { getRedirectParams, getPrefixedLandingPath, getLoginRedirectParams -} from './redirection'; -import { HOME_LOCATION } from './env'; +} from './redirection.js'; +import { HOME_LOCATION } from './env.js'; const validJWTSecret = 'this is a super secret string'; const invalidJWTSecret = 'This is not correct secret'; diff --git a/api/src/utils/redirection.ts b/api/src/utils/redirection.ts index 5c32315f5a6..e3b858db77c 100644 --- a/api/src/utils/redirection.ts +++ b/api/src/utils/redirection.ts @@ -1,11 +1,11 @@ import jwt from 'jsonwebtoken'; -import { availableLangs } from '../../../shared/config/i18n'; -import { allowedOrigins } from './allowed-origins'; +import { availableLangs } from '../../../shared/config/i18n.js'; +import { allowedOrigins } from './allowed-origins.js'; // process.env.HOME_LOCATION is being used as a fallback here. If the one // provided by the client is invalid we default to this. -import { HOME_LOCATION } from './env'; +import { HOME_LOCATION } from './env.js'; /** * Get the returnTo value. diff --git a/api/src/utils/tokens.test.ts b/api/src/utils/tokens.test.ts index 271b7f6bafb..412843f0b84 100644 --- a/api/src/utils/tokens.test.ts +++ b/api/src/utils/tokens.test.ts @@ -2,7 +2,7 @@ import { describe, test, expect, vi } from 'vitest'; vi.useFakeTimers(); /* eslint-disable @typescript-eslint/no-unsafe-assignment */ -import { createAccessToken, createAuthToken, isExpired } from './tokens'; +import { createAccessToken, createAuthToken, isExpired } from './tokens.js'; describe('createAccessToken', () => { test('creates an object with id, ttl, created and userId', () => { diff --git a/api/src/utils/tokens.ts b/api/src/utils/tokens.ts index 9cdde15b7d7..fb7f02f0486 100644 --- a/api/src/utils/tokens.ts +++ b/api/src/utils/tokens.ts @@ -1,7 +1,7 @@ import jwt from 'jsonwebtoken'; -import { customNanoid } from './ids'; +import { customNanoid } from './ids.js'; -import { JWT_SECRET } from './env'; +import { JWT_SECRET } from './env.js'; /** * Encode an id into a JWT (the naming suggests it's a user token, but it's the diff --git a/api/src/utils/validate-donation.test.ts b/api/src/utils/validate-donation.test.ts index 7a2b6970d8a..5fe3587a1c2 100644 --- a/api/src/utils/validate-donation.test.ts +++ b/api/src/utils/validate-donation.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, beforeAll, afterAll, vi } from 'vitest'; -import { inLastFiveMinutes } from './validate-donation'; +import { inLastFiveMinutes } from './validate-donation.js'; describe('inLastFiveMinutes', () => { beforeAll(() => { diff --git a/api/src/utils/validation.test.ts b/api/src/utils/validation.test.ts index 63441e78798..ce0d6832755 100644 --- a/api/src/utils/validation.test.ts +++ b/api/src/utils/validation.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest'; -import { isObjectID } from './validation'; +import { isObjectID } from './validation.js'; describe('Validation', () => { describe('isObjectID', () => { diff --git a/api/tools/exam-environment/generate/deprecate.ts b/api/tools/exam-environment/generate/deprecate.ts index 081dad1b53d..53cca280481 100644 --- a/api/tools/exam-environment/generate/deprecate.ts +++ b/api/tools/exam-environment/generate/deprecate.ts @@ -1,5 +1,5 @@ import { PrismaClient } from '@prisma/client'; -import { MONGOHQ_URL } from '../../../src/utils/env'; +import { MONGOHQ_URL } from '../../../src/utils/env.js'; const args = process.argv.slice(2); const ENV_EXAM_ID = args[0]; diff --git a/api/tools/exam-environment/generate/index.ts b/api/tools/exam-environment/generate/index.ts index 1c5fb1f6f91..7b19b643822 100644 --- a/api/tools/exam-environment/generate/index.ts +++ b/api/tools/exam-environment/generate/index.ts @@ -1,6 +1,6 @@ import { PrismaClient } from '@prisma/client'; -import { generateExam } from '../../../src/exam-environment/utils/exam-environment'; -import { MONGOHQ_URL } from '../../../src/utils/env'; +import { generateExam } from '../../../src/exam-environment/utils/exam-environment.js'; +import { MONGOHQ_URL } from '../../../src/utils/env.js'; const args = process.argv.slice(2); const ENV_EXAM_ID = args[0]; diff --git a/api/tools/exam-environment/generate/insert.ts b/api/tools/exam-environment/generate/insert.ts index 8ca81391a51..19b58bb01f6 100644 --- a/api/tools/exam-environment/generate/insert.ts +++ b/api/tools/exam-environment/generate/insert.ts @@ -1,6 +1,6 @@ import { readFile } from 'fs/promises'; import { ExamEnvironmentExam, PrismaClient } from '@prisma/client'; -import { MONGOHQ_URL } from '../../../src/utils/env'; +import { MONGOHQ_URL } from '../../../src/utils/env.js'; const args = process.argv.slice(2); const EXAM_JSON_PATH = args[0]; diff --git a/api/tools/exam-environment/seed/index.ts b/api/tools/exam-environment/seed/index.ts index adc7a67b04a..158a63ea0c5 100644 --- a/api/tools/exam-environment/seed/index.ts +++ b/api/tools/exam-environment/seed/index.ts @@ -1,6 +1,6 @@ import { PrismaClient } from '@prisma/client'; -import * as mocks from '../../../__mocks__/exam-environment-exam'; -import { MONGOHQ_URL } from '../../../src/utils/env'; +import * as mocks from '../../../__mocks__/exam-environment-exam.js'; +import { MONGOHQ_URL } from '../../../src/utils/env.js'; const prisma = new PrismaClient({ datasources: { diff --git a/api/tools/exam-environment/test/index.ts b/api/tools/exam-environment/test/index.ts index 85e69ac1f4a..d07166c7e3b 100644 --- a/api/tools/exam-environment/test/index.ts +++ b/api/tools/exam-environment/test/index.ts @@ -3,8 +3,8 @@ //! This script should be run, before any deployments with Exam schema changes. //! WARNING: This script queries every single record in the `Env<>` collections. import { PrismaClient } from '@prisma/client'; -import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library'; -import { MONGOHQ_URL } from '../../../src/utils/env'; +import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library.js'; +import { MONGOHQ_URL } from '../../../src/utils/env.js'; const prisma = new PrismaClient({ datasources: { diff --git a/api/tsconfig.json b/api/tsconfig.json index 49b70a9b8b8..67d70f9b90b 100644 --- a/api/tsconfig.json +++ b/api/tsconfig.json @@ -1,14 +1,13 @@ { "compilerOptions": { "target": "es2022", - "module": "CommonJS", + "module": "nodenext", "allowJs": true, "strict": true, "forceConsistentCasingInFileNames": true, "esModuleInterop": true, "resolveJsonModule": true, "noEmit": true, - "noUncheckedIndexedAccess": true, - "skipLibCheck": true /* This should only be necessary while migrating to vitest, once we're done with that migration we can move to ESM and *hopefully* get rid of this */ + "noUncheckedIndexedAccess": true } } diff --git a/api/vitest.utils.test.ts b/api/vitest.utils.test.ts index e2d9e05db56..1bcccd43f89 100644 --- a/api/vitest.utils.test.ts +++ b/api/vitest.utils.test.ts @@ -1,5 +1,5 @@ import { describe, test, expect } from 'vitest'; -import { getCsrfToken, getCookies } from './vitest.utils'; +import { getCsrfToken, getCookies } from './vitest.utils.js'; const fakeCookies = [ '_csrf=123; Path=/; HttpOnly; SameSite=Strict', diff --git a/api/vitest.utils.ts b/api/vitest.utils.ts index 9894bdb771b..5f9250653ae 100644 --- a/api/vitest.utils.ts +++ b/api/vitest.utils.ts @@ -1,10 +1,10 @@ import { beforeAll, afterAll, expect, vi } from 'vitest'; import request from 'supertest'; -import { build, buildOptions } from './src/app'; -import { createUserInput } from './src/utils/create-user'; -import { examJson } from './__mocks__/exam'; -import { CSRF_COOKIE, CSRF_HEADER } from './src/plugins/csrf'; +import { build, buildOptions } from './src/app.js'; +import { createUserInput } from './src/utils/create-user.js'; +import { examJson } from './__mocks__/exam.js'; +import { CSRF_COOKIE, CSRF_HEADER } from './src/plugins/csrf.js'; type FastifyTestInstance = Awaited>; diff --git a/client/config/cert-and-project-map.ts b/client/config/cert-and-project-map.ts index e70fe8b2bd0..011b67e9b28 100644 --- a/client/config/cert-and-project-map.ts +++ b/client/config/cert-and-project-map.ts @@ -3,7 +3,7 @@ import { legacyCertifications, upcomingCertifications, currentCertifications -} from '../../shared/config/certification-settings'; +} from '../../shared-dist/config/certification-settings'; import config from '../config/env.json'; const { showUpcomingChanges } = config; diff --git a/client/i18n/config.js b/client/i18n/config.js index 7b26253d69c..1977f5f3374 100644 --- a/client/i18n/config.js +++ b/client/i18n/config.js @@ -4,7 +4,7 @@ import i18n from 'i18next'; import { initReactI18next } from 'react-i18next'; const envData = require('../config/env.json'); -const { i18nextCodes } = require('../../shared/config/i18n'); +const { i18nextCodes } = require('../../shared-dist/config/i18n'); const { clientLocale } = envData; diff --git a/client/i18n/locales.test.ts b/client/i18n/locales.test.ts index a353c1a7087..3eb061be536 100644 --- a/client/i18n/locales.test.ts +++ b/client/i18n/locales.test.ts @@ -4,11 +4,15 @@ import { fileURLToPath } from 'url'; import { describe, test, expect } from 'vitest'; -import { availableLangs, LangNames, LangCodes } from '../../shared/config/i18n'; +import { + availableLangs, + LangNames, + LangCodes +} from '../../shared-dist/config/i18n'; import { catalogSuperBlocks, SuperBlocks -} from '../../shared/config/curriculum'; +} from '../../shared-dist/config/curriculum'; import intro from './locales/english/intro.json'; interface Intro { diff --git a/client/i18n/schema-validation.ts b/client/i18n/schema-validation.ts index 6b2638b59f5..8c6ad006db2 100644 --- a/client/i18n/schema-validation.ts +++ b/client/i18n/schema-validation.ts @@ -1,6 +1,6 @@ import path from 'path'; import { readFile } from 'fs/promises'; -import { availableLangs } from '../../shared/config/i18n'; +import { availableLangs } from '../../shared-dist/config/i18n'; import introSchema from './locales/english/intro.json'; import linksSchema from './locales/english/links.json'; import metaTagsSchema from './locales/english/meta-tags.json'; diff --git a/client/src/analytics/call-ga.ts b/client/src/analytics/call-ga.ts index 51c675d29db..cd6b26d617b 100644 --- a/client/src/analytics/call-ga.ts +++ b/client/src/analytics/call-ga.ts @@ -1,7 +1,7 @@ import { DonationAmount, DonationDuration -} from '../../../shared/config/donation-settings'; +} from '../../../shared-dist/config/donation-settings'; import { ChallengeFiles } from '../redux/prop-types'; import TagManager from '.'; diff --git a/client/src/assets/chapter-icon.tsx b/client/src/assets/chapter-icon.tsx index cbf9e590f6c..8362f4ece46 100644 --- a/client/src/assets/chapter-icon.tsx +++ b/client/src/assets/chapter-icon.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { FsdChapters } from '../../../shared/config/chapters'; +import { FsdChapters } from '../../../shared-dist/config/chapters'; import DatabaseIcon from './icons/database'; import JavaScriptIcon from './icons/javascript'; import ReactIcon from './icons/react'; diff --git a/client/src/assets/superblock-icon.tsx b/client/src/assets/superblock-icon.tsx index c6ccee559da..3a982dcec94 100644 --- a/client/src/assets/superblock-icon.tsx +++ b/client/src/assets/superblock-icon.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { SuperBlocks } from '../../../shared/config/curriculum'; +import { SuperBlocks } from '../../../shared-dist/config/curriculum'; import APIIcon from './icons/api'; import D3Icon from './icons/d3'; import DatabaseIcon from './icons/database'; diff --git a/client/src/client-only-routes/show-certification.tsx b/client/src/client-only-routes/show-certification.tsx index d2cd6c7dc91..6d7c1724c0b 100644 --- a/client/src/client-only-routes/show-certification.tsx +++ b/client/src/client-only-routes/show-certification.tsx @@ -8,7 +8,7 @@ import { createSelector } from 'reselect'; import { Container, Col, Row, Image, Button, Spacer } from '@freecodecamp/ui'; import envData from '../../config/env.json'; -import { getLangCode } from '../../../shared/config/i18n'; +import { getLangCode } from '../../../shared-dist/config/i18n'; import FreeCodeCampLogo from '../assets/icons/freecodecamp-logo'; import MicrosoftLogo from '../assets/icons/microsoft-logo'; import { createFlashMessage } from '../components/Flash/redux'; @@ -33,13 +33,13 @@ import { standardErrorMessage } from '../utils/error-messages'; -import { PaymentContext } from '../../../shared/config/donation-settings'; +import { PaymentContext } from '../../../shared-dist/config/donation-settings'; import ribbon from '../assets/images/ribbon.svg'; import { Certification, CertSlug, linkedInCredentialIds -} from '../../../shared/config/certification-settings'; +} from '../../../shared-dist/config/certification-settings'; import MultiTierDonationForm from '../components/Donation/multi-tier-donation-form'; import callGA from '../analytics/call-ga'; import ShowProjectLinks from './show-project-links'; diff --git a/client/src/client-only-routes/show-project-links.tsx b/client/src/client-only-routes/show-project-links.tsx index 3e7a895e4ca..79fd3dac6f7 100644 --- a/client/src/client-only-routes/show-project-links.tsx +++ b/client/src/client-only-routes/show-project-links.tsx @@ -19,9 +19,9 @@ import ExamResultsModal from '../components/SolutionViewer/exam-results-modal'; import { openModal } from '../templates/Challenges/redux/actions'; -import { regenerateMissingProperties } from '../../../shared/utils/polyvinyl'; +import { regenerateMissingProperties } from '../../../shared-dist/utils/polyvinyl'; import '../components/layouts/project-links.css'; -import { Certification } from '../../../shared/config/certification-settings'; +import { Certification } from '../../../shared-dist/config/certification-settings'; interface ShowProjectLinksProps { certSlug: Certification; name: string; diff --git a/client/src/components/Donation/donate-form.tsx b/client/src/components/Donation/donate-form.tsx index 77452921d3e..9473f845d73 100644 --- a/client/src/components/Donation/donate-form.tsx +++ b/client/src/components/Donation/donate-form.tsx @@ -12,7 +12,7 @@ import { defaultDonation, DonationAmount, type DonationConfig -} from '../../../../shared/config/donation-settings'; +} from '../../../../shared-dist/config/donation-settings'; import { defaultDonationFormState } from '../../redux'; import { updateDonationFormState, postCharge } from '../../redux/actions'; import { diff --git a/client/src/components/Donation/donation-modal-body.tsx b/client/src/components/Donation/donation-modal-body.tsx index d2d6b456107..0f4ec53a59c 100644 --- a/client/src/components/Donation/donation-modal-body.tsx +++ b/client/src/components/Donation/donation-modal-body.tsx @@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next'; import { useFeature } from '@growthbook/growthbook-react'; import { Col, Row, Modal, Spacer } from '@freecodecamp/ui'; import { closeDonationModal } from '../../redux/actions'; -import { PaymentContext } from '../../../../shared/config/donation-settings'; +import { PaymentContext } from '../../../../shared-dist/config/donation-settings'; import donationAnimation from '../../assets/images/donation-bear-animation.svg'; import donationAnimationB from '../../assets/images/new-bear-animation.svg'; import supporterBearBlock from '../../assets/images/supporter-bear-block.svg'; diff --git a/client/src/components/Donation/multi-tier-donation-form.tsx b/client/src/components/Donation/multi-tier-donation-form.tsx index 6c39db4ba63..b6c1649f026 100644 --- a/client/src/components/Donation/multi-tier-donation-form.tsx +++ b/client/src/components/Donation/multi-tier-donation-form.tsx @@ -18,7 +18,7 @@ import { defaultTierAmount, defaultTierAmountB, type DonationAmount -} from '../../../../shared/config/donation-settings'; // You can further extract these into separate components and import them +} from '../../../../shared-dist/config/donation-settings'; // You can further extract these into separate components and import them import callGA from '../../analytics/call-ga'; import { LocalStorageThemes } from '../../redux/types'; import { formattedAmountLabel, convertToTimeContributed } from './utils'; diff --git a/client/src/components/Donation/patreon-button.tsx b/client/src/components/Donation/patreon-button.tsx index ac39b456af8..e8ce1eb1536 100644 --- a/client/src/components/Donation/patreon-button.tsx +++ b/client/src/components/Donation/patreon-button.tsx @@ -3,7 +3,7 @@ import { DonationAmount, donationUrls, PaymentProvider -} from '../../../../shared/config/donation-settings'; +} from '../../../../shared-dist/config/donation-settings'; import envData from '../../../config/env.json'; import PatreonLogo from '../../assets/images/components/patreon-logo'; import { PostPayment } from './types'; diff --git a/client/src/components/Donation/paypal-button.tsx b/client/src/components/Donation/paypal-button.tsx index 8b876d092e5..35ce3775e08 100644 --- a/client/src/components/Donation/paypal-button.tsx +++ b/client/src/components/Donation/paypal-button.tsx @@ -9,7 +9,7 @@ import { PaymentProvider, type DonationDuration, type DonationAmount -} from '../../../../shared/config/donation-settings'; +} from '../../../../shared-dist/config/donation-settings'; import envData from '../../../config/env.json'; import { userSelector, signInLoadingSelector } from '../../redux/selectors'; import { LocalStorageThemes } from '../../redux/types'; diff --git a/client/src/components/Donation/stripe-card-form.tsx b/client/src/components/Donation/stripe-card-form.tsx index 2f9235c019f..094a28e1955 100644 --- a/client/src/components/Donation/stripe-card-form.tsx +++ b/client/src/components/Donation/stripe-card-form.tsx @@ -10,7 +10,7 @@ import type { } from '@stripe/stripe-js'; import React, { useState } from 'react'; -import { PaymentProvider } from '../../../../shared/config/donation-settings'; +import { PaymentProvider } from '../../../../shared-dist/config/donation-settings'; import { LocalStorageThemes } from '../../redux/types'; import { DonationApprovalData, PostPayment } from './types'; diff --git a/client/src/components/Donation/types.ts b/client/src/components/Donation/types.ts index 105032ef751..2f183a49a50 100644 --- a/client/src/components/Donation/types.ts +++ b/client/src/components/Donation/types.ts @@ -1,5 +1,5 @@ import type { PaymentIntentResult } from '@stripe/stripe-js'; -import { SuperBlocks } from '../../../../shared/config/curriculum'; +import { SuperBlocks } from '../../../../shared-dist/config/curriculum'; export type PaymentContext = 'modal' | 'donate page' | 'certificate'; export type PaymentProvider = 'patreon' | 'paypal' | 'stripe' | 'stripe card'; diff --git a/client/src/components/Donation/wallets-button.tsx b/client/src/components/Donation/wallets-button.tsx index 038ce8a2a8f..6db6829d6e3 100644 --- a/client/src/components/Donation/wallets-button.tsx +++ b/client/src/components/Donation/wallets-button.tsx @@ -10,7 +10,7 @@ import { LocalStorageThemes } from '../../redux/types'; import { PaymentProvider, DonationDuration -} from '../../../../shared/config/donation-settings'; +} from '../../../../shared-dist/config/donation-settings'; import { createStripePaymentIntent } from '../../utils/ajax'; import { DonationApprovalData, PostPayment } from './types'; diff --git a/client/src/components/Header/components/language-list.tsx b/client/src/components/Header/components/language-list.tsx index 8570d692e02..ff40a4d2f7c 100644 --- a/client/src/components/Header/components/language-list.tsx +++ b/client/src/components/Header/components/language-list.tsx @@ -8,7 +8,7 @@ import { LangNames, LangCodes, hiddenLangs -} from '../../../../../shared/config/i18n'; +} from '../../../../../shared-dist/config/i18n'; import { hardGoTo as navigate } from '../../../redux/actions'; import createLanguageRedirect from '../../create-language-redirect'; import LanguageGlobe from '../../../assets/icons/language-globe'; diff --git a/client/src/components/Map/index.tsx b/client/src/components/Map/index.tsx index 76a2e377ac5..d8669a544df 100644 --- a/client/src/components/Map/index.tsx +++ b/client/src/components/Map/index.tsx @@ -8,7 +8,7 @@ import { SuperBlockStage, getStageOrder, superBlockStages -} from '../../../../shared/config/curriculum'; +} from '../../../../shared-dist/config/curriculum'; import { SuperBlockIcon } from '../../assets/superblock-icon'; import LinkButton from '../../assets/icons/link-button'; import { ButtonLink } from '../helpers'; diff --git a/client/src/components/Progress/progress.tsx b/client/src/components/Progress/progress.tsx index 68874e7a7c5..bf9bfe240d7 100644 --- a/client/src/components/Progress/progress.tsx +++ b/client/src/components/Progress/progress.tsx @@ -14,7 +14,7 @@ import { import { liveCerts } from '../../../config/cert-and-project-map'; import { updateAllChallengesInfo } from '../../redux/actions'; import { CertificateNode, ChallengeNode } from '../../redux/prop-types'; -import { getIsDailyCodingChallenge } from '../../../../shared/config/challenge-types'; +import { getIsDailyCodingChallenge } from '../../../../shared-dist/config/challenge-types'; import { isValidDateString, formatDisplayDate diff --git a/client/src/components/app-mount-notifier.test.tsx b/client/src/components/app-mount-notifier.test.tsx index 207eb5263d7..db1b0ee4e72 100644 --- a/client/src/components/app-mount-notifier.test.tsx +++ b/client/src/components/app-mount-notifier.test.tsx @@ -4,7 +4,7 @@ import { I18nextProvider } from 'react-i18next'; import { Provider } from 'react-redux'; import { describe, vi, test, expect } from 'vitest'; -import { i18nextCodes } from '../../../shared/config/i18n'; +import { i18nextCodes } from '../../../shared-dist/config/i18n'; import i18nTestConfig from '../../i18n/config-for-tests'; import { createStore } from '../redux/create-store'; import AppMountNotifier from './app-mount-notifier'; diff --git a/client/src/components/growth-book/codeally-button.tsx b/client/src/components/growth-book/codeally-button.tsx index 97f42438347..5f72072ba83 100644 --- a/client/src/components/growth-book/codeally-button.tsx +++ b/client/src/components/growth-book/codeally-button.tsx @@ -4,7 +4,7 @@ import React from 'react'; import { useFeature } from '@growthbook/growthbook-react'; import { useTranslation } from 'react-i18next'; import { Button } from '@freecodecamp/ui'; -import { challengeTypes } from '../../../../shared/config/challenge-types'; +import { challengeTypes } from '../../../../shared-dist/config/challenge-types'; interface CodeAllyButtonProps { challengeType: number; diff --git a/client/src/components/growth-book/ona-note.tsx b/client/src/components/growth-book/ona-note.tsx index c50416f1c11..9404136a298 100644 --- a/client/src/components/growth-book/ona-note.tsx +++ b/client/src/components/growth-book/ona-note.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { Alert } from '@freecodecamp/ui'; import { useFeatureValue } from '@growthbook/growthbook-react'; -import { SuperBlocks } from '../../../../shared/config/curriculum'; +import { SuperBlocks } from '../../../../shared-dist/config/curriculum'; import { Link } from '../helpers'; type OnaNoteProps = { diff --git a/client/src/components/profile/components/heat-map.test.tsx b/client/src/components/profile/components/heat-map.test.tsx index 9336ed80e2f..226b9cf4c8e 100644 --- a/client/src/components/profile/components/heat-map.test.tsx +++ b/client/src/components/profile/components/heat-map.test.tsx @@ -11,7 +11,7 @@ import { render, screen } from '@testing-library/react'; import React from 'react'; import envData from '../../../../config/env.json'; -import { getLangCode } from '../../../../../shared/config/i18n'; +import { getLangCode } from '../../../../../shared-dist/config/i18n'; import HeatMap from './heat-map'; const { clientLocale } = envData; diff --git a/client/src/components/profile/components/heat-map.tsx b/client/src/components/profile/components/heat-map.tsx index 3afe0d20b6f..9f8b42d7dd6 100644 --- a/client/src/components/profile/components/heat-map.tsx +++ b/client/src/components/profile/components/heat-map.tsx @@ -13,7 +13,7 @@ import 'react-calendar-heatmap/dist/styles.css'; import './heatmap.css'; import envData from '../../../../config/env.json'; -import { getLangCode } from '../../../../../shared/config/i18n'; +import { getLangCode } from '../../../../../shared-dist/config/i18n'; import { User } from '../../../redux/prop-types'; import FullWidthRow from '../../helpers/full-width-row'; diff --git a/client/src/components/profile/components/time-line.tsx b/client/src/components/profile/components/time-line.tsx index b634d325684..7f896a1837e 100644 --- a/client/src/components/profile/components/time-line.tsx +++ b/client/src/components/profile/components/time-line.tsx @@ -8,9 +8,9 @@ import { connect } from 'react-redux'; import { Table, Button, Modal, Spacer } from '@freecodecamp/ui'; import envData from '../../../../config/env.json'; -import { getLangCode } from '../../../../../shared/config/i18n'; +import { getLangCode } from '../../../../../shared-dist/config/i18n'; import { getCertIds, getPathFromID } from '../../../../utils'; -import { regenerateMissingProperties } from '../../../../../shared/utils/polyvinyl'; +import { regenerateMissingProperties } from '../../../../../shared-dist/utils/polyvinyl'; import CertificationIcon from '../../../assets/icons/certification'; import type { ChallengeData, @@ -21,7 +21,7 @@ import ExamResultsModal from '../../SolutionViewer/exam-results-modal'; import { openModal } from '../../../templates/Challenges/redux/actions'; import { Link, FullWidthRow } from '../../helpers'; import { SolutionDisplayWidget } from '../../solution-display-widget'; -import { SuperBlocks } from '../../../../../shared/config/curriculum'; +import { SuperBlocks } from '../../../../../shared-dist/config/curriculum'; import TimelinePagination from './timeline-pagination'; const SolutionViewer = Loadable( diff --git a/client/src/components/profile/components/username.tsx b/client/src/components/profile/components/username.tsx index b8e374a19b6..de19ff69367 100644 --- a/client/src/components/profile/components/username.tsx +++ b/client/src/components/profile/components/username.tsx @@ -8,7 +8,7 @@ import { bindActionCreators } from 'redux'; import type { Dispatch } from 'redux'; import { createSelector } from 'reselect'; -import { isValidUsername } from '../../../../../shared/utils/validate'; +import { isValidUsername } from '../../../../../shared-dist/utils/validate'; import { usernameValidationSelector } from '../../../redux/settings/selectors'; import { validateUsername, diff --git a/client/src/components/profile/components/utils/certification.ts b/client/src/components/profile/components/utils/certification.ts index d0857cd1c02..a41ed424b6f 100644 --- a/client/src/components/profile/components/utils/certification.ts +++ b/client/src/components/profile/components/utils/certification.ts @@ -1,4 +1,4 @@ -import { Certification } from '../../../../../../shared/config/certification-settings'; +import { Certification } from '../../../../../../shared-dist/config/certification-settings'; import { User } from '../../../../redux/prop-types'; export const getCertifications = (user: User) => { diff --git a/client/src/components/profile/components/utils/utils.ts b/client/src/components/profile/components/utils/utils.ts index 7e92020bb5c..741abd1145a 100644 --- a/client/src/components/profile/components/utils/utils.ts +++ b/client/src/components/profile/components/utils/utils.ts @@ -1,5 +1,5 @@ import type { TFunction } from 'i18next'; -import { getLangCode } from '../../../../../../shared/config/i18n'; +import { getLangCode } from '../../../../../../shared-dist/config/i18n'; import envData from '../../../../../config/env.json'; const { clientLocale } = envData; diff --git a/client/src/components/seo/index.tsx b/client/src/components/seo/index.tsx index 76f4021ddba..82e55b744cc 100644 --- a/client/src/components/seo/index.tsx +++ b/client/src/components/seo/index.tsx @@ -2,7 +2,7 @@ import { useStaticQuery, graphql } from 'gatsby'; import React from 'react'; import Helmet from 'react-helmet'; import { useTranslation } from 'react-i18next'; -import { SuperBlocks } from '../../../../shared/config/curriculum'; +import { SuperBlocks } from '../../../../shared-dist/config/curriculum'; interface SEOProps { title?: string; diff --git a/client/src/components/settings/certification.tsx b/client/src/components/settings/certification.tsx index cc10dde2184..33ef6ecfa6e 100644 --- a/client/src/components/settings/certification.tsx +++ b/client/src/components/settings/certification.tsx @@ -6,7 +6,7 @@ import ScrollableAnchor, { configureAnchors } from 'react-scrollable-anchor'; import { connect } from 'react-redux'; import { Table, Button, Spacer } from '@freecodecamp/ui'; -import { regenerateMissingProperties } from '../../../../shared/utils/polyvinyl'; +import { regenerateMissingProperties } from '../../../../shared-dist/utils/polyvinyl'; import ProjectPreviewModal from '../../templates/Challenges/components/project-preview-modal'; import ExamResultsModal from '../SolutionViewer/exam-results-modal'; import { openModal } from '../../templates/Challenges/redux/actions'; @@ -21,7 +21,7 @@ import { currentCertifications, legacyCertifications, upcomingCertifications -} from '../../../../shared/config/certification-settings'; +} from '../../../../shared-dist/config/certification-settings'; import env from '../../../config/env.json'; import type { diff --git a/client/src/html.tsx b/client/src/html.tsx index dd40fad2e07..75cee24306d 100644 --- a/client/src/html.tsx +++ b/client/src/html.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { clientLocale } from '../config/env.json'; -import { rtlLangs } from '../../shared/config/i18n'; +import { rtlLangs } from '../../shared-dist/config/i18n'; interface HTMLProps { body: string; diff --git a/client/src/pages/catalog.tsx b/client/src/pages/catalog.tsx index c302b493230..88b1e148175 100644 --- a/client/src/pages/catalog.tsx +++ b/client/src/pages/catalog.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { Col, Spacer } from '@freecodecamp/ui'; import { ButtonLink } from '../components/helpers'; -import { catalog } from '../../../shared/config/catalog'; +import { catalog } from '../../../shared-dist/config/catalog'; import { showUpcomingChanges } from '../../config/env.json'; import FourOhFour from '../components/FourOhFour'; diff --git a/client/src/pages/donate.tsx b/client/src/pages/donate.tsx index 3867957c5a1..63a31c16a49 100644 --- a/client/src/pages/donate.tsx +++ b/client/src/pages/donate.tsx @@ -22,7 +22,7 @@ import { userSelector, donationFormStateSelector } from '../redux/selectors'; -import { PaymentContext } from '../../../shared/config/donation-settings'; +import { PaymentContext } from '../../../shared-dist/config/donation-settings'; import { DonateFormState } from '../redux/types'; import callGA from '../analytics/call-ga'; import type { User } from '../redux/prop-types'; diff --git a/client/src/redux/donation-saga.js b/client/src/redux/donation-saga.js index 86cf937473a..b105fb0cd9e 100644 --- a/client/src/redux/donation-saga.js +++ b/client/src/redux/donation-saga.js @@ -17,7 +17,7 @@ import { } from '../utils/ajax'; import { stringifyDonationEvents } from '../utils/analytics-strings'; import { stripe } from '../utils/stripe'; -import { PaymentProvider } from '../../../shared/config/donation-settings'; +import { PaymentProvider } from '../../../shared-dist/config/donation-settings'; import { getSessionChallengeData, saveCurrentCount diff --git a/client/src/redux/failed-updates-epic.js b/client/src/redux/failed-updates-epic.js index 91c95f3e277..2c5d089910d 100644 --- a/client/src/redux/failed-updates-epic.js +++ b/client/src/redux/failed-updates-epic.js @@ -11,7 +11,7 @@ import { import store from 'store'; import { v4 as uuid } from 'uuid'; -import { challengeTypes } from '../../../shared/config/challenge-types'; +import { challengeTypes } from '../../../shared-dist/config/challenge-types'; import { isGoodXHRStatus } from '../templates/Challenges/utils'; import postUpdate$ from '../templates/Challenges/utils/post-update'; import { actionTypes } from './action-types'; diff --git a/client/src/redux/index.js b/client/src/redux/index.js index fa63d9226eb..ca48b950ce5 100644 --- a/client/src/redux/index.js +++ b/client/src/redux/index.js @@ -6,7 +6,7 @@ import { actionTypes as challengeTypes, CURRENT_CHALLENGE_KEY } from '../templates/Challenges/redux/action-types'; -import { getIsDailyCodingChallenge } from '../../../shared/config/challenge-types'; +import { getIsDailyCodingChallenge } from '../../../shared-dist/config/challenge-types'; import { actionTypes, ns as MainApp } from './action-types'; import { createAppMountSaga } from './app-mount-saga'; import { createDonationSaga } from './donation-saga'; diff --git a/client/src/redux/prop-types.ts b/client/src/redux/prop-types.ts index a24bd1a81aa..e7e076561a8 100644 --- a/client/src/redux/prop-types.ts +++ b/client/src/redux/prop-types.ts @@ -1,7 +1,7 @@ import { HandlerProps } from 'react-reflex'; -import { SuperBlocks } from '../../../shared/config/curriculum'; -import { BlockLayouts, BlockTypes } from '../../../shared/config/blocks'; -import type { ChallengeFile, Ext } from '../../../shared/utils/polyvinyl'; +import { SuperBlocks } from '../../../shared-dist/config/curriculum'; +import { BlockLayouts, BlockTypes } from '../../../shared-dist/config/blocks'; +import type { ChallengeFile, Ext } from '../../../shared-dist/utils/polyvinyl'; import { type CertTitle } from '../../config/cert-and-project-map'; import { UserThemes } from './types'; diff --git a/client/src/redux/save-challenge-saga.js b/client/src/redux/save-challenge-saga.js index 8b9d399b7bd..294971cb3d3 100644 --- a/client/src/redux/save-challenge-saga.js +++ b/client/src/redux/save-challenge-saga.js @@ -1,6 +1,6 @@ import { call, put, select, takeEvery } from 'redux-saga/effects'; -import { canSaveToDB } from '../../../shared/config/challenge-types'; +import { canSaveToDB } from '../../../shared-dist/config/challenge-types'; import { createFlashMessage } from '../components/Flash/redux'; import { FlashMessages } from '../components/Flash/redux/flash-messages'; import { diff --git a/client/src/redux/settings/settings-sagas.js b/client/src/redux/settings/settings-sagas.js index 1b778ab2845..33f165dc33a 100644 --- a/client/src/redux/settings/settings-sagas.js +++ b/client/src/redux/settings/settings-sagas.js @@ -13,7 +13,7 @@ import { navigate } from 'gatsby'; import { certTypeIdMap, certTypes -} from '../../../../shared/config/certification-settings'; +} from '../../../../shared-dist/config/certification-settings'; import { createFlashMessage } from '../../components/Flash/redux'; import { liveCerts } from '../../../config/cert-and-project-map'; import { diff --git a/client/src/templates/Challenges/classic/desktop-layout.tsx b/client/src/templates/Challenges/classic/desktop-layout.tsx index a049d23b0b3..e83b02a68eb 100644 --- a/client/src/templates/Challenges/classic/desktop-layout.tsx +++ b/client/src/templates/Challenges/classic/desktop-layout.tsx @@ -4,7 +4,7 @@ import { ReflexContainer, ReflexSplitter, ReflexElement } from 'react-reflex'; import { createSelector } from 'reselect'; import { connect } from 'react-redux'; import store from 'store'; -import { challengeTypes } from '../../../../../shared/config/challenge-types'; +import { challengeTypes } from '../../../../../shared-dist/config/challenge-types'; import { ChallengeFiles, DailyCodingChallengeLanguages, diff --git a/client/src/templates/Challenges/classic/editor.tsx b/client/src/templates/Challenges/classic/editor.tsx index efbde68e0bc..e3c260b0344 100644 --- a/client/src/templates/Challenges/classic/editor.tsx +++ b/client/src/templates/Challenges/classic/editor.tsx @@ -36,7 +36,7 @@ import { editorNotes } from '../../../utils/tone/editor-notes'; import { canSaveToDB, challengeTypes -} from '../../../../../shared/config/challenge-types'; +} from '../../../../../shared-dist/config/challenge-types'; import { executeChallenge, saveEditorContent, diff --git a/client/src/templates/Challenges/classic/show.tsx b/client/src/templates/Challenges/classic/show.tsx index 5fc8d78d844..50a62b96347 100644 --- a/client/src/templates/Challenges/classic/show.tsx +++ b/client/src/templates/Challenges/classic/show.tsx @@ -11,7 +11,7 @@ import { editor } from 'monaco-editor'; import type { FitAddon } from 'xterm-addon-fit'; import { useFeature } from '@growthbook/growthbook-react'; -import { challengeTypes } from '../../../../../shared/config/challenge-types'; +import { challengeTypes } from '../../../../../shared-dist/config/challenge-types'; import LearnLayout from '../../../components/layouts/learn'; import { MAX_MOBILE_WIDTH } from '../../../../config/misc'; diff --git a/client/src/templates/Challenges/codeally/show.tsx b/client/src/templates/Challenges/codeally/show.tsx index 4a993c0c43c..745a24c666c 100644 --- a/client/src/templates/Challenges/codeally/show.tsx +++ b/client/src/templates/Challenges/codeally/show.tsx @@ -15,7 +15,7 @@ import { useFeature } from '@growthbook/growthbook-react'; import LearnLayout from '../../../components/layouts/learn'; import ChallengeTitle from '../components/challenge-title'; import PrismFormatted from '../components/prism-formatted'; -import { challengeTypes } from '../../../../../shared/config/challenge-types'; +import { challengeTypes } from '../../../../../shared-dist/config/challenge-types'; import CompletionModal from '../components/completion-modal'; import HelpModal from '../components/help-modal'; import Hotkeys from '../components/hotkeys'; @@ -45,7 +45,7 @@ import ProjectToolPanel from '../projects/tool-panel'; import { getChallengePaths } from '../utils/challenge-paths'; import SolutionForm from '../projects/solution-form'; import { FlashMessages } from '../../../components/Flash/redux/flash-messages'; -import { SuperBlocks } from '../../../../../shared/config/curriculum'; +import { SuperBlocks } from '../../../../../shared-dist/config/curriculum'; import { CodeAllyDown } from '../../../components/growth-book/codeally-down'; import { postUserToken } from '../../../utils/ajax'; import { CodeAllyButton } from '../../../components/growth-book/codeally-button'; diff --git a/client/src/templates/Challenges/components/reset-modal.tsx b/client/src/templates/Challenges/components/reset-modal.tsx index c142c99f7aa..0175593e474 100644 --- a/client/src/templates/Challenges/components/reset-modal.tsx +++ b/client/src/templates/Challenges/components/reset-modal.tsx @@ -8,7 +8,7 @@ import { Button, Modal } from '@freecodecamp/ui'; import { closeModal, resetChallenge } from '../redux/actions'; import { isResetModalOpenSelector } from '../redux/selectors'; import callGA from '../../../analytics/call-ga'; -import { canSaveToDB } from '../../../../../shared/config/challenge-types'; +import { canSaveToDB } from '../../../../../shared-dist/config/challenge-types'; interface ResetModalProps { close: () => void; diff --git a/client/src/templates/Challenges/components/tool-panel.tsx b/client/src/templates/Challenges/components/tool-panel.tsx index 0d3b692ea38..3e4b4eaeb53 100644 --- a/client/src/templates/Challenges/components/tool-panel.tsx +++ b/client/src/templates/Challenges/components/tool-panel.tsx @@ -7,7 +7,7 @@ import { connect } from 'react-redux'; import { bindActionCreators, Dispatch } from 'redux'; import { createSelector } from 'reselect'; -import { canSaveToDB } from '../../../../../shared/config/challenge-types'; +import { canSaveToDB } from '../../../../../shared-dist/config/challenge-types'; import { openModal, executeChallenge } from '../redux/actions'; import { challengeMetaSelector } from '../redux/selectors'; import { saveChallenge } from '../../../redux/actions'; diff --git a/client/src/templates/Challenges/generic/show.tsx b/client/src/templates/Challenges/generic/show.tsx index fe8bb31c673..204e34e4775 100644 --- a/client/src/templates/Challenges/generic/show.tsx +++ b/client/src/templates/Challenges/generic/show.tsx @@ -26,7 +26,7 @@ import { initTests } from '../redux/actions'; import { isChallengeCompletedSelector } from '../redux/selectors'; -import { BlockTypes } from '../../../../../shared/config/blocks'; +import { BlockTypes } from '../../../../../shared-dist/config/blocks'; import { getChallengePaths } from '../utils/challenge-paths'; import Scene from '../components/scene/scene'; import MultipleChoiceQuestions from '../components/multiple-choice-questions'; diff --git a/client/src/templates/Challenges/ms-trophy/link-ms-user.tsx b/client/src/templates/Challenges/ms-trophy/link-ms-user.tsx index 16d782d63aa..c1777213be8 100644 --- a/client/src/templates/Challenges/ms-trophy/link-ms-user.tsx +++ b/client/src/templates/Challenges/ms-trophy/link-ms-user.tsx @@ -13,7 +13,7 @@ import { Spacer } from '@freecodecamp/ui'; -import { isMicrosoftTranscriptLink } from '../../../../../shared/utils/validate'; +import { isMicrosoftTranscriptLink } from '../../../../../shared-dist/utils/validate'; import { linkMsUsername, unlinkMsUsername, diff --git a/client/src/templates/Challenges/projects/solution-form.tsx b/client/src/templates/Challenges/projects/solution-form.tsx index 20b326ffd8b..e3a4b3fdf9d 100644 --- a/client/src/templates/Challenges/projects/solution-form.tsx +++ b/client/src/templates/Challenges/projects/solution-form.tsx @@ -2,7 +2,7 @@ import React, { Component } from 'react'; import { withTranslation } from 'react-i18next'; import type { WithTranslation } from 'react-i18next'; -import { challengeTypes } from '../../../../../shared/config/challenge-types'; +import { challengeTypes } from '../../../../../shared-dist/config/challenge-types'; import { StrictSolutionForm, ValidatedValues diff --git a/client/src/templates/Challenges/quiz/show.tsx b/client/src/templates/Challenges/quiz/show.tsx index 6da1cdccb97..7f9982b7234 100644 --- a/client/src/templates/Challenges/quiz/show.tsx +++ b/client/src/templates/Challenges/quiz/show.tsx @@ -19,7 +19,7 @@ import { } from '@freecodecamp/ui'; // Local Utilities -import { shuffleArray } from '../../../../../shared/utils/shuffle-array'; +import { shuffleArray } from '../../../../../shared-dist/utils/shuffle-array'; import LearnLayout from '../../../components/layouts/learn'; import { ChallengeNode, ChallengeMeta, Test } from '../../../redux/prop-types'; import ChallengeDescription from '../components/challenge-description'; diff --git a/client/src/templates/Challenges/rechallenge/transformers.js b/client/src/templates/Challenges/rechallenge/transformers.js index 3ce07e67f82..1d0b263042d 100644 --- a/client/src/templates/Challenges/rechallenge/transformers.js +++ b/client/src/templates/Challenges/rechallenge/transformers.js @@ -15,7 +15,7 @@ import { transformHeadTailAndContents, compileHeadTail, createSource -} from '../../../../../shared/utils/polyvinyl'; +} from '../../../../../shared-dist/utils/polyvinyl'; import { WorkerExecutor } from '../utils/worker-executor'; import { compileTypeScriptCode, diff --git a/client/src/templates/Challenges/redux/code-storage-epic.js b/client/src/templates/Challenges/redux/code-storage-epic.js index d4bf25b1a7b..4ac79d62b03 100644 --- a/client/src/templates/Challenges/redux/code-storage-epic.js +++ b/client/src/templates/Challenges/redux/code-storage-epic.js @@ -3,7 +3,7 @@ import { of } from 'rxjs'; import { filter, ignoreElements, map, switchMap, tap } from 'rxjs/operators'; import store from 'store'; -import { isPoly, setContent } from '../../../../../shared/utils/polyvinyl'; +import { isPoly, setContent } from '../../../../../shared-dist/utils/polyvinyl'; import { createFlashMessage } from '../../../components/Flash/redux'; import { FlashMessages } from '../../../components/Flash/redux/flash-messages'; import { savedChallengesSelector } from '../../../redux/selectors'; @@ -11,7 +11,7 @@ import { actionTypes as appTypes } from '../../../redux/action-types'; import { getIsDailyCodingChallenge, getDailyCodingChallengeLanguage -} from '../../../../../shared/config/challenge-types'; +} from '../../../../../shared-dist/config/challenge-types'; import { actionTypes } from './action-types'; import { noStoredCodeFound, updateFile } from './actions'; import { challengeFilesSelector, challengeMetaSelector } from './selectors'; diff --git a/client/src/templates/Challenges/redux/completion-epic.js b/client/src/templates/Challenges/redux/completion-epic.js index 52f910b0a7a..04f2ef0cbbe 100644 --- a/client/src/templates/Challenges/redux/completion-epic.js +++ b/client/src/templates/Challenges/redux/completion-epic.js @@ -21,7 +21,7 @@ import { getIsDailyCodingChallenge, getDailyCodingChallengeLanguage, submitTypes -} from '../../../../../shared/config/challenge-types'; +} from '../../../../../shared-dist/config/challenge-types'; import { actionTypes as submitActionTypes } from '../../../redux/action-types'; import { allowSectionDonationRequests, @@ -35,7 +35,7 @@ import { isSignedInSelector, userSelector } from '../../../redux/selectors'; import { mapFilesToChallengeFiles } from '../../../utils/ajax'; import { standardizeRequestBody } from '../../../utils/challenge-request-helpers'; import postUpdate$ from '../utils/post-update'; -import { SuperBlocks } from '../../../../../shared/config/curriculum'; +import { SuperBlocks } from '../../../../../shared-dist/config/curriculum'; import { actionTypes } from './action-types'; import { closeModal, diff --git a/client/src/templates/Challenges/redux/create-question-epic.js b/client/src/templates/Challenges/redux/create-question-epic.js index a132b9c6bb7..704cbd08187 100644 --- a/client/src/templates/Challenges/redux/create-question-epic.js +++ b/client/src/templates/Challenges/redux/create-question-epic.js @@ -4,7 +4,7 @@ import { mapTo, tap } from 'rxjs/operators'; import envData from '../../../../config/env.json'; import { transformEditorLink } from '../utils'; -import { challengeTypes } from '../../../../../shared/config/challenge-types'; +import { challengeTypes } from '../../../../../shared-dist/config/challenge-types'; import { actionTypes } from './action-types'; import { closeModal } from './actions'; import { diff --git a/client/src/templates/Challenges/redux/execute-challenge-saga.js b/client/src/templates/Challenges/redux/execute-challenge-saga.js index 50ad7e4228f..f3b0309fb31 100644 --- a/client/src/templates/Challenges/redux/execute-challenge-saga.js +++ b/client/src/templates/Challenges/redux/execute-challenge-saga.js @@ -17,7 +17,7 @@ import { import { canSaveToDB, challengeTypes -} from '../../../../../shared/config/challenge-types'; +} from '../../../../../shared-dist/config/challenge-types'; import { createFlashMessage } from '../../../components/Flash/redux'; import { FlashMessages } from '../../../components/Flash/redux/flash-messages'; import { diff --git a/client/src/templates/Challenges/redux/index.js b/client/src/templates/Challenges/redux/index.js index 6b74d5e4bed..d3784bc1864 100644 --- a/client/src/templates/Challenges/redux/index.js +++ b/client/src/templates/Challenges/redux/index.js @@ -1,7 +1,7 @@ import { isEmpty } from 'lodash-es'; import { handleActions } from 'redux-actions'; -import { getLines } from '../../../../../shared/utils/get-lines'; +import { getLines } from '../../../../../shared-dist/utils/get-lines'; import { getTargetEditor } from '../utils/get-target-editor'; import { actionTypes, ns } from './action-types'; import codeStorageEpic from './code-storage-epic'; diff --git a/client/src/templates/Challenges/redux/selectors.js b/client/src/templates/Challenges/redux/selectors.js index b1bbf59fd93..d8a39203593 100644 --- a/client/src/templates/Challenges/redux/selectors.js +++ b/client/src/templates/Challenges/redux/selectors.js @@ -1,5 +1,5 @@ import { createSelector } from 'reselect'; -import { challengeTypes } from '../../../../../shared/config/challenge-types'; +import { challengeTypes } from '../../../../../shared-dist/config/challenge-types'; import { completedChallengesSelector, allChallengesInfoSelector, diff --git a/client/src/templates/Challenges/utils/build.ts b/client/src/templates/Challenges/utils/build.ts index 4652f5e7d64..9ae46ef6eb2 100644 --- a/client/src/templates/Challenges/utils/build.ts +++ b/client/src/templates/Challenges/utils/build.ts @@ -1,4 +1,4 @@ -import { challengeTypes } from '../../../../../shared/config/challenge-types'; +import { challengeTypes } from '../../../../../shared-dist/config/challenge-types'; import type { ChallengeFile } from '../../../redux/prop-types'; import { concatHtml } from '../rechallenge/builders'; diff --git a/client/src/templates/Introduction/components/block-header.tsx b/client/src/templates/Introduction/components/block-header.tsx index 8f23186107c..b2869b2d018 100644 --- a/client/src/templates/Introduction/components/block-header.tsx +++ b/client/src/templates/Introduction/components/block-header.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { isEmpty } from 'lodash'; -import { BlockTypes } from '../../../../../shared/config/blocks'; +import { BlockTypes } from '../../../../../shared-dist/config/blocks'; import { ProgressBar } from '../../../components/Progress/progress-bar'; import DropDown from '../../../assets/icons/dropdown'; import CheckMark from './check-mark'; diff --git a/client/src/templates/Introduction/components/block-label.tsx b/client/src/templates/Introduction/components/block-label.tsx index 7b889944821..32502fcf744 100644 --- a/client/src/templates/Introduction/components/block-label.tsx +++ b/client/src/templates/Introduction/components/block-label.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import { BlockTypes } from '../../../../../shared/config/blocks'; +import { BlockTypes } from '../../../../../shared-dist/config/blocks'; interface BlockLabelProps { blockType: BlockTypes; diff --git a/client/src/templates/Introduction/components/block.test.tsx b/client/src/templates/Introduction/components/block.test.tsx index 2460b4478c7..ca61d9ec9aa 100644 --- a/client/src/templates/Introduction/components/block.test.tsx +++ b/client/src/templates/Introduction/components/block.test.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; import { describe, it, expect, afterEach, vi, Mock } from 'vitest'; import type { TFunction } from 'i18next'; -import { SuperBlocks } from '../../../../../shared/config/curriculum'; +import { SuperBlocks } from '../../../../../shared-dist/config/curriculum'; import { ChallengeFiles, PrerequisiteChallenge, @@ -12,11 +12,14 @@ import { FileKeyChallenge, BilibiliIds } from '../../../redux/prop-types'; -import { isAuditedSuperBlock } from '../../../../../shared/utils/is-audited'; -import { BlockLayouts, BlockTypes } from '../../../../../shared/config/blocks'; +import { isAuditedSuperBlock } from '../../../../../shared-dist/utils/is-audited'; +import { + BlockLayouts, + BlockTypes +} from '../../../../../shared-dist/config/blocks'; import { Block } from './block'; -vi.mock('../../../../../shared/utils/is-audited', () => ({ +vi.mock('../../../../../shared-dist/utils/is-audited', () => ({ isAuditedSuperBlock: vi.fn().mockReturnValueOnce(true) })); diff --git a/client/src/templates/Introduction/components/block.tsx b/client/src/templates/Introduction/components/block.tsx index ca78502480b..bdc41b86f07 100644 --- a/client/src/templates/Introduction/components/block.tsx +++ b/client/src/templates/Introduction/components/block.tsx @@ -7,20 +7,23 @@ import { bindActionCreators, Dispatch } from 'redux'; import { createSelector } from 'reselect'; import { Spacer } from '@freecodecamp/ui'; -import { challengeTypes } from '../../../../../shared/config/challenge-types'; +import { challengeTypes } from '../../../../../shared-dist/config/challenge-types'; import { chapterBasedSuperBlocks, SuperBlocks -} from '../../../../../shared/config/curriculum'; +} from '../../../../../shared-dist/config/curriculum'; import envData from '../../../../config/env.json'; -import { isAuditedSuperBlock } from '../../../../../shared/utils/is-audited'; +import { isAuditedSuperBlock } from '../../../../../shared-dist/utils/is-audited'; import Caret from '../../../assets/icons/caret'; import { Link } from '../../../components/helpers'; import { completedChallengesSelector } from '../../../redux/selectors'; import { playTone } from '../../../utils/tone'; import { makeExpandedBlockSelector, toggleBlock } from '../redux'; import { isProjectBased } from '../../../utils/curriculum-layout'; -import { BlockLayouts, BlockTypes } from '../../../../../shared/config/blocks'; +import { + BlockLayouts, + BlockTypes +} from '../../../../../shared-dist/config/blocks'; import CheckMark from './check-mark'; import Challenges from './challenges'; import BlockLabel from './block-label'; diff --git a/client/src/templates/Introduction/components/cert-challenge.tsx b/client/src/templates/Introduction/components/cert-challenge.tsx index 36bdfdeb2b5..2a21b9ae4ec 100644 --- a/client/src/templates/Introduction/components/cert-challenge.tsx +++ b/client/src/templates/Introduction/components/cert-challenge.tsx @@ -7,8 +7,8 @@ import { Button } from '@freecodecamp/ui'; import { certSlugTypeMap, superBlockCertTypeMap -} from '../../../../../shared/config/certification-settings'; -import { SuperBlocks } from '../../../../../shared/config/curriculum'; +} from '../../../../../shared-dist/config/certification-settings'; +import { SuperBlocks } from '../../../../../shared-dist/config/curriculum'; import { isSignedInSelector, diff --git a/client/src/templates/Introduction/components/challenges.tsx b/client/src/templates/Introduction/components/challenges.tsx index e61d150280c..8d166763513 100644 --- a/client/src/templates/Introduction/components/challenges.tsx +++ b/client/src/templates/Introduction/components/challenges.tsx @@ -3,8 +3,8 @@ import { withTranslation, useTranslation } from 'react-i18next'; import GreenNotCompleted from '../../../assets/icons/green-not-completed'; import GreenPass from '../../../assets/icons/green-pass'; -import { SuperBlocks } from '../../../../../shared/config/curriculum'; -import { challengeTypes } from '../../../../../shared/config/challenge-types'; +import { SuperBlocks } from '../../../../../shared-dist/config/curriculum'; +import { challengeTypes } from '../../../../../shared-dist/config/challenge-types'; import { Link } from '../../../components/helpers'; import { ButtonLink } from '../../../components/helpers/button-link'; diff --git a/client/src/templates/Introduction/components/help-translate.tsx b/client/src/templates/Introduction/components/help-translate.tsx index a39fdfa2a28..ffdbec4254e 100644 --- a/client/src/templates/Introduction/components/help-translate.tsx +++ b/client/src/templates/Introduction/components/help-translate.tsx @@ -1,8 +1,8 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { Spacer } from '@freecodecamp/ui'; -import { SuperBlocks } from '../../../../../shared/config/curriculum'; -import { isAuditedSuperBlock } from '../../../../../shared/utils/is-audited'; +import { SuperBlocks } from '../../../../../shared-dist/config/curriculum'; +import { isAuditedSuperBlock } from '../../../../../shared-dist/utils/is-audited'; import { Link } from '../../../components/helpers'; import envData from '../../../../config/env.json'; diff --git a/client/src/templates/Introduction/components/legacy-links.tsx b/client/src/templates/Introduction/components/legacy-links.tsx index 42ed3fafeb5..6ef7d93dc39 100644 --- a/client/src/templates/Introduction/components/legacy-links.tsx +++ b/client/src/templates/Introduction/components/legacy-links.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { Alert } from '@freecodecamp/ui'; -import { SuperBlocks } from '../../../../../shared/config/curriculum'; +import { SuperBlocks } from '../../../../../shared-dist/config/curriculum'; import { isOldRespCert, isRelationalDbCert, diff --git a/client/src/templates/Introduction/components/super-block-accordion.tsx b/client/src/templates/Introduction/components/super-block-accordion.tsx index 10d77ddc098..5dff8899904 100644 --- a/client/src/templates/Introduction/components/super-block-accordion.tsx +++ b/client/src/templates/Introduction/components/super-block-accordion.tsx @@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next'; // TODO: Add this component to freecodecamp/ui and remove this dependency import { Disclosure } from '@headlessui/react'; -import { SuperBlocks } from '../../../../../shared/config/curriculum'; +import { SuperBlocks } from '../../../../../shared-dist/config/curriculum'; import DropDown from '../../../assets/icons/dropdown'; // TODO: source the superblock structure via a GQL query, rather than directly // from the curriculum @@ -12,10 +12,13 @@ import fullStackOpen from '../../../../../curriculum/structure/superblocks/full- import a1Spanish from '../../../../../curriculum/structure/superblocks/a1-professional-spanish.json'; import { ChapterIcon } from '../../../assets/chapter-icon'; -import { type Chapter } from '../../../../../shared/config/chapters'; -import { BlockLayouts, BlockTypes } from '../../../../../shared/config/blocks'; -import { FsdChapters } from '../../../../../shared/config/chapters'; -import { type Module } from '../../../../../shared/config/modules'; +import { type Chapter } from '../../../../../shared-dist/config/chapters'; +import { + BlockLayouts, + BlockTypes +} from '../../../../../shared-dist/config/blocks'; +import { FsdChapters } from '../../../../../shared-dist/config/chapters'; +import { type Module } from '../../../../../shared-dist/config/modules'; import envData from '../../../../config/env.json'; import Block from './block'; import CheckMark from './check-mark'; diff --git a/client/src/templates/Introduction/components/super-block-intro.tsx b/client/src/templates/Introduction/components/super-block-intro.tsx index cae5b16f71e..5c329a3e542 100644 --- a/client/src/templates/Introduction/components/super-block-intro.tsx +++ b/client/src/templates/Introduction/components/super-block-intro.tsx @@ -4,7 +4,7 @@ import { useTranslation, Trans } from 'react-i18next'; import { Alert, Spacer, Container, Row, Col, Callout } from '@freecodecamp/ui'; import { ConnectedProps, connect } from 'react-redux'; import { useFeatureIsOn } from '@growthbook/growthbook-react'; -import { SuperBlocks } from '../../../../../shared/config/curriculum'; +import { SuperBlocks } from '../../../../../shared-dist/config/curriculum'; import { SuperBlockIcon } from '../../../assets/superblock-icon'; import { Link } from '../../../components/helpers'; import CapIcon from '../../../assets/icons/cap'; diff --git a/client/src/templates/Introduction/super-block-intro.tsx b/client/src/templates/Introduction/super-block-intro.tsx index 51dced68295..b0db3ef2991 100644 --- a/client/src/templates/Introduction/super-block-intro.tsx +++ b/client/src/templates/Introduction/super-block-intro.tsx @@ -14,7 +14,7 @@ import { Container, Col, Row, Spacer } from '@freecodecamp/ui'; import { chapterBasedSuperBlocks, SuperBlocks -} from '../../../../shared/config/curriculum'; +} from '../../../../shared-dist/config/curriculum'; import DonateModal from '../../components/Donation/donation-modal'; import Login from '../../components/Header/components/login'; import Map from '../../components/Map'; @@ -29,8 +29,11 @@ import { } from '../../redux/selectors'; import type { User } from '../../redux/prop-types'; import { CertTitle, liveCerts } from '../../../config/cert-and-project-map'; -import { superBlockToCertMap } from '../../../../shared/config/certification-settings'; -import { BlockLayouts, BlockTypes } from '../../../../shared/config/blocks'; +import { superBlockToCertMap } from '../../../../shared-dist/config/certification-settings'; +import { + BlockLayouts, + BlockTypes +} from '../../../../shared-dist/config/blocks'; import Block from './components/block'; import CertChallenge from './components/cert-challenge'; import LegacyLinks from './components/legacy-links'; diff --git a/client/src/utils/__fixtures__/completed-challenges.ts b/client/src/utils/__fixtures__/completed-challenges.ts index b76a0b1acf6..4263cc1e7da 100644 --- a/client/src/utils/__fixtures__/completed-challenges.ts +++ b/client/src/utils/__fixtures__/completed-challenges.ts @@ -1,5 +1,5 @@ import { challengeFiles } from '../../../utils/__fixtures__/challenges'; -import { challengeTypes } from '../../../../shared/config/challenge-types'; +import { challengeTypes } from '../../../../shared-dist/config/challenge-types'; const baseChallenge = { id: '1', diff --git a/client/src/utils/ajax.ts b/client/src/utils/ajax.ts index f2ae23c74a2..75b45a2874f 100644 --- a/client/src/utils/ajax.ts +++ b/client/src/utils/ajax.ts @@ -12,7 +12,7 @@ import type { SurveyResults, User } from '../redux/prop-types'; -import { DonationDuration } from '../../../shared/config/donation-settings'; +import { DonationDuration } from '../../../shared-dist/config/donation-settings'; const { apiLocation } = envData; diff --git a/client/src/utils/curriculum-layout.ts b/client/src/utils/curriculum-layout.ts index 1ad52852362..92bcd9ac951 100644 --- a/client/src/utils/curriculum-layout.ts +++ b/client/src/utils/curriculum-layout.ts @@ -1,4 +1,4 @@ -import { challengeTypes } from '../../../shared/config/challenge-types'; +import { challengeTypes } from '../../../shared-dist/config/challenge-types'; // Show a single project in a certification layout diff --git a/client/src/utils/is-a-cert.ts b/client/src/utils/is-a-cert.ts index 21db7cf73ab..a047fea5f9f 100644 --- a/client/src/utils/is-a-cert.ts +++ b/client/src/utils/is-a-cert.ts @@ -1,4 +1,4 @@ -import { SuperBlocks } from '../../../shared/config/curriculum'; +import { SuperBlocks } from '../../../shared-dist/config/curriculum'; export function isOldRespCert(superBlock: string): boolean { return superBlock === String(SuperBlocks.RespWebDesign); diff --git a/client/src/utils/math-jax.ts b/client/src/utils/math-jax.ts index da838145d05..f1a2a0e388b 100644 --- a/client/src/utils/math-jax.ts +++ b/client/src/utils/math-jax.ts @@ -1,4 +1,4 @@ -import { SuperBlocks } from '../../../shared/config/curriculum'; +import { SuperBlocks } from '../../../shared-dist/config/curriculum'; import { scriptLoader } from './script-loaders'; export const mathJaxSrc = diff --git a/client/src/utils/path-parsers.ts b/client/src/utils/path-parsers.ts index d401e435dc0..407bfdbe044 100644 --- a/client/src/utils/path-parsers.ts +++ b/client/src/utils/path-parsers.ts @@ -1,5 +1,5 @@ import { WindowLocation } from '@gatsbyjs/reach-router'; -import { i18nConstants } from '../../../shared/config/constants'; +import { i18nConstants } from '../../../shared-dist/config/constants'; const splitPath = (pathname: string): string[] => pathname.split('/').filter(x => x); diff --git a/client/src/utils/solution-display-type.ts b/client/src/utils/solution-display-type.ts index e84839b0bbd..a5086f51144 100644 --- a/client/src/utils/solution-display-type.ts +++ b/client/src/utils/solution-display-type.ts @@ -1,5 +1,5 @@ import type { CompletedChallenge } from '../redux/prop-types'; -import { challengeTypes } from '../../../shared/config/challenge-types'; +import { challengeTypes } from '../../../shared-dist/config/challenge-types'; import { hasProtocolRE } from '.'; type DisplayType = diff --git a/client/tools/create-env.ts b/client/tools/create-env.ts index 9994838663f..881e7509e6a 100644 --- a/client/tools/create-env.ts +++ b/client/tools/create-env.ts @@ -2,7 +2,7 @@ import { spawn } from 'child_process'; import * as fs from 'fs'; import * as path from 'path'; -import { availableLangs, Languages } from '../../shared/config/i18n'; +import { availableLangs, Languages } from '../../shared-dist/config/i18n'; import env from './read-env'; const configPath = path.resolve(__dirname, '../config'); @@ -14,7 +14,7 @@ function checkClientLocale() { if (!availableLangs.client.includes(process.env.CLIENT_LOCALE as Languages)) { throw Error(` - CLIENT_LOCALE, ${process.env.CLIENT_LOCALE}, is not an available language in shared/config/i18n.ts + CLIENT_LOCALE, ${process.env.CLIENT_LOCALE}, is not an available language in shared-dist/config/i18n.ts `); } @@ -30,7 +30,7 @@ function checkCurriculumLocale() { ) { throw Error(` - CURRICULUM_LOCALE, ${process.env.CURRICULUM_LOCALE}, is not an available language in shared/config/i18n.ts + CURRICULUM_LOCALE, ${process.env.CURRICULUM_LOCALE}, is not an available language in shared-dist/config/i18n.ts `); } diff --git a/client/tools/generate-search-placeholder.ts b/client/tools/generate-search-placeholder.ts index 3dc73c0f7f8..b1201c96b1f 100644 --- a/client/tools/generate-search-placeholder.ts +++ b/client/tools/generate-search-placeholder.ts @@ -11,7 +11,7 @@ import { environment } from '../config/env.json'; import { newsIndex } from '../src/utils/algolia-locale-setup'; -import { i18nextCodes } from '../../shared/config/i18n'; +import { i18nextCodes } from '../../shared-dist/config/i18n'; const i18nextCode = i18nextCodes[clientLocale as keyof typeof i18nextCodes]; diff --git a/client/utils/gatsby/challenge-page-creator.js b/client/utils/gatsby/challenge-page-creator.js index 17178a00dff..7cb872fb03d 100644 --- a/client/utils/gatsby/challenge-page-creator.js +++ b/client/utils/gatsby/challenge-page-creator.js @@ -1,5 +1,5 @@ const path = require('path'); -const { viewTypes } = require('../../../shared/config/challenge-types'); +const { viewTypes } = require('../../../shared-dist/config/challenge-types'); const backend = path.resolve( __dirname, diff --git a/client/utils/index.ts b/client/utils/index.ts index 76587d22d3a..1e85eab1a48 100644 --- a/client/utils/index.ts +++ b/client/utils/index.ts @@ -1,4 +1,4 @@ -import { Certification } from '../../shared/config/certification-settings'; +import { Certification } from '../../shared-dist/config/certification-settings'; const idToPath = new Map( Object.entries({ diff --git a/curriculum/build-superblock.js b/curriculum/build-superblock.js index 7649a2ddd09..d6f34dd2037 100644 --- a/curriculum/build-superblock.js +++ b/curriculum/build-superblock.js @@ -4,8 +4,8 @@ const { isEmpty } = require('lodash'); const debug = require('debug')('fcc:build-superblock'); const { parseMD } = require('../tools/challenge-parser/parser'); -const { createPoly } = require('../shared/utils/polyvinyl'); -const { isAuditedSuperBlock } = require('../shared/utils/is-audited'); +const { createPoly } = require('../shared-dist/utils/polyvinyl'); +const { isAuditedSuperBlock } = require('../shared-dist/utils/is-audited'); const { translateCommentsInChallenge } = require('../tools/challenge-parser/translation-parser'); diff --git a/curriculum/build-superblock.test.js b/curriculum/build-superblock.test.js index 1b454cf9226..c6dff65bda5 100644 --- a/curriculum/build-superblock.test.js +++ b/curriculum/build-superblock.test.js @@ -1,5 +1,5 @@ import { describe, test, expect, vi } from 'vitest'; -import { isPoly } from '../shared/utils/polyvinyl.js'; +import { isPoly } from '../shared-dist/utils/polyvinyl.js'; import { validateChallenges, buildBlock, diff --git a/curriculum/get-challenges.js b/curriculum/get-challenges.js index 401896bc759..72554bea10d 100644 --- a/curriculum/get-challenges.js +++ b/curriculum/get-challenges.js @@ -3,7 +3,7 @@ const path = require('path'); const util = require('util'); const { curriculum: curriculumLangs } = - require('../shared/config/i18n').availableLangs; + require('../shared-dist/config/i18n').availableLangs; const { buildCurriculum } = require('./build-curriculum'); const access = util.promisify(fs.access); diff --git a/curriculum/schema/challenge-schema.js b/curriculum/schema/challenge-schema.js index c46f53d3882..714a9107eb8 100644 --- a/curriculum/schema/challenge-schema.js +++ b/curriculum/schema/challenge-schema.js @@ -1,11 +1,11 @@ const Joi = require('joi'); Joi.objectId = require('joi-objectid')(Joi); -const { challengeTypes } = require('../../shared/config/challenge-types'); +const { challengeTypes } = require('../../shared-dist/config/challenge-types'); const { chapterBasedSuperBlocks, catalogSuperBlocks -} = require('../../shared/config/curriculum'); +} = require('../../shared-dist/config/curriculum'); const { availableCharacters, availableBackgrounds, diff --git a/curriculum/test/utils/sort-challenges.test.js b/curriculum/test/utils/sort-challenges.test.js index 0ae15451eb9..89d04fb2eb7 100644 --- a/curriculum/test/utils/sort-challenges.test.js +++ b/curriculum/test/utils/sort-challenges.test.js @@ -1,6 +1,6 @@ import { describe, it, expect } from 'vitest'; -import { shuffleArray } from '../../../shared/utils/shuffle-array.js'; +import { shuffleArray } from '../../../shared-dist/utils/shuffle-array.js'; import { sortChallenges } from './sort-challenges.js'; const challenges = [ diff --git a/curriculum/utils.js b/curriculum/utils.js index 0e8fabe1de9..df0ad1d1a17 100644 --- a/curriculum/utils.js +++ b/curriculum/utils.js @@ -1,12 +1,12 @@ const path = require('path'); const { - generateSuperBlockList, - SuperBlocks -} = require('../shared/config/curriculum'); + SuperBlocks, + generateSuperBlockList +} = require('../shared-dist/config/curriculum'); require('dotenv').config({ path: path.resolve(__dirname, '../.env') }); -const { availableLangs } = require('../shared/config/i18n'); +const { availableLangs } = require('../shared-dist/config/i18n'); const curriculumLangs = availableLangs.curriculum; // checks that the CURRICULUM_LOCALE exists and is an available language diff --git a/curriculum/utils.test.ts b/curriculum/utils.test.ts index efbf247ee0f..6229fbf9679 100644 --- a/curriculum/utils.test.ts +++ b/curriculum/utils.test.ts @@ -2,7 +2,7 @@ import path from 'path'; import { config } from 'dotenv'; import { describe, it, expect } from 'vitest'; -import { SuperBlocks } from '../shared/config/curriculum'; +import { SuperBlocks } from '../shared-dist/config/curriculum'; import { createSuperOrder, filterByBlock, diff --git a/docker/api/Dockerfile b/docker/api/Dockerfile index 8f7abaa86cf..7a9f0a8c76a 100644 --- a/docker/api/Dockerfile +++ b/docker/api/Dockerfile @@ -18,18 +18,16 @@ COPY --chown=node:node client/ client/ RUN pnpm config set dedupe-peer-dependents false # While we want to ignore scripts generally, we do need to generate the prisma -# client. Note: npx is the simplest way to generate the client without us having -# to have prisma as a prod dependency. The jq tricks are to ensure we're using -# the right version of prisma. -RUN pnpm install -F=api -F=curriculum -F tools/scripts/build -F challenge-parser \ ---frozen-lockfile --ignore-scripts -RUN cd api && npx prisma@$(jq -r '.devDependencies.prisma' < package.json) generate +# client. +RUN pnpm install --frozen-lockfile --ignore-scripts +RUN cd api && pnpm prisma generate # The api needs to source curriculum.json and build:curriculum relies on the # following env vars. ARG SHOW_UPCOMING_CHANGES=false ENV SHOW_UPCOMING_CHANGES=$SHOW_UPCOMING_CHANGES +RUN pnpm create:shared RUN pnpm build:curriculum RUN pnpm -F=api build diff --git a/eslint.config.mjs b/eslint.config.mjs index 51863ab63b9..39d86adb128 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -35,7 +35,8 @@ export default tseslint.config( 'shared/**/*.js', 'docs/**/*.md', '**/playwright*.config.ts', - 'playwright/**/*' + 'playwright/**/*', + 'shared-dist/**/*' ] }, js.configs.recommended, diff --git a/jest.config.js b/jest.config.js index 0c9aa39cd07..0e2bb0431ea 100644 --- a/jest.config.js +++ b/jest.config.js @@ -8,7 +8,8 @@ module.exports = { 'tools/scripts/build/', 'tools/scripts/lint/', 'curriculum', - 'client' + 'client', + 'shared/' ], moduleNameMapper: { // CSS Modules - match files that end with 'module.css' diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ed161ad5ec7..7c156074a74 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -195,11 +195,11 @@ importers: specifier: ^7.0.5 version: 7.0.5 ajv: - specifier: 8.12.0 - version: 8.12.0 + specifier: 8.17.1 + version: 8.17.1 ajv-formats: - specifier: 2.1.1 - version: 2.1.1(ajv@8.12.0) + specifier: 3.0.1 + version: 3.0.1(ajv@8.17.1) date-fns: specifier: 4.1.0 version: 4.1.0 @@ -227,6 +227,9 @@ importers: lodash: specifier: 4.17.21 version: 4.17.21 + lodash-es: + specifier: 4.17.21 + version: 4.17.21 mongodb: specifier: 6.10.0 version: 6.10.0(@aws-sdk/credential-providers@3.521.0)(socks@2.8.3) @@ -252,8 +255,8 @@ importers: specifier: 16.0.0 version: 16.0.0 validator: - specifier: 13.11.0 - version: 13.11.0 + specifier: 13.15.15 + version: 13.15.15 devDependencies: '@total-typescript/ts-reset': specifier: 0.5.1 @@ -261,6 +264,9 @@ importers: '@types/jsonwebtoken': specifier: 9.0.5 version: 9.0.5 + '@types/lodash-es': + specifier: ^4.17.12 + version: 4.17.12 '@types/nodemailer': specifier: 6.4.14 version: 6.4.14 @@ -5412,14 +5418,6 @@ packages: resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} engines: {node: '>=8'} - ajv-formats@2.1.1: - resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} - peerDependencies: - ajv: ^8.0.0 - peerDependenciesMeta: - ajv: - optional: true - ajv-formats@3.0.1: resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} peerDependencies: @@ -5439,6 +5437,9 @@ packages: ajv@8.12.0: resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==} + ajv@8.17.1: + resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + algoliasearch-helper@3.22.5: resolution: {integrity: sha512-lWvhdnc+aKOKx8jyA3bsdEgHzm/sglC4cYdMG4xSQyRiPLJVJtH/IVYZG3Hp6PkTEhQqhyVYkeP9z2IlcHJsWw==} peerDependencies: @@ -13866,6 +13867,10 @@ packages: resolution: {integrity: sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==} engines: {node: '>= 0.10'} + validator@13.15.15: + resolution: {integrity: sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A==} + engines: {node: '>= 0.10'} + value-or-promise@1.0.6: resolution: {integrity: sha512-9r0wQsWD8z/BxPOvnwbPf05ZvFngXyouE9EKB+5GbYix+BYnAwrIChCUyFIinfbf2FL/U71z+CPpbnmTdxrwBg==} engines: {node: '>=12'} @@ -17311,8 +17316,8 @@ snapshots: '@fastify/ajv-compiler@4.0.2': dependencies: - ajv: 8.12.0 - ajv-formats: 3.0.1(ajv@8.12.0) + ajv: 8.17.1 + ajv-formats: 3.0.1(ajv@8.17.1) fast-uri: 3.0.6 '@fastify/cookie@11.0.1': @@ -20594,13 +20599,9 @@ snapshots: clean-stack: 2.2.0 indent-string: 4.0.0 - ajv-formats@2.1.1(ajv@8.12.0): + ajv-formats@3.0.1(ajv@8.17.1): optionalDependencies: - ajv: 8.12.0 - - ajv-formats@3.0.1(ajv@8.12.0): - optionalDependencies: - ajv: 8.12.0 + ajv: 8.17.1 ajv-keywords@3.5.2(ajv@6.12.6): dependencies: @@ -20620,6 +20621,13 @@ snapshots: require-from-string: 2.0.2 uri-js: 4.4.1 + ajv@8.17.1: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.0.6 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + algoliasearch-helper@3.22.5(algoliasearch@4.22.1): dependencies: '@algolia/events': 4.0.1 @@ -23847,8 +23855,8 @@ snapshots: fast-json-stringify@6.0.1: dependencies: '@fastify/merge-json-schemas': 0.2.1 - ajv: 8.12.0 - ajv-formats: 3.0.1(ajv@8.12.0) + ajv: 8.17.1 + ajv-formats: 3.0.1(ajv@8.17.1) fast-uri: 3.0.6 json-schema-ref-resolver: 2.0.1 rfdc: 1.4.1 @@ -31830,6 +31838,8 @@ snapshots: validator@13.11.0: {} + validator@13.15.15: {} + value-or-promise@1.0.6: {} vary@1.1.2: {} diff --git a/shared/config/certification-settings.ts b/shared/config/certification-settings.ts index 0089090afda..ca9352395b2 100644 --- a/shared/config/certification-settings.ts +++ b/shared/config/certification-settings.ts @@ -1,4 +1,4 @@ -import { SuperBlocks } from '../../shared/config/curriculum'; +import { SuperBlocks } from '../config/curriculum.js'; /** * Certifications are not equivalent to superblocks. Each superblock corresponds diff --git a/shared/config/curriculum.ts b/shared/config/curriculum.ts index 4dd7e01b6ec..91853e428f2 100644 --- a/shared/config/curriculum.ts +++ b/shared/config/curriculum.ts @@ -1,6 +1,6 @@ // TODO: eventually this should all flow from the curriculum service, since it // defines the top-level structure of the curriculum. -import { Languages } from './i18n'; +import { Languages } from './i18n.js'; // all superblocks export enum SuperBlocks { diff --git a/shared/package.json b/shared/package.json index 74a608aa0ad..a212d7e1a62 100644 --- a/shared/package.json +++ b/shared/package.json @@ -9,6 +9,7 @@ "node": ">=16", "pnpm": ">=10" }, + "type": "module", "repository": { "type": "git", "url": "git+https://github.com/freeCodeCamp/freeCodeCamp.git" diff --git a/shared/tsconfig.json b/shared/tsconfig.json index e80c67a8f20..058aabd062b 100644 --- a/shared/tsconfig.json +++ b/shared/tsconfig.json @@ -2,6 +2,8 @@ "include": ["**/*.ts", "**/*.test.ts"], "extends": "../tsconfig-base.json", "compilerOptions": { + "outDir": "../shared-dist", + "declaration": true, "noEmit": false, "module": "CommonJS" } diff --git a/shared/utils/is-audited.ts b/shared/utils/is-audited.ts index 0c420a1afd5..52222eb5673 100644 --- a/shared/utils/is-audited.ts +++ b/shared/utils/is-audited.ts @@ -1,7 +1,4 @@ -import { - type SuperBlocks, - getAuditedSuperBlocks -} from '../../shared/config/curriculum'; +import { type SuperBlocks, getAuditedSuperBlocks } from '../config/curriculum'; export function isAuditedSuperBlock(language: string, superblock: SuperBlocks) { // TODO: when all the consumers of this function use TypeScript we can remove diff --git a/tools/scripts/build/build-external-curricula-data-v1.test.ts b/tools/scripts/build/build-external-curricula-data-v1.test.ts index 5a3a976620f..8047bb785c8 100644 --- a/tools/scripts/build/build-external-curricula-data-v1.test.ts +++ b/tools/scripts/build/build-external-curricula-data-v1.test.ts @@ -8,7 +8,7 @@ import { SuperBlocks, SuperBlockStage, superBlockStages -} from '../../../shared/config/curriculum'; +} from '../../../shared-dist/config/curriculum'; import { superblockSchemaValidator, availableSuperBlocksValidator diff --git a/tools/scripts/build/build-external-curricula-data-v1.ts b/tools/scripts/build/build-external-curricula-data-v1.ts index dde6557311c..4d79ba46752 100644 --- a/tools/scripts/build/build-external-curricula-data-v1.ts +++ b/tools/scripts/build/build-external-curricula-data-v1.ts @@ -1,9 +1,9 @@ import { mkdirSync, writeFileSync, readFileSync } from 'fs'; import { resolve, dirname } from 'path'; import { omit } from 'lodash'; -import { submitTypes } from '../../../shared/config/challenge-types'; +import { submitTypes } from '../../../shared-dist/config/challenge-types'; import { type ChallengeNode } from '../../../client/src/redux/prop-types'; -import { SuperBlocks } from '../../../shared/config/curriculum'; +import { SuperBlocks } from '../../../shared-dist/config/curriculum'; export type CurriculumIntros = { [keyValue in SuperBlocks]: { diff --git a/tools/scripts/build/build-external-curricula-data-v2.test.ts b/tools/scripts/build/build-external-curricula-data-v2.test.ts index 1f05e59045f..1a26487b32a 100644 --- a/tools/scripts/build/build-external-curricula-data-v2.test.ts +++ b/tools/scripts/build/build-external-curricula-data-v2.test.ts @@ -9,7 +9,7 @@ import { SuperBlocks, SuperBlockStage, superBlockStages -} from '../../../shared/config/curriculum'; +} from '../../../shared-dist/config/curriculum'; import { superblockSchemaValidator, availableSuperBlocksValidator diff --git a/tools/scripts/build/build-external-curricula-data-v2.ts b/tools/scripts/build/build-external-curricula-data-v2.ts index 72c866bb035..66f83b21b5a 100644 --- a/tools/scripts/build/build-external-curricula-data-v2.ts +++ b/tools/scripts/build/build-external-curricula-data-v2.ts @@ -1,10 +1,10 @@ import { mkdirSync, writeFileSync, readFileSync } from 'fs'; import { resolve, dirname } from 'path'; import { omit } from 'lodash'; -import { submitTypes } from '../../../shared/config/challenge-types'; +import { submitTypes } from '../../../shared-dist/config/challenge-types'; import { type ChallengeNode } from '../../../client/src/redux/prop-types'; -import { SuperBlocks } from '../../../shared/config/curriculum'; -import type { Chapter } from '../../../shared/config/chapters'; +import { SuperBlocks } from '../../../shared-dist/config/curriculum'; +import type { Chapter } from '../../../shared-dist/config/chapters'; import { getSuperblockStructure } from '../../../curriculum/build-curriculum'; export type CurriculumIntros = diff --git a/tools/scripts/build/external-data-schema-v1.js b/tools/scripts/build/external-data-schema-v1.js index 58f6ae9cee6..13a7997221c 100644 --- a/tools/scripts/build/external-data-schema-v1.js +++ b/tools/scripts/build/external-data-schema-v1.js @@ -1,7 +1,7 @@ const Joi = require('joi'); const { chapterBasedSuperBlocks -} = require('../../../shared/config/curriculum'); +} = require('../../../shared-dist/config/curriculum'); const blockSchema = Joi.object({}).keys({ desc: Joi.array().min(1), diff --git a/tools/scripts/build/external-data-schema-v2.js b/tools/scripts/build/external-data-schema-v2.js index 86df99d2398..b8328861a99 100644 --- a/tools/scripts/build/external-data-schema-v2.js +++ b/tools/scripts/build/external-data-schema-v2.js @@ -1,7 +1,7 @@ const Joi = require('joi'); const { chapterBasedSuperBlocks -} = require('../../../shared/config/curriculum'); +} = require('../../../shared-dist/config/curriculum'); const slugRE = new RegExp('^[a-z0-9-]+$');