freecodecamp-curriculum
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesefreeCodeCamp 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 testsfreeCodeCamp/
├── 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 or
nvm)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 freeCodeCampbash
git clone https://github.com/<YOUR_USERNAME>/freeCodeCamp.git
cd freeCodeCamp2. Install Dependencies
2. 安装依赖
bash
pnpm installbash
pnpm install3. Configure Environment
3. 配置环境变量
bash
cp sample.env .envKey variables to set:
.envbash
undefinedbash
cp sample.env .env需要设置的关键变量:
.envbash
undefinedMongoDB
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
undefinedSENDGRID_API_KEY=$SENDGRID_API_KEY
undefined4. Seed the Database
4. 初始化数据库
bash
pnpm run seedbash
pnpm run seed5. Start Development Servers
5. 启动开发服务器
bash
undefinedbash
undefinedStart 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
undefinedyaml
undefinedcurriculum/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 herejs
// 你的起始代码在这里--solutions--
--solutions--
js
// inline comment
/* multi-line
comment */undefinedjs
// 单行注释
/* 多行
注释 */undefinedChallenge Types
挑战类型
| Type | Value | Description |
|---|---|---|
| HTML | 0 | HTML/CSS challenges |
| JavaScript | 1 | JS algorithm challenges |
| JSX | 2 | React component challenges |
| Vanilla JS | 3 | DOM manipulation |
| Python | 7 | Python challenges |
| Project | 5 | Certification projects |
| Video | 11 | Video-based lessons |
| 类型 | 值 | 描述 |
|---|---|---|
| HTML | 0 | HTML/CSS挑战 |
| JavaScript | 1 | JS算法挑战 |
| JSX | 2 | React组件挑战 |
| 原生JS | 3 | DOM操作挑战 |
| Python | 7 | Python挑战 |
| 项目 | 5 | 认证项目 |
| 视频 | 11 | 视频课程 |
Creating a New Challenge
创建新挑战
Using the Helper Script
使用辅助脚本
bash
undefinedbash
undefinedCreate 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
undefinedcd tools/challenge-helper-scripts
pnpm run create-challenge --superblock responsive-web-design --block css-flexbox
undefinedManual Creation
手动创建
- Find the correct directory under
curriculum/challenges/english/ - Create a new file with a unique ID
.md
bash
undefined- 在下找到对应的目录
curriculum/challenges/english/ - 创建一个带有唯一ID的新文件
.md
bash
undefinedGenerate a unique challenge ID
生成唯一挑战ID
node -e "const {ObjectID} = require('mongodb'); console.log(new ObjectID().toString())"
3. Follow the challenge file format abovenode -e "const {ObjectID} = require('mongodb'); console.log(new ObjectID().toString())"
3. 遵循上述挑战文件格式Validate Your Challenge
验证你的挑战
bash
undefinedbash
undefinedLint 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 blocks:
# --hints--测试使用自定义断言库,在块中编写:
# --hints--JavaScript Challenges
JavaScript挑战
markdown
undefinedmarkdown
undefined--hints--
--hints--
myVariableletjs
assert.match(code, /let\s+myVariable/);The function should return when passed .
true42js
assert.strictEqual(myFunction(42), true);The DOM should contain an element with id .
mainjs
const el = document.getElementById('main');
assert.exists(el);undefinedmyVariableletjs
assert.match(code, /let\\s+myVariable/);当传入时,函数应返回。
42truejs
assert.strictEqual(myFunction(42), true);DOM中应包含ID为的元素。
mainjs
const el = document.getElementById('main');
assert.exists(el);undefinedAvailable 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
undefinedbash
undefinedRun 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
undefinedpnpm --filter client test -- --watch
undefinedCurriculum Tests
课程测试
bash
undefinedbash
undefinedValidate 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
undefinedpnpm run lint:curriculum
undefinedE2E Tests (Playwright)
端到端测试(Playwright)
bash
undefinedbash
undefinedRun 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
undefinedpnpm run test:e2e -- --ui
undefinedWriting 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
undefinedbash
undefinedDevelopment
开发
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 linking to forum.freecodecamp.org:
forumTopicIdyaml
forumTopicId: 301090 # Must be a real forum post ID每个挑战需要关联到forum.freecodecamp.org的对应帖子:
forumTopicIdyaml
forumTopicId: 301090 # 必须是真实的论坛帖子IDCurriculum Meta Files
课程元数据文件
Each block needs a :
_meta.jsonjson
{
"name": "Basic JavaScript",
"dashedName": "basic-javascript",
"order": 0,
"time": "5 hours",
"template": "",
"required": [],
"isUpcomingChange": false,
"isBeta": false,
"isLocked": false,
"isPrivate": false
}每个模块需要一个文件:
_meta.jsonjson
{
"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
undefinedbash
undefinedCheck 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
undefinedMONGOHQ_URL=mongodb://127.0.0.1:27017/freecodecamp-test pnpm test
undefinedPort Conflicts
端口冲突
bash
undefinedbash
undefinedAPI runs on 3000, Client on 8000
API运行在3000,客户端在8000
lsof -i :3000
kill -9 <PID>
undefinedlsof -i :3000
kill -9 <PID>
undefinedCurriculum Validation Failures
课程验证失败
bash
undefinedbash
undefinedSee 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前置格式错误
undefinedundefinedNode/pnpm Version Mismatch
Node/pnpm版本不匹配
bash
undefinedbash
undefinedUse 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
undefinednode --version # 应与.nvmrc一致
pnpm --version # 应与package.json中的packageManager一致
nvm use # 切换到正确的Node版本
undefinedClient Build Errors
客户端构建错误
bash
undefinedbash
undefinedClear Gatsby cache
清理Gatsby缓存
pnpm --filter client run clean
pnpm run develop:client
---pnpm --filter client run clean
pnpm run develop:client
---Contributing Workflow
贡献流程
bash
undefinedbash
undefined1. 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
资源
- Contribution guide: https://contribute.freecodecamp.org
- Forum: https://forum.freecodecamp.org
- Discord: https://discord.gg/PRyKn3Vbay
- How to report bugs: https://forum.freecodecamp.org/t/how-to-report-a-bug/19543