freecodecamp-curriculum

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

freeCodeCamp Curriculum & Platform Development

freeCodeCamp 课程与平台开发

Skill by ara.so — Daily 2026 Skills collection.
freeCodeCamp.org is a free, open-source learning platform with thousands of interactive coding challenges, certifications, and a full-stack curriculum. The codebase includes a React/TypeScript frontend, Node.js/Fastify backend, and a YAML/Markdown-based curriculum system.

技能来源:ara.so — 2026每日技能合集。
freeCodeCamp.org是一个免费的开源学习平台,拥有数千个交互式编程挑战、认证项目以及全栈课程。其代码库包含基于React/TypeScript的前端、Node.js/Fastify后端,以及基于YAML/Markdown的课程系统。

Architecture Overview

架构概述

freeCodeCamp/
├── api/                   # Fastify API server (TypeScript)
├── client/                # Gatsby/React frontend (TypeScript)
├── curriculum/            # All challenges and certifications (YAML/Markdown)
│   └── challenges/
│       ├── english/
│       │   ├── responsive-web-design/
│       │   ├── javascript-algorithms-and-data-structures/
│       │   └── ...
│       └── ...
├── tools/
│   ├── challenge-helper-scripts/  # CLI tools for curriculum authoring
│   └── ui-components/             # Shared React components
├── config/                # Shared configuration
└── e2e/                   # Playwright end-to-end tests

freeCodeCamp/
├── api/                   # Fastify API服务器(TypeScript)
├── client/                # Gatsby/React前端(TypeScript)
├── curriculum/            # 所有挑战与认证项目(YAML/Markdown)
│   └── challenges/
│       ├── english/
│       │   ├── responsive-web-design/
│       │   ├── javascript-algorithms-and-data-structures/
│       │   └── ...
│       └── ...
├── tools/
│   ├── challenge-helper-scripts/  # 课程编写用CLI工具
│   └── ui-components/             # 共享React组件
├── config/                # 共享配置
└── e2e/                   # Playwright端到端测试

Local Development Setup

本地开发环境搭建

Prerequisites

前置要求

  • Node.js 20+ (use
    nvm
    or
    fnm
    )
  • pnpm 9+
  • MongoDB (local or Atlas)
  • A GitHub account (for OAuth)
  • Node.js 20+(使用
    nvm
    fnm
    管理版本)
  • pnpm 9+
  • MongoDB(本地实例或Atlas云服务)
  • GitHub账号(用于OAuth认证)

1. Fork & Clone

1. Fork并克隆仓库

bash
git clone https://github.com/<YOUR_USERNAME>/freeCodeCamp.git
cd freeCodeCamp
bash
git clone https://github.com/<YOUR_USERNAME>/freeCodeCamp.git
cd freeCodeCamp

2. Install Dependencies

2. 安装依赖

bash
pnpm install
bash
pnpm install

3. Configure Environment

3. 配置环境变量

bash
cp sample.env .env
Key
.env
variables to set:
bash
undefined
bash
cp sample.env .env
需要设置的关键
.env
变量:
bash
undefined

MongoDB

MongoDB

MONGOHQ_URL=mongodb://127.0.0.1:27017/freecodecamp
MONGOHQ_URL=mongodb://127.0.0.1:27017/freecodecamp

GitHub OAuth (create at github.com/settings/developers)

GitHub OAuth(在github.com/settings/developers创建)

GITHUB_ID=$GITHUB_OAUTH_CLIENT_ID GITHUB_SECRET=$GITHUB_OAUTH_CLIENT_SECRET
GITHUB_ID=$GITHUB_OAUTH_CLIENT_ID GITHUB_SECRET=$GITHUB_OAUTH_CLIENT_SECRET

Auth

认证

JWT_SECRET=$YOUR_JWT_SECRET SESSION_SECRET=$YOUR_SESSION_SECRET
JWT_SECRET=$YOUR_JWT_SECRET SESSION_SECRET=$YOUR_SESSION_SECRET

Email (optional for local dev)

邮件(本地开发可选)

SENDGRID_API_KEY=$SENDGRID_API_KEY
undefined
SENDGRID_API_KEY=$SENDGRID_API_KEY
undefined

4. Seed the Database

4. 初始化数据库

bash
pnpm run seed
bash
pnpm run seed

5. Start Development Servers

5. 启动开发服务器

bash
undefined
bash
undefined

Start everything (API + Client)

启动所有服务(API + 客户端)

pnpm run develop
pnpm run develop

Or start individually:

或者单独启动:

pnpm run develop:api # Fastify API on :3000 pnpm run develop:client # Gatsby on :8000

---
pnpm run develop:api # Fastify API运行在端口:3000 pnpm run develop:client # Gatsby前端运行在端口:8000

---

Curriculum Challenge Structure

课程挑战结构

Challenges are stored as YAML/Markdown files under
curriculum/challenges/
.
挑战以YAML/Markdown文件形式存储在
curriculum/challenges/
目录下。

Challenge File Format

挑战文件格式

yaml
undefined
yaml
undefined

curriculum/challenges/english/02-javascript-algorithms-and-data-structures/basic-javascript/comment-your-javascript-code.md

curriculum/challenges/english/02-javascript-algorithms-and-data-structures/basic-javascript/comment-your-javascript-code.md


id: bd7123c8c441eddfaeb5bdef # unique MongoDB ObjectId-style string title: Comment Your JavaScript Code challengeType: 1 # 1=JS, 0=HTML, 2=JSX, 3=Vanilla JS, 5=Project, 7=Video forumTopicId: 16783 dashedName: comment-your-javascript-code


id: bd7123c8c441eddfaeb5bdef # 唯一的MongoDB ObjectId格式字符串 title: Comment Your JavaScript Code challengeType: 1 # 1=JS, 0=HTML, 2=JSX, 3=原生JS, 5=项目, 7=视频 forumTopicId: 16783 dashedName: comment-your-javascript-code

--description--

--description--

Comments are lines of code that JavaScript will intentionally ignore.
js
// This is an in-line comment.
/* This is a multi-line comment */
注释是JavaScript会主动忽略的代码行。
js
// 这是单行注释。
/* 这是多行注释 */

--instructions--

--instructions--

Try creating one of each type of comment.
尝试创建每种类型的注释各一个。

--hints--

--hints--

hint 1
js
assert(code.match(/(\/\/)/).length > 0);
hint 2
js
assert(code.match(/(\/\*[\s\S]+?\*\/)/).length > 0);
提示1
js
assert(code.match(/(\\/\\/)/).length > 0);
提示2
js
assert(code.match(/(\\/\\*[\\s\\S]+?\\*\\/)/).length > 0);

--seed--

--seed--

--seed-contents--

--seed-contents--

js
// Your starting code here
js
// 你的起始代码在这里

--solutions--

--solutions--

js
// inline comment
/* multi-line
   comment */
undefined
js
// 单行注释
/* 多行
   注释 */
undefined

Challenge Types

挑战类型

TypeValueDescription
HTML0HTML/CSS challenges
JavaScript1JS algorithm challenges
JSX2React component challenges
Vanilla JS3DOM manipulation
Python7Python challenges
Project5Certification projects
Video11Video-based lessons

类型描述
HTML0HTML/CSS挑战
JavaScript1JS算法挑战
JSX2React组件挑战
原生JS3DOM操作挑战
Python7Python挑战
项目5认证项目
视频11视频课程

Creating a New Challenge

创建新挑战

Using the Helper Script

使用辅助脚本

bash
undefined
bash
undefined

Create a new challenge interactively

交互式创建新挑战

pnpm run create-challenge
pnpm run create-challenge

Or use the helper directly

或者直接使用辅助工具

cd tools/challenge-helper-scripts pnpm run create-challenge --superblock responsive-web-design --block css-flexbox
undefined
cd tools/challenge-helper-scripts pnpm run create-challenge --superblock responsive-web-design --block css-flexbox
undefined

Manual Creation

手动创建

  1. Find the correct directory under
    curriculum/challenges/english/
  2. Create a new
    .md
    file with a unique ID
bash
undefined
  1. curriculum/challenges/english/
    下找到对应的目录
  2. 创建一个带有唯一ID的新
    .md
    文件
bash
undefined

Generate a unique challenge ID

生成唯一挑战ID

node -e "const {ObjectID} = require('mongodb'); console.log(new ObjectID().toString())"

3. Follow the challenge file format above
node -e "const {ObjectID} = require('mongodb'); console.log(new ObjectID().toString())"

3. 遵循上述挑战文件格式

Validate Your Challenge

验证你的挑战

bash
undefined
bash
undefined

Lint and validate all curriculum files

检查并验证所有课程文件

pnpm run test:curriculum
pnpm run test:curriculum

Test a specific challenge

测试单个挑战

pnpm run test:curriculum -- --challenge <challenge-id>
pnpm run test:curriculum -- --challenge <challenge-id>

Test a specific block

测试单个模块

pnpm run test:curriculum -- --block basic-javascript

---
pnpm run test:curriculum -- --block basic-javascript

---

Writing Challenge Tests

编写挑战测试

Tests use a custom assertion library. Inside
# --hints--
blocks:
测试使用自定义断言库,在
# --hints--
块中编写:

JavaScript Challenges

JavaScript挑战

markdown
undefined
markdown
undefined

--hints--

--hints--

myVariable
should be declared with
let
.
js
assert.match(code, /let\s+myVariable/);
The function should return
true
when passed
42
.
js
assert.strictEqual(myFunction(42), true);
The DOM should contain an element with id
main
.
js
const el = document.getElementById('main');
assert.exists(el);
undefined
myVariable
应该使用
let
声明。
js
assert.match(code, /let\\s+myVariable/);
当传入
42
时,函数应返回
true
js
assert.strictEqual(myFunction(42), true);
DOM中应包含ID为
main
的元素。
js
const el = document.getElementById('main');
assert.exists(el);
undefined

Available Test Utilities

可用的测试工具

js
// DOM access (for HTML challenges)
document.querySelector('#my-id')
document.getElementById('test')

// Code inspection
assert.match(code, /regex/);          // raw source code string
assert.include(code, 'someString');

// Value assertions (Chai-style)
assert.strictEqual(actual, expected);
assert.isTrue(value);
assert.exists(value);
assert.approximately(actual, expected, delta);

// For async challenges
// Use __helpers object
const result = await fetch('/api/test');
assert.strictEqual(result.status, 200);

js
// DOM访问(针对HTML挑战)
document.querySelector('#my-id')
document.getElementById('test')

// 代码检查
assert.match(code, /regex/);          // 原始源代码字符串
assert.include(code, 'someString');

// 值断言(Chai风格)
assert.strictEqual(actual, expected);
assert.isTrue(value);
assert.exists(value);
assert.approximately(actual, expected, delta);

// 异步挑战测试
// 使用__helpers对象
const result = await fetch('/api/test');
assert.strictEqual(result.status, 200);

API Development (Fastify)

API开发(Fastify)

Route Structure

路由结构

typescript
// api/src/routes/example.ts
import { type FastifyPluginCallbackTypebox } from '../helpers/plugin-callback-typebox';
import { Type } from '@fastify/type-provider-typebox';

export const exampleRoutes: FastifyPluginCallbackTypebox = (
  fastify,
  _options,
  done
) => {
  fastify.get(
    '/example/:id',
    {
      schema: {
        params: Type.Object({
          id: Type.String()
        }),
        response: {
          200: Type.Object({
            data: Type.String()
          })
        }
      }
    },
    async (req, reply) => {
      const { id } = req.params;
      return reply.send({ data: `Result for ${id}` });
    }
  );

  done();
};
typescript
// api/src/routes/example.ts
import { type FastifyPluginCallbackTypebox } from '../helpers/plugin-callback-typebox';
import { Type } from '@fastify/type-provider-typebox';

export const exampleRoutes: FastifyPluginCallbackTypebox = (
  fastify,
  _options,
  done
) => {
  fastify.get(
    '/example/:id',
    {
      schema: {
        params: Type.Object({
          id: Type.String()
        }),
        response: {
          200: Type.Object({
            data: Type.String()
          })
        }
      }
    },
    async (req, reply) => {
      const { id } = req.params;
      return reply.send({ data: `Result for ${id}` });
    }
  );

  done();
};

Adding a New API Route

添加新API路由

typescript
// api/src/app.ts - register the plugin
import { exampleRoutes } from './routes/example';

await fastify.register(exampleRoutes, { prefix: '/api' });
typescript
// api/src/app.ts - 注册插件
import { exampleRoutes } from './routes/example';

await fastify.register(exampleRoutes, { prefix: '/api' });

Database Access (Mongoose)

数据库访问(Mongoose)

typescript
// api/src/schemas/user.ts
import mongoose from 'mongoose';

const userSchema = new mongoose.Schema({
  email: { type: String, required: true, unique: true },
  completedChallenges: [
    {
      id: String,
      completedDate: Number,
      solution: String
    }
  ]
});

export const User = mongoose.model('User', userSchema);

typescript
// api/src/schemas/user.ts
import mongoose from 'mongoose';

const userSchema = new mongoose.Schema({
  email: { type: String, required: true, unique: true },
  completedChallenges: [
    {
      id: String,
      completedDate: Number,
      solution: String
    }
  ]
});

export const User = mongoose.model('User', userSchema);

Client (Gatsby/React) Development

客户端开发(Gatsby/React)

Adding a New Page

添加新页面

tsx
// client/src/pages/my-new-page.tsx
import React from 'react';
import { Helmet } from 'react-helmet';
import { useTranslation } from 'react-i18next';

const MyNewPage = (): JSX.Element => {
  const { t } = useTranslation();

  return (
    <>
      <Helmet>
        <title>{t('page-title.my-new-page')} | freeCodeCamp.org</title>
      </Helmet>
      <main>
        <h1>{t('headings.my-new-page')}</h1>
      </main>
    </>
  );
};

export default MyNewPage;
tsx
// client/src/pages/my-new-page.tsx
import React from 'react';
import { Helmet } from 'react-helmet';
import { useTranslation } from 'react-i18next';

const MyNewPage = (): JSX.Element => {
  const { t } = useTranslation();

  return (
    <>
      <Helmet>
        <title>{t('page-title.my-new-page')} | freeCodeCamp.org</title>
      </Helmet>
      <main>
        <h1>{t('headings.my-new-page')}</h1>
      </main>
    </>
  );
};

export default MyNewPage;

Using the Redux Store

使用Redux状态管理

tsx
// client/src/redux/selectors.ts pattern
import { createSelector } from 'reselect';
import { RootState } from './types';

export const userSelector = (state: RootState) => state.app.user;

export const completedChallengesSelector = createSelector(
  userSelector,
  user => user?.completedChallenges ?? []
);
tsx
// In a component
import { useAppSelector } from '../redux/hooks';
import { completedChallengesSelector } from '../redux/selectors';

const MyComponent = () => {
  const completedChallenges = useAppSelector(completedChallengesSelector);
  return <div>{completedChallenges.length} challenges completed</div>;
};
tsx
// client/src/redux/selectors.ts 模式
import { createSelector } from 'reselect';
import { RootState } from './types';

export const userSelector = (state: RootState) => state.app.user;

export const completedChallengesSelector = createSelector(
  userSelector,
  user => user?.completedChallenges ?? []
);
tsx
// 在组件中使用
import { useAppSelector } from '../redux/hooks';
import { completedChallengesSelector } from '../redux/selectors';

const MyComponent = () => {
  const completedChallenges = useAppSelector(completedChallengesSelector);
  return <div>已完成挑战数量:{completedChallenges.length}</div>;
};

i18n Translations

国际化翻译

tsx
// Add keys to client/i18n/locales/english/translations.json
{
  "my-component": {
    "title": "My Title",
    "description": "My description with {{variable}}"
  }
}

// Use in component
const { t } = useTranslation();
t('my-component.title');
t('my-component.description', { variable: 'value' });

tsx
// 在client/i18n/locales/english/translations.json中添加键值对
{
  "my-component": {
    "title": "我的标题",
    "description": "我的描述,包含变量{{variable}}"
  }
}

// 在组件中使用
const { t } = useTranslation();
t('my-component.title');
t('my-component.description', { variable: '具体值' });

Testing

测试

Unit Tests (Jest)

单元测试(Jest)

bash
undefined
bash
undefined

Run all unit tests

运行所有单元测试

pnpm test
pnpm test

Run tests for a specific package

运行指定包的测试

pnpm --filter api test pnpm --filter client test
pnpm --filter api test pnpm --filter client test

Watch mode

监听模式

pnpm --filter client test -- --watch
undefined
pnpm --filter client test -- --watch
undefined

Curriculum Tests

课程测试

bash
undefined
bash
undefined

Validate all challenges

验证所有挑战

pnpm run test:curriculum
pnpm run test:curriculum

Validate specific superblock

验证指定超级模块

pnpm run test:curriculum -- --superblock javascript-algorithms-and-data-structures
pnpm run test:curriculum -- --superblock javascript-algorithms-and-data-structures

Lint challenge markdown

检查课程Markdown格式

pnpm run lint:curriculum
undefined
pnpm run lint:curriculum
undefined

E2E Tests (Playwright)

端到端测试(Playwright)

bash
undefined
bash
undefined

Run all e2e tests

运行所有端到端测试

pnpm run test:e2e
pnpm run test:e2e

Run specific test file

运行指定测试文件

pnpm run test:e2e -- e2e/learn.spec.ts
pnpm run test:e2e -- e2e/learn.spec.ts

Run with UI

带UI界面运行

pnpm run test:e2e -- --ui
undefined
pnpm run test:e2e -- --ui
undefined

Writing E2E Tests

编写端到端测试

typescript
// e2e/my-feature.spec.ts
import { test, expect } from '@playwright/test';

test('user can complete a challenge', async ({ page }) => {
  await page.goto('/learn/javascript-algorithms-and-data-structures/basic-javascript/comment-your-javascript-code');

  // Fill in the code editor
  await page.locator('.monaco-editor').click();
  await page.keyboard.type('// inline comment\n/* block comment */');

  // Run tests
  await page.getByRole('button', { name: /run the tests/i }).click();

  // Check results
  await expect(page.getByText('Tests Passed')).toBeVisible();
});

typescript
// e2e/my-feature.spec.ts
import { test, expect } from '@playwright/test';

test('用户可以完成挑战', async ({ page }) => {
  await page.goto('/learn/javascript-algorithms-and-data-structures/basic-javascript/comment-your-javascript-code');

  // 填写代码编辑器
  await page.locator('.monaco-editor').click();
  await page.keyboard.type('// 单行注释\
/* 多行注释 */');

  // 运行测试
  await page.getByRole('button', { name: /运行测试/i }).click();

  // 检查结果
  await expect(page.getByText('测试通过')).toBeVisible();
});

Key pnpm Scripts Reference

核心pnpm脚本参考

bash
undefined
bash
undefined

Development

开发

pnpm run develop # Start all services pnpm run develop:api # API only pnpm run develop:client # Client only
pnpm run develop # 启动所有服务 pnpm run develop:api # 仅启动API pnpm run develop:client # 仅启动客户端

Building

构建

pnpm run build # Build everything pnpm run build:api # Build API pnpm run build:client # Build client (Gatsby)
pnpm run build # 构建所有内容 pnpm run build:api # 构建API pnpm run build:client # 构建客户端(Gatsby)

Testing

测试

pnpm test # Unit tests pnpm run test:curriculum # Validate curriculum pnpm run test:e2e # Playwright e2e
pnpm test # 单元测试 pnpm run test:curriculum # 验证课程 pnpm run test:e2e # Playwright端到端测试

Linting

代码检查

pnpm run lint # ESLint all packages pnpm run lint:curriculum # Curriculum markdown lint
pnpm run lint # 检查所有包的ESLint规则 pnpm run lint:curriculum # 检查课程Markdown格式

Database

数据库

pnpm run seed # Seed DB with curriculum data pnpm run seed:certified-user # Seed a test certified user
pnpm run seed # 用课程数据初始化数据库 pnpm run seed:certified-user # 初始化一个已认证的测试用户

Utilities

工具

pnpm run create-challenge # Interactive challenge creator pnpm run clean # Clean build artifacts

---
pnpm run create-challenge # 交互式创建挑战 pnpm run clean # 清理构建产物

---

Superblock & Block Naming Conventions

超级模块与模块命名规范

Superblocks map to certifications. Directory names use kebab-case:
responsive-web-design/
javascript-algorithms-and-data-structures/
front-end-development-libraries/
data-visualization/
relational-database/
back-end-development-and-apis/
quality-assurance/
scientific-computing-with-python/
data-analysis-with-python/
machine-learning-with-python/
coding-interview-prep/
the-odin-project/
project-euler/
Block directories within a superblock:
responsive-web-design/
├── basic-html-and-html5/
├── basic-css/
├── applied-visual-design/
├── css-flexbox/
└── css-grid/

超级模块对应认证项目,目录名称使用短横线分隔命名(kebab-case):
responsive-web-design/
javascript-algorithms-and-data-structures/
front-end-development-libraries/
data-visualization/
relational-database/
back-end-development-and-apis/
quality-assurance/
scientific-computing-with-python/
data-analysis-with-python/
machine-learning-with-python/
coding-interview-prep/
the-odin-project/
project-euler/
超级模块下的模块目录:
responsive-web-design/
├── basic-html-and-html5/
├── basic-css/
├── applied-visual-design/
├── css-flexbox/
└── css-grid/

Common Patterns & Gotchas

常见模式与注意事项

Challenge ID Generation

挑战ID生成

Every challenge needs a unique 24-character hex ID:
typescript
// tools/challenge-helper-scripts/helpers/id-gen.ts
import { ObjectId } from 'bson';
export const generateId = (): string => new ObjectId().toHexString();
每个挑战需要一个唯一的24位十六进制ID:
typescript
// tools/challenge-helper-scripts/helpers/id-gen.ts
import { ObjectId } from 'bson';
export const generateId = (): string => new ObjectId().toHexString();

Adding Forum Links

添加论坛链接

Every challenge needs a
forumTopicId
linking to forum.freecodecamp.org:
yaml
forumTopicId: 301090  # Must be a real forum post ID
每个挑战需要
forumTopicId
关联到forum.freecodecamp.org的对应帖子:
yaml
forumTopicId: 301090  # 必须是真实的论坛帖子ID

Curriculum Meta Files

课程元数据文件

Each block needs a
_meta.json
:
json
{
  "name": "Basic JavaScript",
  "dashedName": "basic-javascript",
  "order": 0,
  "time": "5 hours",
  "template": "",
  "required": [],
  "isUpcomingChange": false,
  "isBeta": false,
  "isLocked": false,
  "isPrivate": false
}
每个模块需要一个
_meta.json
文件:
json
{
  "name": "基础JavaScript",
  "dashedName": "basic-javascript",
  "order": 0,
  "time": "5小时",
  "template": "",
  "required": [],
  "isUpcomingChange": false,
  "isBeta": false,
  "isLocked": false,
  "isPrivate": false
}

Testing with Authentication

带认证的测试

typescript
// In e2e tests, use the test user fixture
import { authedUser } from './fixtures/authed-user';

test.use({ storageState: 'playwright/.auth/user.json' });

test('authenticated action', async ({ page }) => {
  // page is already logged in
  await page.goto('/settings');
  await expect(page.getByText('Account Settings')).toBeVisible();
});

typescript
// 在端到端测试中,使用测试用户夹具
import { authedUser } from './fixtures/authed-user';

test.use({ storageState: 'playwright/.auth/user.json' });

test('已认证用户操作', async ({ page }) => {
  // 页面已处于登录状态
  await page.goto('/settings');
  await expect(page.getByText('账户设置')).toBeVisible();
});

Troubleshooting

故障排除

MongoDB Connection Issues

MongoDB连接问题

bash
undefined
bash
undefined

Check if MongoDB is running

检查MongoDB是否运行

mongosh --eval "db.adminCommand('ping')"
mongosh --eval "db.adminCommand('ping')"

Start MongoDB (macOS with Homebrew)

启动MongoDB(macOS使用Homebrew)

brew services start mongodb-community
brew services start mongodb-community

Use in-memory MongoDB for tests

测试时使用内存MongoDB

MONGOHQ_URL=mongodb://127.0.0.1:27017/freecodecamp-test pnpm test
undefined
MONGOHQ_URL=mongodb://127.0.0.1:27017/freecodecamp-test pnpm test
undefined

Port Conflicts

端口冲突

bash
undefined
bash
undefined

API runs on 3000, Client on 8000

API运行在3000,客户端在8000

lsof -i :3000 kill -9 <PID>
undefined
lsof -i :3000 kill -9 <PID>
undefined

Curriculum Validation Failures

课程验证失败

bash
undefined
bash
undefined

See detailed error output

查看详细错误输出

pnpm run test:curriculum -- --verbose
pnpm run test:curriculum -- --verbose

Common issues:

常见问题:

- Missing forumTopicId

- 缺少forumTopicId

- Duplicate challenge IDs

- 重复的挑战ID

- Invalid challengeType

- 无效的challengeType

- Malformed YAML frontmatter

- YAML前置格式错误

undefined
undefined

Node/pnpm Version Mismatch

Node/pnpm版本不匹配

bash
undefined
bash
undefined

Use the project's required versions

使用项目要求的版本

node --version # Should match .nvmrc pnpm --version # Should match packageManager in package.json
nvm use # Switches to correct Node version
undefined
node --version # 应与.nvmrc一致 pnpm --version # 应与package.json中的packageManager一致
nvm use # 切换到正确的Node版本
undefined

Client Build Errors

客户端构建错误

bash
undefined
bash
undefined

Clear Gatsby cache

清理Gatsby缓存

pnpm --filter client run clean pnpm run develop:client

---
pnpm --filter client run clean pnpm run develop:client

---

Contributing Workflow

贡献流程

bash
undefined
bash
undefined

1. Create a feature branch

1. 创建功能分支

git checkout -b fix/challenge-typo-in-basic-js
git checkout -b fix/challenge-typo-in-basic-js

2. Make changes and test

2. 修改代码并测试

pnpm run test:curriculum pnpm test
pnpm run test:curriculum pnpm test

3. Lint

3. 代码检查

pnpm run lint
pnpm run lint

4. Commit using conventional commits

4. 使用规范提交信息提交

git commit -m "fix(curriculum): correct typo in basic-javascript challenge"
git commit -m "fix(curriculum): 修正基础JavaScript挑战中的拼写错误"

5. Push and open PR against main

5. 推送并向主分支发起PR

git push origin fix/challenge-typo-in-basic-js

Commit message prefixes: `fix:`, `feat:`, `chore:`, `docs:`, `refactor:`, `test:`

---
git push origin fix/challenge-typo-in-basic-js

提交信息前缀:`fix:`, `feat:`, `chore:`, `docs:`, `refactor:`, `test:`

---

Resources

资源