# freeCodeCamp China: npm test here checks challenge JSON, not the server

> FreeCodeCampChina/freecodecamp.cn is the code and Chinese curriculum behind the freeCodeCamp.cn site, published under three different licences and pinned at version 0.1.0. Its test script validates curriculum seed files, its install has no lockfile, and its last push was on 2023-07-16.

**FreeCodeCampChina/freecodecamp.cn** — FCC China open source codebase and curriculum. Learn to code and help nonprofits.

- Repository: https://github.com/FreeCodeCampChina/freecodecamp.cn
- Website: https://fcc.asia/
- Stars: 37,795 · Forks: 1,417
- Language: CSS
- License: NOASSERTION
- Published: 2026-08-17 · Updated: 2026-08-18 · Language: en
- Canonical page: https://hysenlabs.com/projects/freecodecampchina-freecodecamp-cn

## npm test lints JSON and runs a seed checker, never the application

The test wiring is short enough to read in one glance:

```json
"pretest": "npm run lint",
"test": "npm run test-challenges"
```

So npm test runs the linter first, then one script:

```json
"test-challenges": "babel-node seed/test-challenges.js | tap-spec"
```

That is a checker over the curriculum seed files piped into a TAP reporter, not a suite that exercises the Express application, the client, or any route. The linter it depends on is the other half of the picture, with lint set to run lint-js and lint-json, where lint-js is eslint across server/, common/, config/ and client/, and lint-json fans out into four separate jsonlint passes, one each over the server JSON, the challenge JSON under seed/challenges, the resources JSON and the utils JSON.

What a green run therefore proves is narrow: the JSON is parseable, the JavaScript passes the style rules, and whatever assertions seed/test-challenges.js makes about challenge data hold. What it does not prove is that the site starts, that a lesson renders, or that a user's submitted solution is graded correctly. For a fork, that is the difference between a repository that is cheap to modify and one where every behavioural change is unverifiable without opening a browser.

## No lockfile, Babel 6 and stage-0, and pins from the async 1.x era

The top level of the repository has no package-lock.json and no yarn.lock. Dependencies are declared with ranges in package.json, and several of those ranges are visibly old: babel-cli at ^6.3.17, babel-core at ^6.3.26, babel-preset-es2015, babel-preset-react and babel-preset-stage-0 all at ^6.3.13, async at ^1.5.0, cheerio at ~0.20.0 and adler32 at ~0.1.7. The .babelrc at the root is where those presets are wired.

Two consequences follow, and they are the ones that bite on a fresh clone. Without a committed lockfile, two people installing the same commit can resolve different dependency trees, because a caret range happily accepts a newer minor or major that was published later. And a tree that old pulls a large set of transitive packages at whatever versions are still resolvable today, which is precisely the shape of tree where an advisory shows up months after you installed it.

stage-0 is the other signal. It enables syntax that had not left proposals at the time, which tells you the codebase was written to compile experimental JavaScript through Babel 6 rather than to run on a modern Node. Practical upshot: budget an afternoon for dependency reconciliation before you touch application code, and treat the absence of a lockfile as something to fix in your fork on day one rather than something to inherit.

## The only-once script seeds the database, and its name is the warning

Before the server has anything to serve, the database needs seeding, and the repository does that through a script called only-once. Its name is the documentation. It chains create-rev, which writes an empty object into server/rev-manifest.json, and then runs node seed, with echo markers around both steps so you can see which half you are in.

A fresh clone cannot simply start. The order is the setup, and the README does not spell it out, because the README is aimed at campers using the hosted site rather than at people running the code. Everything the repository says about local operation lives in the npm scripts. sample.env is committed at the root, with the dot removed, so configuration is copied rather than authored, and the presence of full-test-data.js, dataAsync.js and leanTest.js at the root suggests several fixtures for seeding.

For a fork this is the part that decides your first day. Seeding is a one-time action, not an idempotent sync, and running it against an existing database is the kind of mistake that leaves you restoring from your own backup. The live deployment is running on a different footing: the production scripts are separate, and the site that the README says this code is running live at is freecodecamp.cn, with the community reachable through its own signin page.

## Production start runs bower install, then gulp, then pm2

Four distinct tools sit in the run path, and the manifest names them one at a time:

```json
"build": "NODE_ENV=production gulp build -p",
"start": "babel-node server/server.js",
"prestart-production": "bower cache clean && bower install && gulp build -p",
"start-production": "node pm2Start"
```

Read them as four stages. Development starts the server through babel-node, compiling on the fly. A production build sets NODE_ENV and calls gulp, and gulpfile.js is the task runner driving it. Webpack does the bundling, and there are two configurations for it, webpack.config.js and webpack.config.node.js, so client and server bundles are built separately. Process supervision is pm2, started through pm2Start.js.

The stage that deserves attention is prestart-production, because bower cache clean and bower install run at start time. Bower, the front-end package manager, is still in the critical path here, with bower.json and .bowerrc committed at the root, which means a production start fetches front-end dependencies over the network before the build can begin. That is a network dependency at boot, a cache clean that discards what was there, and a package manager the project has not replaced. If you are putting this behind a service, those three properties are the ones to design around.

## The license field drops the non-commercial term the README applies

The README splits the repository into three licensed parts. The computer software is under BSD-3-Clause, in LICENSE.md. The curricular content in ./seed/challenges or its subdirectories, along with the wiki, is under CC-BY-SA-4.0, in LICENSE-freeCodeCamp-Curriculum.md. The translation of the website is under CC-BY-NC-4.0, in LICENSE-freeCodeCamp-Translation.md, with the explicit instruction not to use it for commercial or business purposes. Copyright is stated as 2017 freeCodeCamp.

Now compare the manifest field, which reads:

```json
"license": "(BSD-3-Clause AND CC-BY-SA-4.0)"
```

The non-commercial term is missing. The expression names the software licence and the curriculum licence and stops, while the third file, the one governing the Chinese translation, is the part with an NC restriction on it. An automated licence scanner, a dependency audit or a compliance review that reads the SPDX field will conclude that the whole repository is BSD plus CC-BY-SA. It will not see the term that forbids commercial use of the translation.

That gap is the single most consequential detail in this repository for anyone planning to reuse it, and it is the kind that survives review precisely because the field looks well formed. The three LICENSE files at the root are the authority, and the field is a summary that happens to be incomplete.

## Filing a bug needs a forum thread and a third party who confirms it

The bug reporting process is a gate with three steps, stated as instructions not to file an issue until they are done. First, read the Help I've Found a Bug article on the forum and follow its instructions. Second, ask for confirmation in the appropriate Help Room. Third, and this is the one people skip, do not open an issue without a third party confirmation of your problem.

The friction is real and it is by design. An issue tracker that can only be entered with someone else's corroboration is a different kind of artefact from an open one, and the value is a low noise queue. The cost is that a reproducible bug in a private deployment has no path in, since the confirmation has to come from another person rather than from your own logs.

There is a second detail that matters for a regional fork. Those two forum links point at forum.freecodecamp.org, the upstream English forum, not at a Chinese one, and the second step names a Help Room rather than a local channel. So the process for the Chinese site routes through the upstream community's forum and chat in English. Contributing itself is more welcoming, with pull requests invited from campers and from seasoned JavaScript developers alike through CONTRIBUTING.md, but the bug path is the narrow one.

## seed/challenges is the product, and the fourth certification is the hard part

If you fork this project, the part that matters is the content, and the content is data. seed/ is where the curriculum lives, seed/challenges holds the challenge JSON, the four lint-json passes validate those files, and seed/test-challenges.js is the script that checks them. The application around it is the delivery mechanism. That reframing is useful, because a curriculum fork is a translation and editing job rather than a rewrite, and the licence split makes it a legally different job depending on which part you touch.

The four certifications show the shape of that content. Front End covers HTML, CSS, JavaScript, jQuery and Bootstrap, and asks for 10 front-end projects plus a set of JavaScript algorithms. Data Visualization adds Sass, React and D3, with 5 React apps and 5 D3.js data visualisation apps. Back End introduces Node.js, Express and MongoDB along with Git, and asks for 5 APIs and 5 full stack apps. Full Stack is the different one, because it pairs you with another camper, an agile project manager and a stakeholder from a nonprofit, and has you build two projects from scratch and then maintain and upgrade two existing ones.

That last certification is the part a code fork cannot automate. It depends on a human match and on access to a nonprofit that wants the software maintained, which is an organisational dependency rather than a technical one. One more detail in the third certification is worth knowing if you are counting on the curriculum to enforce a stack: solutions are accepted in any programming language, as long as a live demo and the source code are publicly accessible.

## Version 0.1.0, no releases, and a last push on 2023-07-16

The maintenance picture is unambiguous. The repository is not archived, but the last push was on 2023-07-16, and the version field in package.json has read 0.1.0 for the whole life of the project as far as the manifest shows. There are no GitHub releases, so there is no tag to check out, no changelog to diff, and no way to ask which version a running instance is on other than by looking at the commit.

The default branch is dev, not main, which is the freeCodeCamp convention and matters if you are cloning: a plain clone gives you the development branch. Continuous integration is configured through .travis.yml rather than a GitHub Actions workflow, another artefact of the era, alongside .editorconfig, .eslintrc, .eslintignore and .jshintrc at the root.

What this means in practice is that you are forking a snapshot rather than joining a project. The code that runs at freecodecamp.cn is what it is, and the last three-plus years of ecosystem movement, from the Node versions this Babel 6 toolchain supports to the dependency tree it resolves without a lockfile, have happened without the repository changing. Anyone planning to modernise it should treat 2023-07-16 as the baseline to diff against, not as a version to upgrade from.

## Conclusion

Adopt this repository if you want to run or translate the freeCodeCamp curriculum rather than build something new, and you are content to work from a commit with no tagged release behind it. Do not adopt it as a starting point for a fresh Node application: the dependency pins are Babel 6 era, there is no lockfile, and the test script will not tell you whether the server works. Before you build on it, read all three licence files rather than the license field in package.json, because that field omits the non-commercial term covering the Chinese translation, and confirm the curriculum JSON is what you actually intend to fork, since seed/challenges is where the real content sits and the code around it has been unchanged since 2023-07-16.

## FAQ

### Is freeCodeCamp totally free?

The project describes itself as a free open source community with a self-paced, browser-based full stack JavaScript curriculum, and the code, curriculum and translation are all published under open licences. The README states no cost for any part of the path, including the nonprofit projects in the fourth certification.

### Is a freeCodeCamp certification worth anything?

The repository states what each certification requires rather than what it is worth: 10 front-end projects, 5 React and 5 D3 apps, 5 APIs and 5 full stack apps, and for the fourth certification two projects built from scratch plus two existing ones maintained. It makes no claim about employer recognition or outcomes, and says nothing about how employers view the certificates.

### Is freeCodeCamp enough to get a job?

The stated aim is to help campers build job-worthy portfolios of real apps used by real people while helping nonprofits, and the Front End certification requires 10 projects and a set of JavaScript algorithms. The README does not offer any evidence about employment outcomes, so treat that aim as the project's framing rather than a measured result.

## Sources

- [Official documentation](https://fcc.asia/)
- [Official README](https://github.com/FreeCodeCampChina/freecodecamp.cn#readme)
- [Project repository](https://github.com/FreeCodeCampChina/freecodecamp.cn)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/freecodecampchina-freecodecamp-cn
