Without notes, state yesterday’s main idea and one unresolved question.
npm and package.json
TypeScript, Packages, and Tooling
Objective
Understand dependencies, scripts, and project metadata before installing libraries.
TypeScript is similar to design-rule checking: it catches invalid connections before runtime but cannot prove system behavior.
- package.json
- dependencies versus devDependencies
- npm scripts and lockfiles
Why this matters
Until now every line of code in this course was yours. From today you start using code other people wrote, and that means answering three questions honestly: what did I pull in, why is it here, and what exactly will land on a teammate's machine when they run one command? By the end of the hour you will have created a package, added a real tool to it, run it through a named script, and removed it again — and you will be able to explain every line of the file that describes it.
What npm actually is
npm is two things sharing a name, which is the source of most early confusion.
- The registry — a huge public archive of published JavaScript code at
npmjs.com. Each published unit is a package: someone else's code plus a file describing it. - The command-line tool — the
npmcommand, installed alongside Node.js on Day 2, that downloads packages from that registry into your project and keeps a record of what it did.
A dependency is a package your project needs in order to work. Nothing is magic here: npm install downloads files into a folder called node_modules inside your project and writes down
what it downloaded.
npm --version
11.13.0
package.json — the file that describes your project
package.json is a plain JSON file in the root of your project. It is the project's
description of itself: its name, what it depends on, and the commands you run on it. Nearly every
JavaScript tool you will ever use reads it.
Create one with npm init -y (-y means "accept every default without asking"):
{
"name": "pkg-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"type": "commonjs"
}
Field by field:
nameandversion— the package's identity.npm init -ytakes the name from the folder.versionfollows semantic versioning ("semver"):MAJOR.MINOR.PATCH, where a patch bump is a fix, a minor bump adds something backwards-compatible, and a major bump may break code that used the old version.description,keywords,author,license— metadata for humans and for the registry. Harmless to leave empty on a private project.main— which file is loaded when someone imports your package. Only matters if you publish.type—"module"means your.jsfiles use theimport/exportsyntax you learned on Day 23;"commonjs"means the olderrequire()style. It is a real behavioural switch, not metadata.scripts— named commands. More on these below.dependencies/devDependencies— what you installed. Absent until you install something.
`package.json` is the bill of materials
A BOM lists every part a board needs: designator, part number, quantity. It is not the board —
it is the document that lets someone else build the same board. package.json is that document
for your project. Delete node_modules and the parts are gone; keep package.json and anyone
can order them again.
Dependencies versus devDependencies
Two lists, one real distinction: is this package needed by the shipped program, or only by you while you work on it?
dependencies— needed at runtime. A date-formatting library your app calls while running belongs here. Install withnpm install <name>.devDependencies— needed only during development. Formatters, type checkers, test runners, build tools. Install withnpm install --save-dev <name>(short form:-D).
Both install identically on your machine. The difference matters when the code is deployed: a
production install can skip devDependencies, and a reader of your package.json can see at a
glance what is a shipping part and what is bench equipment.
Parts on the board vs. instruments on the bench
dependencies are the components soldered to the board — remove one and the product stops
working. devDependencies are the oscilloscope and the reflow oven: essential to building the
board, and not shipped inside the enclosure. Putting your scope in the BOM is not a crime, but
it tells the next engineer something false about the product.
npm scripts
The scripts field maps a short name to a shell command. You run one with npm run <name>.
"scripts": {
"format": "prettier --write .",
"check-format": "prettier --check ."
}
Two things make scripts worth using over typing the command yourself. First, they are documented
and shared — a new person runs npm run build without knowing what tool you chose. Second, npm
temporarily puts your project's node_modules/.bin on the command search path, so a script can
call a locally installed tool by bare name. That is why prettier --write . works inside a
script even though prettier is not installed system-wide.
npm run with no name lists what is available:
Scripts available in [email protected] via `npm run`:
dev
vite
build
tsc && vite build
preview
vite preview
Lockfiles
When you write "prettier": "^3.9.6" in package.json, the ^ is a range, not a version. It
means "3.9.6 or any later 3.x release". Install a month later and you may get 3.10.1. That is
useful for picking up fixes and terrible for reproducibility.
The lockfile — package-lock.json, written automatically by npm — closes that gap. It records
the exact resolved version of every package that was actually installed, including the
dependencies of your dependencies, plus the URL it came from and an integrity checksum:
"node_modules/prettier": {
"version": "3.9.6",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.6.tgz",
"integrity": "sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==",
"dev": true
}
That is the lockfile's whole job: resolved dependency versions. It holds no passwords, no
settings, no source code. Commit it to Git — it is what makes "works on my machine" reproducible.
npm ci installs strictly from it, which is what build servers use.
The recipe and the shopping receipt
package.json says "flour". The lockfile is the receipt showing you bought a specific 1kg bag
of a specific brand on a specific day. Hand someone the recipe and they may bake something
slightly different; hand them the receipt too and they can reproduce your loaf exactly.
Walkthrough
Work in a disposable folder — this one gets deleted at the end.
cd ~/fullstack-journey
mkdir pkg-demo
cd pkg-demo
npm init -y
npm prints the file it wrote. Now add one harmless development dependency. Prettier is a code formatter: it rewrites your files to a consistent style and changes nothing about behaviour.
npm install --save-dev prettier
added 1 package, and audited 2 packages in 2s
found 0 vulnerabilities
Three things changed on disk. Check each:
ls -A
node_modules package-lock.json package.json
package.json gained a devDependencies block containing "prettier": "^3.9.6".
package-lock.json appeared with the exact version. node_modules holds the downloaded code.
Confirm what npm thinks is installed:
npm ls
[email protected] /Users/you/fullstack-journey/pkg-demo
`-- [email protected]
Now make a deliberately messy file and add a script that checks formatting. Edit package.json so
scripts contains "check-format": "prettier --check .", then:
printf 'const a = 1\nconsole.log( a )\n' > demo.js
npm run check-format
Checking formatting...
[warn] demo.js
[warn] Code style issues found in the above file. Run Prettier with --write to fix.
Remember exit codes from Day 4 — this run exits non-zero because it found a problem. Fix it with
npx prettier --write demo.js, then re-run the script and watch it pass. Finally, remove the
dependency:
npm uninstall prettier
cat package.json
The devDependencies block is gone, and the lockfile has been rewritten to match.
Checkpoint
Say out loud which of the three changed things you would commit to Git (package.json and
package-lock.json) and which you would not (node_modules, because it is rebuildable from the
other two — and enormous).
How to use AI today
Today's mode is tutor. A tutor-style request asks the AI to explain the concept, give a small example, and then let you attempt it yourself. A request for the finished answer produces a file you cannot defend. Ask for the first kind.
Tutor mode, once your `package.json` exists
"Explain every field in this package.json. Separate standard fields from tool-specific fields." Then close the answer and re-explain three fields from memory before moving on.
Your turn
Make a fresh folder
~/fullstack-journey/pkg-demoand runnpm init -y. Read the whole printed file before doing anything else.Open
package.jsonin VS Code. Write a one-line comment for yourself in a separate notes file for each field, in your own words.Install Prettier as a development dependency:
npm install --save-dev prettier.Confirm all three effects:
devDependenciesinpackage.json, apackage-lock.jsonnaming version3.x.y, and anode_modulesfolder. Confirm withnpm ls.Add two scripts,
formatandcheck-format, as shown above. Runnpm runwith no arguments and check both appear.Create a badly formatted
.jsfile, runnpm run check-format(it should fail), runnpm run format, then run the check again (it should pass).Run
npm uninstall prettier. ConfirmdevDependenciesis gone frompackage.json.Run
npm run nopeand read the error, which names the exact fix:npm error Missing script: "nope" npm error To see a list of scripts, run: npm error npm run
You are done when
You can point at every line of your package.json and say what it does, and say what the
lockfile records that package.json does not.
Common pitfalls
- Committing
node_modules. It is thousands of files and fully rebuildable. Addnode_modulesto.gitignore; commitpackage.jsonandpackage-lock.json. - Deleting
package-lock.json"to fix things". You throw away the exact versions that worked. If you must reinstall, deletenode_modulesand runnpm ci, which reinstalls from the lockfile. - Assuming
^3.9.6means "3.9.6". It is a range. Only the lockfile pins the real version. - Installing globally by reflex.
npm install -gputs a tool on your whole machine and outside your project's record. Prefer a devDependency so the project describes its own needs.
Verify it yourself
Open today's reference, the npm Docs page About npm, and follow it to the pages on package.json
and on npm install.
- This lesson listed nine
package.jsonfields. Find one the docs describe that today's lesson did not mention, and write down what it does. - Find the docs' own statement about which files belong in source control. Does it agree with the
pitfall above about
node_modules?
Record both answers in ~/fullstack-journey/notes/day-29.md, then delete the pkg-demo folder.
The hour
- 0–5 min Recall
- 5–20 min Learn
Read only the listed concept notes and official reference sections needed today.
- 20–48 min Build
Create a fresh package, inspect package.json, add one harmless development dependency, and remove it again.
- 48–55 min Explain and verify
Run the result, inspect evidence, and explain the data/control flow in your own words.
- 55–60 min Quiz and commit
Complete the quiz, record one lesson, and commit the verified change when applicable.
What to hand in
A package.json whose scripts and dependencies you can explain.
Working with AI today
Ask for explanations, analogies, questions, and hints. Do not request a complete finished solution first.
Explain every field in this package.json. Separate standard fields from tool-specific fields.
References
End-of-day quiz
Explain-back gate
Pass the quiz above to unlock completion.
Quiz + explain-back checks required.