# Package managers

npm, Composer, pip en NuGet: code van anderen in je project, met de juiste versie. En waarom node_modules nooit op git komt.

Multimedia & Creatieve Technologie · 2.1 Programming principles · Les 3 · Package managers · versie 1.0.0 · 2026-10-04

Tags: package-managers, npm, composer, python, git

Slides: <https://slides.mctantwerp.be/nl/programming-principles/04-package-managers>

Speaker notes: <https://slides.mctantwerp.be/nl/programming-principles/04-package-managers.notes.md>

<!-- slide 2 -->

## Agenda

1. Wat een package manager is
2. Manager, registry en lock file: package.json, ^ en ~
3. npm
4. Composer
5. Python
6. C# en Unity
7. Heel belangrijk
8. Opdracht

<!-- slide 3 -->

## 1. Wat een package manager is

<!-- slide 4 -->

### Software installeren, 2004

![Het venster Add or Remove Programs van Windows XP, met een lijst geïnstalleerde programma's](https://slides.mctantwerp.be/decks/nl/programming-principles/04-package-managers/add-remove-programs.jpg)

<!-- slide 5 -->

### Of zo

![De winkel van Steam in de browser](https://slides.mctantwerp.be/decks/nl/programming-principles/04-package-managers/steam.jpg)

<!-- slide 6 -->

### Eén commando, en het staat erop

- **[Windows](https://learn.microsoft.com/en-us/windows/package-manager/winget/)**: `winget`, standaard in Windows 11. Of Chocolatey en Scoop.
- **[macOS](https://brew.sh/)**: `brew`, Homebrew. De App Store voor alles met een venster.
- **Linux**: `apt` op Ubuntu en Debian, `dnf` op Fedora, `pacman` op Arch.
- **Stores**: Steam, App Store, Play Store. Dezelfde gedachte, met een winkel errond.

```console
$ winget install Git.Git
Found Git [Git.Git] Version 2.51.0
Successfully installed
$ brew install git
==> Downloading https://ghcr.io/v2/homebrew/core/git/...
==> Pouring git--2.51.0.arm64_sequoia.bottle.tar.gz
$ brew upgrade
Alles wat via brew kwam, in één keer naar de laatste versie
```

<!-- slide 7 -->

**Hetzelfde, maar voor code.**

Een library die iemand anders schreef, met één commando in jouw project. Met de juiste versie, en met alles wat die library zelf nodig heeft.

<!-- slide 8 -->

### Een e-mailadres valideren

*index.html*

```html
<script type="module">
  import validator from 'https://esm.sh/email-validator@2'

  console.log(validator.validate('test@test.be'))
  console.log(validator.validate('bladiblabla'))
  console.log(validator.validate('jan..peeters@kdg.be'))
  console.log(validator.validate('@@@'))
</script>
```

*Met een package*

```html
<script type="module">
  import validator from 'https://esm.sh/email-validator@2'

  console.log(validator.validate('test@test.be'))
  console.log(validator.validate('bladiblabla'))
  console.log(validator.validate('jan..peeters@kdg.be'))
  console.log(validator.validate('@@@'))
</script>
```

*Zelf geschreven*

```html
<script>
  const validate = (email) => email.includes('@')

  console.log(validate('test@test.be'))
  console.log(validate('bladiblabla'))
  console.log(validate('jan..peeters@kdg.be'))
  console.log(validate('@@@'))
</script>
```

<!-- slide 9 -->

### Waarom je packages gebruikt

- Je vindt het warm water niet opnieuw uit. Voor bijna alles bestaat er al iets.
- Meer tijd voor wat jouw project uniek maakt, minder voor datumnotaties en e-mailadressen
- Een populair package is getest door duizenden projecten. De bugs die jij nog zou maken, zijn er al uit.
- Een update van het package is een fix voor jou, zonder dat je een regel aanraakt

<!-- slide 10 -->

### Wees kritisch

*left-pad (2016, ingekort)*

```js
module.exports = leftpad

function leftpad (str, len, ch) {
  str = String(str)
  var i = -1
  if (!ch && ch !== 0) ch = ' '
  len = len - str.length
  while (++i < len) {
    str = ch + str
  }
  return str
}
```

Elf regels. Toen de auteur ze van npm haalde, vielen de builds van Babel en React om, over de hele wereld.

> [!WARNING]
> Elk package is een beslissing van iemand anders, in jouw project. Iets van vijf regels schrijf je beter zelf.

[kik, left-pad, and npm](https://blog.npmjs.org/post/141577284765/kik-left-pad-and-npm)

<!-- slide 11 -->

### Het gaat ook mis

- **[chalk en debug](https://www.aikido.dev/blog/npm-debug-and-chalk-packages-compromised)**: September 2025. Een maintainer trapt in een phishingmail, en achttien packages met samen miljarden downloads per week krijgen een versie met malware. Na een paar uur haalt npm ze weg.
- **[Shai-Hulud](https://github.blog/security/supply-chain-security/our-plan-for-a-more-secure-npm-supply-chain/)**: Een week later. Een worm die bij elke installatie tokens steelt en zichzelf in de andere packages van dat account publiceert. GitHub haalt meer dan 500 packages offline.

> [!NOTE]
> **Supply chain attack**
>
> Niemand valt jouw code aan. Ze vallen de code aan die jij installeert, en die draait met dezelfde rechten als jij.

<!-- slide 12 -->

### Voor je installeert

- Downloads per week en de datum van de laatste release. Drie jaar stil is een slecht teken.
- Open issues en of iemand ze beantwoordt
- De naam, letter per letter. expresss is niet express, en dat is met opzet.
- Hoeveel dependencies het zelf meebrengt
- De license, als je project ooit iets moet opbrengen

<!-- slide 13 -->

### Waarom je er zelf eentje maakt

- Je geeft iets terug aan de community waar je zelf zoveel van gebruikt
- Vind jij het handig, dan vindt iemand anders het ook handig
- Je krijgt feedback op je code van mensen die je niet kent
- Een package met echte gebruikers staat goed in je portfolio

<!-- slide 14 -->

## 2. Manager, registry en lock file

<!-- slide 15 -->

### Manager en registry

- **Package manager**: De tool op je laptop. Je typt een commando, hij zoekt het package, kiest een versie en zet het in je project. `npm`, `composer`, `pip`.
- **Registry**: De online catalogus met alle packages en al hun versies. Iedereen kan er publiceren. npmjs.com, packagist.org, pypi.org.

<!-- slide 16 -->

### Manifest en lock file

#### package.json

```json
{
  "dependencies": {
    "email-validator": "^2.0.4"
  }
}
```

Wat je project nodig heeft, met een range. Kort, en je leest het zelf.

#### package-lock.json

```json
"node_modules/email-validator": {
  "version": "2.0.4",
  "resolved": "https://registry.npmjs.org/...",
  "integrity": "sha512-V1u8..."
}
```

Exact wat er geïnstalleerd is, ook de dependencies van je dependencies. npm schrijft hem, jij niet.

<!-- slide 17 -->

### Per taal

| Taal | Manager | Registry | Manifest | Lock file | Map |
| --- | --- | --- | --- | --- | --- |
| JavaScript | npm, pnpm, Yarn | [npmjs.com](https://www.npmjs.com) | `package.json` | `package-lock.json` | `node_modules/` |
| PHP | Composer | [packagist.org](https://packagist.org) | `composer.json` | `composer.lock` | `vendor/` |
| Python | pip, uv | [pypi.org](https://pypi.org) | `requirements.txt` | `uv.lock` | `.venv/` |
| C# | NuGet | [nuget.org](https://www.nuget.org) | `*.csproj` | `packages.lock.json` | `bin/ obj/` |
| Ruby | Bundler | [rubygems.org](https://rubygems.org) | `Gemfile` | `Gemfile.lock` | `vendor/bundle/` |

<!-- slide 18 -->

### Versies in package.json

- **^2.0.4**: Vanaf 2.0.4, alles onder 3.0.0. Nieuwe minors en patches, geen nieuwe major. De standaard.
- **~2.0.4**: Vanaf 2.0.4, alles onder 2.1.0. Alleen patches.
- **2.0.4**: Precies die versie, en niets anders. Ook geen security fix.

> [!TIP]
> Onder 1.0.0 gelden andere regels: daar is elke minor een breaking change, dus `^0.3.1` blijft binnen 0.3.

<!-- slide 19 -->

### Welke versie krijg je

Een veld voor de versie van email-validator in package.json, met de knoppen ^2.0.4, ~2.0.4, 2.0.4, >=2.0.0, ^0.3.1, *. Daaronder de versies 0.3.1, 0.3.2, 0.4.0, 1.9.0, 2.0.0, 2.0.4, 2.0.5, 2.1.0, 2.4.1, 3.0.0: wat de range toelaat licht op, de hoogste daarvan is wat npm install kiest.

<!-- slide 20 -->

### In package.json staat `"^4.17.0"`. Er bestaan 4.17.21, 4.18.0 en 5.0.0. Je hebt geen lock file. Wat installeert npm?

- A. 4.17.0
- B. 4.17.21
- C. 4.18.0
- D. 5.0.0

**Antwoord:** C. 4.18.0

De caret laat alles toe onder de volgende major, dus onder 5.0.0. npm neemt de hoogste versie die past: 4.18.0. Met een lock file had je gekregen wat daarin stond, wat er ondertussen ook uitgekomen is.

<!-- slide 21 -->

## 3. npm

<!-- slide 22 -->

### Node.js en npm

```console
$ node -v
v24.9.0
$ npm -v
11.6.0
```

- npm komt mee met [Node.js](https://nodejs.org). Kies de LTS versie.
- Zonder Node geen npm, en ook geen `node test.js`
- Je versie hoeft niet gelijk te zijn aan de mijne. Een LTS versie die nog ondersteund wordt, is goed.

<!-- slide 23 -->

### Wat LTS betekent

- **Current**: De nieuwste major, met de nieuwste features. Het eerste halfjaar verandert er nog van alles, en niet elk package werkt er al mee. Om mee te spelen.
- **LTS, Long Term Support**: Een versie die stilstaat: geen nieuwe features meer, wel bugfixes en security fixes, 30 maanden lang. Voor je projecten, op school en in productie.

> [!NOTE]
> Tot Node 26 werden alleen de even versies LTS: 20, 22, 24. Vanaf Node 27 komt er één major per jaar, en wordt elke versie LTS.

Welke versie nu LTS is, en tot wanneer: [nodejs.org, Node.js Releases](https://nodejs.org/en/about/previous-releases)

<!-- slide 24 -->

### De release cycle van Ubuntu

Tijdlijn van de releases: 22.04 LTS (2022-04 tot 2027-04, met ubuntu pro tot 2032-04), 22.10 (2022-10 tot 2023-07), 23.04 (2023-04 tot 2024-01), 23.10 (2023-10 tot 2024-07), 24.04 LTS (2024-04 tot 2029-04, met ubuntu pro tot 2034-04), 24.10 (2024-10 tot 2025-07), 25.04 (2025-04 tot 2026-01), 25.10 (2025-10 tot 2026-07), 26.04 LTS (2026-04 tot 2031-04, met ubuntu pro tot 2036-04).

<!-- slide 25 -->

### De commando's

```bash
# Een nieuw project starten, maakt package.json
npm init -y

# Alles uit package.json installeren, na een clone
npm install

# Een package toevoegen
npm install email-validator

# Een package verwijderen
npm uninstall email-validator

# Updaten binnen de ranges van package.json
npm update
```

<!-- slide 26 -->

### Wat npm install doet

```console
$ npm init -y
Wrote to ~/Projects/email-test/package.json
$ npm install email-validator
added 1 package, and audited 2 packages in 512ms
found 0 vulnerabilities
$ ls
node_modules  package-lock.json  package.json
$ cat package.json
{
  "name": "email-test",
  "dependencies": {
   "email-validator": "^2.0.4"
  }
}
```

<!-- slide 27 -->

### npm gebruiken

*test.js*

```js
const validator = require('email-validator')

console.log(validator.validate('test@test.be'))
console.log(validator.validate('bladiblabla'))
```

```console
$ node test.js
true
false
```

<!-- slide 28 -->

### Weg is niet weg

```console
$ rm -rf node_modules
$ node test.js
Error: Cannot find module 'email-validator'
Require stack:
- ~/Projects/email-test/test.js
$ npm install
added 1 package, and audited 2 packages in 340ms
found 0 vulnerabilities
$ node test.js
true
false
```

> [!TIP]
> `package.json` en `package-lock.json` zijn genoeg om de hele map terug te halen.

<!-- slide 29 -->

### dependencies en devDependencies

*package.json*

```json
{
  "dependencies": {
    "email-validator": "^2.0.4"
  },
  "devDependencies": {
    "vite": "^8.3.0"
  }
}
```

- dependencies: wat je code nodig heeft om te draaien
- devDependencies: wat jij nodig hebt tijdens het bouwen. Vite, een linter, je tests. `npm install -D vite`
- Composer noemt het `require` en `require-dev`, met `composer require --dev`

<!-- slide 30 -->

### npm run

*package.json*

```json
{
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "test": "node test.js"
  }
}
```

```console
$ npm run dev
> vite
 ➜  Local:   http://localhost:5173/
$ npx cowsay hallo
Draait het package één keer, zonder het te installeren
```

Wie je project cloont, hoeft niet te weten welke tool erachter zit. `npm run dev` is genoeg.

<!-- slide 31 -->

### npm, pnpm, Yarn

|   | npm | pnpm | Yarn |
| --- | --- | --- | --- |
| Starten | `npm init` | `pnpm init` | `yarn init` |
| Alles installeren | `npm install` | `pnpm install` | `yarn` |
| Toevoegen | `npm install vite -D` | `pnpm add vite -D` | `yarn add vite -D` |
| Verwijderen | `npm uninstall vite` | `pnpm remove vite` | `yarn remove vite` |
| Script | `npm run dev` | `pnpm dev` | `yarn dev` |
| Exact de lock file | `npm ci` | `pnpm install --frozen-lockfile` | `yarn install --immutable` |
| Lock file | `package-lock.json` | `pnpm-lock.yaml` | `yarn.lock` |

<!-- slide 32 -->

### Onderhoud

```console
$ npm outdated
Package   Current  Wanted  Latest
vite      7.1.2    7.3.0   8.3.0
chart.js  4.4.0    4.5.1   4.5.1
$ npm update
changed 2 packages in 3s
$ npm audit
1 high severity vulnerability
To address all issues, run:
 npm audit fix
$ npm audit fix
found 0 vulnerabilities
```

<!-- slide 33 -->

## 4. Composer

<!-- slide 34 -->

### Composer

|   | npm | Composer |
| --- | --- | --- |
| Starten | `npm init` | `composer init` |
| Alles installeren | `npm install` | `composer install` |
| Toevoegen | `npm install x` | `composer require x` |
| Verwijderen | `npm uninstall x` | `composer remove x` |
| Updaten | `npm update` | `composer update` |

- Packages komen van [packagist.org](https://packagist.org) en heten `vendor/naam`
- De map heet `vendor/`, de lock file `composer.lock`
- PHP moet geïnstalleerd zijn. [Herd](https://herd.laravel.com) geeft je PHP en Composer samen.

<!-- slide 35 -->

### Een package toevoegen

```console
$ composer require egulias/email-validator
./composer.json has been created
Lock file operations: 3 installs, 0 updates, 0 removals
  - Locking doctrine/lexer (3.0.1)
  - Locking egulias/email-validator (4.0.4)
  - Locking symfony/polyfill-intl-idn (v1.33.0)
Generating autoload files
$ ls
composer.json  composer.lock  vendor
```

Eén package gevraagd, drie gekregen. `egulias/email-validator` heeft er zelf twee nodig.

<!-- slide 36 -->

### Composer gebruiken

*test.php*

```php
<?php

require 'vendor/autoload.php';

use Egulias\EmailValidator\EmailValidator;
use Egulias\EmailValidator\Validation\RFCValidation;

$validator = new EmailValidator();
$rfc = new RFCValidation();

var_dump($validator->isValid('test@test.be', $rfc));
var_dump($validator->isValid('bladiblabla', $rfc));
```

```console
$ php test.php
bool(true)
bool(false)
```

<!-- slide 37 -->

### Weg is nog altijd niet weg

```console
$ rm -rf vendor
$ php test.php
PHP Warning:  require(vendor/autoload.php): Failed to open stream: No such file or directory
PHP Fatal error:  Uncaught Error: Failed opening required 'vendor/autoload.php'
$ composer install
Installing dependencies from lock file (including require-dev)
Package operations: 3 installs, 0 updates, 0 removals
Generating autoload files
$ php test.php
bool(true)
bool(false)
```

<!-- slide 38 -->

## 5. Python

<!-- slide 39 -->

### pip weigert

```console
$ pip3 install email-validator
error: externally-managed-environment

× This environment is externally managed
╰─> To install Python packages system-wide, try brew install
    xyz, where xyz is the package you are trying to
    install.

    If you wish to install a Python library that isn’t in Homebrew,
    use a virtual environment:

   python3 -m venv path/to/venv
   source path/to/venv/bin/activate
```

<!-- slide 40 -->

### Een virtual environment

```console
$ python3 -m venv .venv
$ source .venv/bin/activate
$ pip install email-validator
Successfully installed dnspython-2.8.0 email-validator-2.3.0 idna-3.10
$ pip freeze > requirements.txt
$ pip install -r requirements.txt
Na een clone: alles terug
```

- `.venv/` is een eigen Python met eigen packages, voor dit project alleen
- Windows: `.venv\Scripts\activate`
- `requirements.txt` is je manifest. Die commit je, `.venv/` niet.

<!-- slide 41 -->

### Python gebruiken

*test.py*

```python
from email_validator import validate_email, EmailNotValidError

for email in ['test@kdg.be', 'bladiblabla']:
    try:
        info = validate_email(email, check_deliverability=False)
        print(f'{info.normalized} is geldig')
    except EmailNotValidError as e:
        print(e)
```

```console
$ python test.py
test@kdg.be is geldig
An email address must have an @-sign.
```

<!-- slide 42 -->

### Of gewoon uv

```console
$ uv init
Initialized project `email-test-py`
$ uv add email-validator
Creating virtual environment at: .venv
Installed 3 packages in 9ms
$ uv run test.py
test@kdg.be is geldig
An email address must have an @-sign.
```

- Manifest in `pyproject.toml`, lock file in `uv.lock`
- Maakt de virtual environment zelf, je activeert niets
- Wie je project cloont, typt `uv sync` en heeft alles
- [docs.astral.sh/uv](https://docs.astral.sh/uv/)

<!-- slide 43 -->

## 6. C# en Unity

<!-- slide 44 -->

### NuGet

```bash
# Alles uit de .csproj installeren, na een clone
dotnet restore

# Een package toevoegen
dotnet add package EmailValidation

# Een package verwijderen
dotnet remove package EmailValidation
```

- Het manifest is je `.csproj`, met een `PackageReference` per package
- De packages zelf staan globaal in `~/.nuget/packages`
- Ik doe alles in de terminal. Met Visual Studio (🤮) kan het ook met de GUI 😇

<!-- slide 45 -->

### Als het toch Visual Studio moet zijn

![Visual Studio met het menu Tools, NuGet Package Manager, Manage NuGet Packages for Solution](https://slides.mctantwerp.be/decks/nl/programming-principles/04-package-managers/nuget-visual-studio.jpg)

<!-- slide 46 -->

### NuGet gebruiken

```console
$ dotnet new console
The template "Console App" was created successfully.
$ dotnet add package EmailValidation
info : PackageReference for package
      'EmailValidation' version '1.4.0' added
$ dotnet run
test@test.be: True
bladiblabla: False
```

*Program.cs*

```csharp
using EmailValidation;

string[] emails = ["test@test.be", "bladiblabla"];

foreach (var email in emails)
{
    var valid = EmailValidator.Validate(email);
    Console.WriteLine($"{email}: {valid}");
}
```

<!-- slide 47 -->

### Unity

*Packages/manifest.json*

```json
{
  "dependencies": {
    "com.unity.cinemachine": "3.1.4",
    "com.unity.inputsystem": "1.14.2",
    "com.unity.textmeshpro": "3.0.9"
  }
}
```

- Window, Package Manager: zoeken en installeren met een knop
- `manifest.json` en `packages-lock.json` commit je
- `Library/`, `Temp/` en `Logs/` niet. Unity maakt ze zelf opnieuw.
- Neem de [Unity .gitignore](https://github.com/github/gitignore/blob/main/Unity.gitignore) van GitHub

<!-- slide 48 -->

## 7. Heel belangrijk

<!-- slide 49 -->

### Package folders

- **node_modules/**: npm, pnpm, Yarn
- **vendor/**: Composer
- **.venv/**: pip, uv
- **bin/ en obj/**: dotnet
- **Library/**: Unity
- **dist/ en build/**: Wat je build maakt

De code van al je packages. Niet van jou.

<!-- slide 50 -->

Strafwerk op een schoolbord: "Ik zet node_modules en vendor nooit op git, nooit in een zip en ik kopieer ze nooit over." Eerst vier keer, dan BIS, BIS 2, BIS 3 en BIS 4 tot het honderd keer op het bord staat.

<!-- slide 51 -->

### De zwaarste objecten in het universum

De zwaarste objecten in het universum, van licht naar zwaar: De zon (2 × 10³⁰ kg), Neutronenster (een theelepel weegt een berg), Zwart gat (zelfs licht ontsnapt niet), node_modules (na één npm install).

<!-- slide 52 -->

### Geen grap

Mappen naast elkaar op schaal: package.json + lock 108 KB (2 bestanden), src/ 4,1 MB (281 bestanden), node_modules/ 221 MB (10.823 bestanden).

<!-- slide 53 -->

### Wat er gebeurt als je het toch pusht

```console
$ git add .
$ git commit -m "Eerste versie"
[main (root-commit) 4e1b0c2] Eerste versie
 10823 files changed, 1874213 insertions(+)
$ git push
Writing objects: 100% (11402/11402), 214.62 MiB | 8.40 MiB/s, done.
remote: error: File node_modules/@next/swc-darwin-arm64/next-swc.darwin-arm64.node is 112.40 MB;
remote: error: this exceeds GitHub's file size limit of 100.00 MB
remote: error: GH001: Large files detected. You may want to try Git Large File Storage.
! [remote rejected] main -> main (pre-receive hook declined)
```

<!-- slide 54 -->

### Waarom nooit

#### Het komt terug

- `npm install`, `composer install`, `uv sync`. De lock file zegt exact welke versies.
- Wat ooit gecommit is, blijft in je geschiedenis. Elke clone downloadt die 200 MB opnieuw, ook nadat je de map weggooide.
- Elke update wordt een commit met duizenden bestanden die niemand kan reviewen

#### Het werkt niet bij een ander

- Packages bevatten binaries per systeem: `lightningcss-darwin-arm64` draait alleen op een Mac met een M-chip
- Van je Mac naar de Windows-laptop van je groepsgenoot, en het project start niet meer
- Wat er dan staat: `Cannot find module @rollup/rollup-win32-x64-msvc`

<!-- slide 55 -->

### Ook niet in je zip

```console
$ zip -r portfolio.zip portfolio
portfolio.zip is 94 MB
$ zip -r portfolio.zip portfolio -x "*/node_modules/*" "*/.git/*"
portfolio.zip is 2,1 MB
```

- Niet in de zip die je indient, en niet op een stick
- Je project niet in OneDrive of iCloud: die syncen 10.823 bestanden
- Naar een andere laptop? Clone en install, niet kopiëren.

**[Bestandsbeheer](https://slides.mctantwerp.be/nl/general/02-bestandsbeheer)**: Zippen zonder node_modules

<!-- slide 56 -->

### Zo hou je het eruit

*.gitignore*

```bash
node_modules/
vendor/
.venv/
bin/
obj/
Library/
.env
```

```console
$ echo "node_modules/" >> .gitignore
$ git rm -r --cached node_modules
rm 'node_modules/.package-lock.json' ...
$ git commit -m "Haal node_modules uit de repo"
```

> [!TIP]
> Zet `.gitignore` erin voor je eerste `git add`. GitHub heeft templates per taal.

<!-- slide 57 -->

**Een cache, geen code.**

Alles erin komt terug met één commando. Wat je deelt, is het recept: package.json en de lock file.

<!-- slide 58 -->

### Je krijgt een zip met node_modules, gemaakt op een Mac. Op jouw Windows-laptop crasht npm run dev. Wat doe je?

- A. Node opnieuw installeren
- B. node_modules verwijderen en npm install
- C. Vragen om een nieuwe zip
- D. package-lock.json verwijderen

**Antwoord:** B. node_modules verwijderen en npm install

Die node_modules bevat binaries voor macOS, die draaien niet op Windows. npm install haalt de versies voor jouw systeem, op basis van dezelfde lock file. De lock file verwijderen lost niets op en geeft je misschien andere versies dan op die Mac.

<!-- slide 59 -->

### Welke bestanden van een npm project horen in git?

- A. Alleen package.json
- B. package.json en package-lock.json
- C. package.json, package-lock.json en node_modules
- D. Alleen node_modules, dan moet niemand installeren

**Antwoord:** B. package.json en package-lock.json

package.json zegt wat je nodig hebt, de lock file zegt exact welke versies. Zonder lock file krijgt je collega misschien nieuwere versies dan jij. node_modules komt terug met npm install.

<!-- slide 60 -->

## 8. Opdracht

<!-- slide 61 -->

### Opdracht: Package managers installeren

*Individueel*

Doe dezelfde test als op de slides: package installeren, gebruiken, de map weggooien, terugzetten.

1. Installeer Node.js en test met npm (of pnpm)
2. Web: PHP en Composer. 3D: de .NET SDK en NuGet. AV: Python met pip of uv.
3. Documenteer in je [portfolio](/nl/general/04-portfolio) wat je deed, met de commando’s en screenshots
4. Beantwoord in je eigen woorden: wat is een package manager, en waarom is het handig?
5. Bonus: nog een derde package manager

<!-- slide 62 -->

### Wat je installeert

- **[Node.js en npm](https://nodejs.org)**: De LTS versie van nodejs.org. npm zit erbij.
- **[Herd](https://herd.laravel.com)**: PHP, Composer en Node in één keer. Mac en Windows.
- **[Laragon](https://kdg.cloud.panopto.eu/Panopto/Pages/Viewer.aspx?id=d34762b7-0d76-496b-9859-b36d00a055b4)**: Alternatief op Windows: Node, PHP en Python samen. Grow video.
- **[Composer](https://getcomposer.org/download/)**: Los installeren, als je geen Herd of Laragon gebruikt
- **[Python](https://www.python.org/downloads/)**: Van python.org, en daarna `uv` via docs.astral.sh/uv
- **[.NET SDK](https://dotnet.microsoft.com/en-us/download)**: De SDK, niet alleen de runtime. `dotnet` en NuGet zitten erin.

## Begrippen

- **Package manager**: Een tool die code van anderen in je project zet, met de juiste versie en alles wat die code zelf nodig heeft. npm voor JavaScript, Composer voor PHP, pip voor Python, NuGet voor C#. (slide 7)
- **Package**: Een stuk herbruikbare code met een naam en een versienummer, gepubliceerd zodat anderen het kunnen installeren. Ook library of dependency genoemd zodra het in jouw project zit. (slide 7)
- **Registry**: De online catalogus waar de packages staan: npmjs.com, packagist.org, pypi.org, nuget.org. De package manager op je laptop haalt ze daar op. (slide 15)
- **Dependency**: Een package waar jouw project op steunt. Een dependency heeft zelf ook dependencies, en die worden mee geïnstalleerd. Daarom krijg je 203 packages als je er 28 vraagt. (slide 26)
- **package.json**: Het manifest van een JavaScript project: welke packages je nodig hebt, met een versie range, en de scripts zoals npm run dev. Je schrijft het zelf of laat npm het aanpassen. (slide 16)
- **Lock file**: package-lock.json, composer.lock, uv.lock: de exacte versie van elk geïnstalleerd package, ook van de dependencies van je dependencies. Zo krijgt iedereen in het team hetzelfde. Commit je altijd mee. (slide 16)
- **Caret (^)**: ^2.0.4 laat elke versie toe vanaf 2.0.4 tot onder 3.0.0: nieuwe minors en patches wel, een nieuwe major niet. De standaard als je npm install doet. (slide 18)
- **Tilde (~)**: ~2.0.4 laat alleen patches toe: vanaf 2.0.4 tot onder 2.1.0. (slide 18)
- **node_modules**: De map waar npm alle packages van je project in zet. Komt altijd terug met npm install, dus hij staat in .gitignore en nooit in een zip. (slide 49)
- **vendor**: De map waar Composer de packages van een PHP project in zet, met vendor/autoload.php erbij. Net als node_modules nooit op git. (slide 49)
- **devDependencies**: Packages die je alleen nodig hebt tijdens het ontwikkelen, zoals Vite of een linter. Je installeert ze met npm install -D. Composer noemt het require-dev. (slide 29)
- **LTS**: Long Term Support: een versie die geen nieuwe features meer krijgt, wel bugfixes en security fixes, bij Node.js 30 maanden lang. De versie die je installeert voor een project. (slide 23)
- **npm run**: Voert een script uit dat in package.json onder scripts staat, zoals npm run dev of npm run build. Zo hoeft niemand de echte commando’s van een project te kennen. (slide 30)
- **npm ci**: Installeert exact wat in package-lock.json staat en past niets aan. Faalt als package.json en de lock file niet overeenkomen. Bedoeld voor servers en CI. (slide 31)
- **npx**: Voert het commando van een package uit zonder het in je project te installeren. Handig voor iets wat je één keer nodig hebt. (slide 30)
- **npm audit**: Vergelijkt je geïnstalleerde packages met een lijst van bekende lekken en zegt welke versie het oplost. npm audit fix past de veilige updates toe. (slide 32)
- **Supply chain attack**: Een aanval via de code die je installeert in plaats van via je eigen code: iemand neemt het account van een maintainer over en publiceert een versie met malware. In september 2025 gebeurde dat met chalk, debug en honderden andere npm packages. (slide 11)
- **Typosquatting**: Een kwaadaardig package met een naam die bijna gelijk is aan een populaire, in de hoop dat je je vertypt. expresss in plaats van express. (slide 12)
- **Autoloader**: vendor/autoload.php van Composer. Eén require bovenaan je script en elke class van je packages is beschikbaar, zonder dat je zelf bestanden includet. (slide 36)
- **Virtual environment**: Een map (meestal .venv) met een eigen Python en eigen packages voor één project. python -m venv .venv maakt hem, activeren doe je per terminal. Hoort niet op git. (slide 40)
- **requirements.txt**: De klassieke lijst van packages van een Python project, één per regel. pip freeze schrijft hem, pip install -r requirements.txt installeert alles terug. (slide 40)
- **uv**: Een snelle package manager voor Python. uv add zet een package in pyproject.toml en uv.lock, en maakt de virtual environment voor je aan. (slide 42)
- **NuGet**: De package manager en registry van .NET. Met dotnet add package komt een package in je .csproj, de bestanden zelf staan globaal in ~/.nuget/packages. (slide 44)

## Links en bronnen

### In de presentatie

- [Windows](https://learn.microsoft.com/en-us/windows/package-manager/winget/) (slide 6)
- [macOS](https://brew.sh/) (slide 6)
- [kik, left-pad, and npm](https://blog.npmjs.org/post/141577284765/kik-left-pad-and-npm) (slide 10)
- [npm debug and chalk packages compromised](https://www.aikido.dev/blog/npm-debug-and-chalk-packages-compromised) (slide 11)
- [Our plan for a more secure npm supply chain](https://github.blog/security/supply-chain-security/our-plan-for-a-more-secure-npm-supply-chain/) (slide 11): Shai-Hulud, september 2025
- [npmjs.com](https://www.npmjs.com) (slide 17)
- [packagist.org](https://packagist.org) (slide 17, slide 34)
- [pypi.org](https://pypi.org) (slide 17)
- [nuget.org](https://www.nuget.org) (slide 17)
- [rubygems.org](https://rubygems.org) (slide 17)
- [Node.js](https://nodejs.org) (slide 22, slide 62)
- [nodejs.org, Node.js Releases](https://nodejs.org/en/about/previous-releases) (slide 23)
- [Herd](https://herd.laravel.com) (slide 34, slide 62)
- [uv](https://docs.astral.sh/uv/) (slide 42)
- [Unity .gitignore](https://github.com/github/gitignore/blob/main/Unity.gitignore) (slide 47)
- [Laragon](https://kdg.cloud.panopto.eu/Panopto/Pages/Viewer.aspx?id=d34762b7-0d76-496b-9859-b36d00a055b4) (slide 62)
- [Composer](https://getcomposer.org/download/) (slide 62)
- [Python](https://www.python.org/downloads/) (slide 62)
- [.NET SDK](https://dotnet.microsoft.com/en-us/download) (slide 62)

### Bronnen

- [npm install](https://docs.npmjs.com/cli/commands/npm-install)
- [npm ci](https://docs.npmjs.com/cli/commands/npm-ci)
- [About semantic versioning](https://docs.npmjs.com/about-semantic-versioning): Wat ^ en ~ betekenen
- [npm semver calculator](https://semver.npmjs.com/)
- [Composer, basic usage](https://getcomposer.org/doc/01-basic-usage.md)
- [Install packages in a virtual environment using pip and venv](https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments/)
- [PEP 668, externally managed environments](https://peps.python.org/pep-0668/): Waarom pip install globaal geweigerd wordt
- [Ubuntu release cycle](https://ubuntu.com/about/release-cycle): Negen maanden voor een gewone release, vijf jaar voor een LTS
- [Install and use a NuGet package with the dotnet CLI](https://learn.microsoft.com/en-us/nuget/consume-packages/install-use-packages-dotnet-cli)
- [Unity Package Manager window](https://docs.unity3d.com/Manual/upm-ui.html)
- [About large files on GitHub](https://docs.github.com/en/repositories/working-with-files/managing-large-files/about-large-files-on-github): De limiet van 100 MB per bestand
