Cours JavaScript et TypeScript · Module 5: JavaScript avancé
CommonJS (require/module.exports)
Dans cette leçon11
Dans l'exercice précédent, nous avons découvert les modules ES - le système de modules moderne de JavaScript. Nous allons maintenant plonger dans un autre système de modules, plus ancien mais toujours très important : CommonJS. Même si notre Jurassic Park a besoin des technologies les plus récentes pour surveiller les dinosaures dans le navigateur, son infrastructure serveur solide repose sur Node.js, où CommonJS domine.
Qu'est-ce que CommonJS ?
CommonJS est un système de modules conçu avant tout pour les environnements côté serveur, et il est devenu le standard de l'écosystème Node.js. Il offre une façon d'organiser le code JavaScript en modules séparés, comme les modules ES, mais avec une syntaxe et un comportement différents.
Contrairement aux modules ES, ajoutés à JavaScript en ES6 (2015), CommonJS existait avant et est devenu le standard de fait pour Node.js. Même aujourd'hui, malgré la prise en charge officielle des modules ES dans Node.js, CommonJS reste le format dominant pour les modules Node.js et dans l'écosystème npm.
Les bases de CommonJS
Les principaux concepts de CommonJS sont :
- La fonction
require()pour importer des modules - L'objet
module.exportsouexportspour exporter des éléments
Voyons comment ils fonctionnent dans le contexte de notre système Jurassic Park.
Exporter des éléments en CommonJS
1. Exporter par assignation à module.exports
La façon la plus simple d'exporter consiste à assigner une valeur à module.exports :
1// dinosaur.js - Modèle de dinosaure
2class Dinosaur {
3 constructor(name, species, diet, dangerLevel) {
4 this.name = name;
5 this.species = species;
6 this.diet = diet;
7 this.dangerLevel = dangerLevel;
8 this.health = 100;
9 this.isContained = true;
10 }
11
12 feed() {
13 this.health = Math.min(100, this.health + 20);
14 return `${this.name} a été nourri. Niveau de santé : ${this.health}%`;
15 }
16
17 checkStatus() {
18 return {
19 name: this.name,
20 health: this.health,
21 isContained: this.isContained,
22 warning: this.dangerLevel > 7 ? "NIVEAU DE DANGER ÉLEVÉ" : ""
23 };
24 }
25}
26
27// Exporte la classe entière
28module.exports = Dinosaur;2. Exporter un objet contenant plusieurs éléments
On peut aussi exporter plusieurs éléments en assignant un objet à module.exports :
1// securityUtils.js - Fonctions utilitaires de sécurité
2const SECURITY_LEVELS = {
3 GREEN: "normal",
4 YELLOW: "elevated",
5 ORANGE: "high",
6 RED: "critical"
7};
8
9function checkFenceStatus(sectorId) {
10 // Simule la vérification de l'état des clôtures dans un secteur donné
11 const status = Math.random() > 0.1 ? "active" : "failure";
12 return {
13 sector: sectorId,
14 status,
15 timestamp: new Date().toISOString()
16 };
17}
18
19function triggerAlarm(sector, reason) {
20 console.log(`ALARME ! Secteur ${sector} : ${reason}`);
21 // Logique d'activation de l'alarme
22 return {
23 success: true,
24 alarmId: "ALR-" + Math.floor(Math.random() * 10000)
25 };
26}
27
28function getSecurityStatus() {
29 // Renvoie l'état de sécurité global du parc
30 return {
31 level: SECURITY_LEVELS.GREEN,
32 activeAlarms: 0,
33 lastChecked: new Date().toISOString()
34 };
35}
36
37// Exporte plusieurs éléments sous forme d'objet
38module.exports = {
39 SECURITY_LEVELS,
40 checkFenceStatus,
41 triggerAlarm,
42 getSecurityStatus
43};3. Ajouter des exports au fur et à mesure
On peut aussi ajouter les exports un par un via l'objet exports (qui est une référence vers module.exports) :
1// parkStats.js - Statistiques de Jurassic Park
2let totalVisitors = 0;
3let revenue = 0;
4const incidents = [];
5
6// Exporte la première fonction
7exports.addVisitors = function(count) {
8 totalVisitors += count;
9 return totalVisitors;
10};
11
12// Exporte la deuxième fonction
13exports.addRevenue = function(amount) {
14 revenue += amount;
15 return revenue;
16};
17
18// Exporte la troisième fonction
19exports.recordIncident = function(type, location, description) {
20 const incident = {
21 id: incidents.length + 1,
22 type,
23 location,
24 description,
25 timestamp: new Date().toISOString()
26 };
27 incidents.push(incident);
28 return incident;
29};
30
31// Exporte la quatrième fonction
32exports.getDailyStats = function() {
33 return {
34 totalVisitors,
35 revenue,
36 incidentCount: incidents.length
37 };
38};NOTE : Souviens-toi que exports n'est qu'une référence vers module.exports. Si tu assignes quelque chose directement à module.exports, tu perds cette référence, donc tu ne peux pas mélanger ces styles dans un même fichier :
1// Ceci NE fonctionnera PAS
2exports.someFunction = function() { /* ... */ };
3module.exports = SomeClass; // Ceci écrase l'export précédent
4
5// Ceci fonctionnera
6exports.someFunction = function() { /* ... */ };
7exports.SomeClass = SomeClass;
8
9// Ceci fonctionnera aussi
10module.exports.someFunction = function() { /* ... */ };
11module.exports.SomeClass = SomeClass;
12
13// Et ceci fonctionnera (en assignant l'objet entier)
14module.exports = {
15 someFunction: function() { /* ... */ },
16 SomeClass: SomeClass
17};Importer des modules en CommonJS
1. Importer un module entier
1// app.js
2const Dinosaur = require('./dinosaur.js');
3
4// Crée de nouvelles instances de dinosaure
5const trex = new Dinosaur("Rex", "Tyrannosaurus", "carnivore", 9);
6const brachi = new Dinosaur("Longneck", "Brachiosaurus", "herbivore", 4);
7
8console.log(trex.checkStatus());
9console.log(brachi.feed());2. Importer et déstructurer
1// securityMonitor.js
2const security = require('./securityUtils.js');
3
4// Utilise les fonctions et constantes importées depuis l'objet
5function monitorSecurity() {
6 const sectors = ["A1", "B2", "C3", "D4"];
7
8 // Vérifie chaque secteur
9 sectors.forEach(sector => {
10 const fenceStatus = security.checkFenceStatus(sector);
11
12 if (fenceStatus.status === "failure") {
13 const alarmResult = security.triggerAlarm(
14 sector,
15 "Panne de clôture électrique !"
16 );
17 console.log(`Alarme ${alarmResult.alarmId} déclenchée dans le secteur ${sector}`);
18
19 // Met à jour l'état de sécurité
20 console.log("Changement du niveau de sécurité vers :", security.SECURITY_LEVELS.RED);
21 }
22 });
23}
24
25setInterval(monitorSecurity, 60000); // Vérifie toutes les minutes3. Déstructurer à l'import
Dans les versions plus récentes de Node.js, on peut aussi déstructurer directement à l'import :
1// dashboard.js
2const { checkFenceStatus, triggerAlarm, SECURITY_LEVELS } = require('./securityUtils.js');
3
4// On peut maintenant utiliser les fonctions directement
5function checkAllSectors() {
6 ["A1", "B2", "C3", "D4"].forEach(sector => {
7 console.log(`Vérification du secteur ${sector} : `, checkFenceStatus(sector));
8
9 // Utilise la constante importée
10 if (Math.random() < 0.1) {
11 console.log(`Définition du niveau de sécurité : ${SECURITY_LEVELS.ORANGE}`);
12 }
13 });
14}Différences entre CommonJS et les modules ES
Il est important de comprendre les différences clés entre CommonJS et les modules ES :
| Caractéristique | CommonJS | Modules ES |
|---|---|---|
| Syntaxe d'import | require() | import ... from ... |
| Syntaxe d'export | module.exports = ... ou exports.x = ... | export ... ou export default ... |
| Chargement | Synchrone | Asynchrone (chargé avant l'exécution) |
| Exécution | Code exécuté à chaque import | Code exécuté une seule fois, puis le module est mis en cache |
| Analyse statique | Non prise en charge (chemins dynamiques) | Prise en charge (chemins statiques) |
| Support navigateur | Pas nativement pris en charge | Pris en charge dans les navigateurs modernes |
| Hoisting | Les imports ne sont pas hoistés | Les imports sont hoistés (utilisables avant la déclaration) |
| Tree-shaking | Plus difficile à mettre en place | Plus facile grâce à l'analyse statique |
Structure d'un projet Node.js (backend de Jurassic Park)
Voyons comment on pourrait organiser le backend de notre système de gestion de Jurassic Park avec Node.js et CommonJS :
1jurassic-park-backend/
2├── config/
3│ ├── database.js # Configuration de la base de données
4│ ├── environment.js # Variables d'environnement
5│ └── security.js # Paramètres de sécurité
6├── models/
7│ ├── dinosaur.js # Modèle de dinosaure
8│ ├── employee.js # Modèle d'employé
9│ ├── facility.js # Modèle d'installation
10│ └── index.js # Agrégateur de modèles
11├── controllers/
12│ ├── dinosaurController.js # Contrôleur des dinosaures
13│ ├── securityController.js # Contrôleur de la sécurité
14│ └── employeeController.js # Contrôleur des employés
15├── routes/
16│ ├── api.js # Routeur API principal
17│ ├── dinosaurs.js # Routes liées aux dinosaures
18│ └── security.js # Routes liées à la sécurité
19├── services/
20│ ├── monitoringService.js # Service de surveillance
21│ ├── notificationService.js # Service de notifications
22│ └── loggingService.js # Service de journalisation
23├── utils/
24│ ├── logger.js # Utilitaire de journalisation
25│ └── validators.js # Validateurs de données
26├── middleware/
27│ ├── auth.js # Middleware d'authentification
28│ └── errorHandler.js # Gestion des erreurs
29├── app.js # Application principale
30└── server.js # Fichier de démarrage du serveurExaminons des implémentations d'exemple de ces modules :
Fichier de configuration (config/database.js)
1// config/database.js
2module.exports = {
3 host: process.env.DB_HOST || 'localhost',
4 port: process.env.DB_PORT || 27017,
5 name: process.env.DB_NAME || 'jurassic_park',
6 user: process.env.DB_USER || 'admin',
7 password: process.env.DB_PASSWORD || 'secret',
8
9 // Fonction utilitaire pour construire l'URI de connexion
10 getConnectionString: function() {
11 return `mongodb://${this.user}:${this.password}@${this.host}:${this.port}/${this.name}`;
12 }
13};Modèle (models/dinosaur.js)
1// models/dinosaur.js
2const mongoose = require('mongoose');
3const Schema = mongoose.Schema;
4
5// Schéma du dinosaure
6const dinosaurSchema = new Schema({
7 name: { type: String, required: true },
8 species: { type: String, required: true },
9 diet: { type: String, enum: ['carnivore', 'herbivore', 'omnivore'], required: true },
10 dangerLevel: { type: Number, min: 1, max: 10, required: true },
11 enclosureId: { type: Schema.Types.ObjectId, ref: 'Enclosure' },
12 health: { type: Number, default: 100 },
13 status: {
14 isContained: { type: Boolean, default: true },
15 lastCheck: { type: Date, default: Date.now }
16 },
17 createdAt: { type: Date, default: Date.now },
18 updatedAt: { type: Date, default: Date.now }
19});
20
21// Méthodes du schéma
22dinosaurSchema.methods.feed = function() {
23 this.health = Math.min(100, this.health + 20);
24 this.updatedAt = Date.now();
25 return this.save();
26};
27
28dinosaurSchema.methods.checkStatus = function() {
29 return {
30 name: this.name,
31 health: this.health,
32 isContained: this.status.isContained,
33 warning: this.dangerLevel > 7 ? "NIVEAU DE DANGER ÉLEVÉ" : ""
34 };
35};
36
37// Méthodes statiques
38dinosaurSchema.statics.findDangerous = function() {
39 return this.find({ dangerLevel: { $gte: 8 } });
40};
41
42// Hook pre-save
43dinosaurSchema.pre('save', function(next) {
44 this.updatedAt = Date.now();
45 next();
46});
47
48// Crée le modèle
49const Dinosaur = mongoose.model('Dinosaur', dinosaurSchema);
50
51// Exporte le modèle
52module.exports = Dinosaur;Contrôleur (controllers/dinosaurController.js)
1// controllers/dinosaurController.js
2const Dinosaur = require('../models/dinosaur');
3const logger = require('../utils/logger');
4
5// Objet contrôleur avec plusieurs méthodes
6const dinosaurController = {
7 // Récupère tous les dinosaures
8 getAllDinosaurs: async (req, res) => {
9 try {
10 const dinosaurs = await Dinosaur.find().populate('enclosureId');
11 res.json(dinosaurs);
12 } catch (error) {
13 logger.error('Erreur lors de la récupération des dinosaures :', error);
14 res.status(500).json({ error: 'Erreur serveur' });
15 }
16 },
17
18 // Récupère un dinosaure par son ID
19 getDinosaurById: async (req, res) => {
20 try {
21 const dinosaur = await Dinosaur.findById(req.params.id).populate('enclosureId');
22
23 if (!dinosaur) {
24 return res.status(404).json({ error: 'Dinosaure introuvable' });
25 }
26
27 res.json(dinosaur);
28 } catch (error) {
29 logger.error(`Erreur lors de la récupération du dinosaure ID=${req.params.id} : `, error);
30 res.status(500).json({ error: 'Erreur serveur' });
31 }
32 },
33
34 // Ajoute un nouveau dinosaure
35 createDinosaur: async (req, res) => {
36 try {
37 const { name, species, diet, dangerLevel, enclosureId } = req.body;
38
39 const newDinosaur = new Dinosaur({
40 name,
41 species,
42 diet,
43 dangerLevel,
44 enclosureId
45 });
46
47 await newDinosaur.save();
48 logger.info(`Nouveau dinosaure ajouté : ${name} (${species})`);
49
50 res.status(201).json(newDinosaur);
51 } catch (error) {
52 logger.error('Erreur lors de la création du dinosaure :', error);
53 res.status(400).json({ error: error.message });
54 }
55 },
56
57 // Nourrit un dinosaure
58 feedDinosaur: async (req, res) => {
59 try {
60 const dinosaur = await Dinosaur.findById(req.params.id);
61
62 if (!dinosaur) {
63 return res.status(404).json({ error: 'Dinosaure introuvable' });
64 }
65
66 await dinosaur.feed();
67 logger.info(`Dinosaure nourri : ${dinosaur.name}`);
68
69 res.json({
70 message: `${dinosaur.name} a été nourri. Niveau de santé : ${dinosaur.health}%`,
71 health: dinosaur.health
72 });
73 } catch (error) {
74 logger.error(`Erreur lors du nourrissage du dinosaure ID=${req.params.id} : `, error);
75 res.status(500).json({ error: 'Erreur serveur' });
76 }
77 },
78
79 // Trouve les dinosaures dangereux
80 getDangerousDinosaurs: async (req, res) => {
81 try {
82 const dangerousDinos = await Dinosaur.findDangerous();
83 res.json(dangerousDinos);
84 } catch (error) {
85 logger.error('Erreur lors de la récupération des dinosaures dangereux :', error);
86 res.status(500).json({ error: 'Erreur serveur' });
87 }
88 }
89};
90
91// Exporte l'objet contrôleur entier
92module.exports = dinosaurController;Routes (routes/dinosaurs.js)
1// routes/dinosaurs.js
2const express = require('express');
3const router = express.Router();
4const dinosaurController = require('../controllers/dinosaurController');
5const authMiddleware = require('../middleware/auth');
6
7// Routes liées aux dinosaures
8router.get('/', dinosaurController.getAllDinosaurs);
9router.get('/dangerous', dinosaurController.getDangerousDinosaurs);
10router.get('/:id', dinosaurController.getDinosaurById);
11
12// Routes nécessitant une authentification
13router.post('/', authMiddleware.verifyToken, dinosaurController.createDinosaur);
14router.post('/:id/feed', authMiddleware.verifyToken, dinosaurController.feedDinosaur);
15
16// Exporte le routeur
17module.exports = router;Fichier principal de l'application (app.js)
1// app.js
2const express = require('express');
3const mongoose = require('mongoose');
4const cors = require('cors');
5const morgan = require('morgan');
6const helmet = require('helmet');
7
8// Imports de configuration et de middleware
9const dbConfig = require('./config/database');
10const errorHandler = require('./middleware/errorHandler');
11const logger = require('./utils/logger');
12
13// Imports des routes
14const apiRouter = require('./routes/api');
15const dinosaurRoutes = require('./routes/dinosaurs');
16const securityRoutes = require('./routes/security');
17
18// Initialise l'application Express
19const app = express();
20
21// Middleware
22app.use(helmet()); // Sécurité
23app.use(cors()); // Partage des ressources entre origines (CORS)
24app.use(morgan('dev')); // Journalisation des requêtes HTTP
25app.use(express.json()); // Analyse du JSON
26app.use(express.urlencoded({ extended: false })); // Analyse des formulaires
27
28// Connexion à la base de données
29mongoose.connect(dbConfig.getConnectionString(), {
30 useNewUrlParser: true,
31 useUnifiedTopology: true
32})
33.then(() => {
34 logger.info('Connecté à la base de données MongoDB');
35})
36.catch(err => {
37 logger.error('Erreur de connexion à la base de données :', err);
38 process.exit(1);
39});
40
41// Routes
42app.use('/api', apiRouter);
43app.use('/api/dinosaurs', dinosaurRoutes);
44app.use('/api/security', securityRoutes);
45
46// Gère les erreurs 404
47app.use((req, res, next) => {
48 res.status(404).json({ error: 'Introuvable' });
49});
50
51// Gestion centralisée des erreurs
52app.use(errorHandler);
53
54// Exporte l'application
55module.exports = app;Fichier de démarrage du serveur (server.js)
1// server.js
2const http = require('http');
3const app = require('./app');
4const logger = require('./utils/logger');
5
6// Paramètres du port
7const port = process.env.PORT || 3000;
8app.set('port', port);
9
10// Crée le serveur HTTP
11const server = http.createServer(app);
12
13// Écoute les connexions
14server.listen(port, () => {
15 logger.info(`Serveur Jurassic Park démarré sur le port ${port}`);
16});
17
18// Gère les erreurs du serveur
19server.on('error', (error) => {
20 if (error.syscall !== 'listen') {
21 throw error;
22 }
23
24 logger.error(`Erreur du serveur : ${error}`);
25 process.exit(1);
26});Dépendances circulaires
L'un des défis du travail avec CommonJS (et avec les modules en général) est celui des dépendances circulaires, qui se produisent quand le module A importe le module B et que le module B importe le module A :
1// dinosaurUtils.js
2const Dinosaur = require('./dinosaur');
3
4function isDangerous(dino) {
5 return dino.dangerLevel > 7;
6}
7
8module.exports = { isDangerous };
9
10// dinosaur.js
11const dinosaurUtils = require('./dinosaurUtils'); // Dépendance circulaire !
12
13class Dinosaur {
14 // ... implémentation de la classe
15
16 isHighRisk() {
17 return dinosaurUtils.isDangerous(this);
18 }
19}
20
21module.exports = Dinosaur;CommonJS gère les dépendances circulaires, mais cela peut entraîner des comportements étranges comme des objets incomplets. Pour éviter ce genre de problème :
- Restructure le code pour éviter les dépendances circulaires
- Utilise le pattern d'injection de dépendances
- Déplace le code partagé dans un troisième module dont les deux dépendent
Transpiler de CommonJS vers les modules ES et inversement
Dans de nombreux projets, on doit gérer à la fois CommonJS et les modules ES. Par exemple, le frontend de notre Jurassic Park pourrait utiliser les modules ES tandis que le backend utilise CommonJS. Que faire quand on veut partager du code ?
Babel pour la transpilation
On peut utiliser Babel pour transpiler entre les formats :
1// babel.config.js pour modules ES -> CommonJS
2module.exports = {
3 presets: [
4 ['@babel/preset-env', {
5 targets: {
6 node: 'current'
7 },
8 modules: 'commonjs' // Convertit import/export en require/exports
9 }]
10 ]
11};Dual Package Hazard (piège du double package)
Une autre approche consiste à créer des packages qui prennent en charge les deux formats de modules. Dans package.json, tu peux définir :
1{
2 "name": "jurassic-park-utils",
3 "version": "1.0.0",
4 "main": "dist/index.js",
5 "module": "dist/index.mjs",
6 "exports": {
7 "import": "./dist/index.mjs",
8 "require": "./dist/index.js"
9 }
10}Approche moderne : Node.js avec les modules ES
Les versions plus récentes de Node.js (à partir de 13.2.0) prennent officiellement en charge les modules ES. On peut activer ce support de plusieurs façons :
- En utilisant l'extension
.mjspour les fichiers de modules ES - En définissant
"type": "module"dans package.json
Cependant, de nombreux packages npm utilisent encore CommonJS, donc tu devras probablement travailler avec les deux systèmes.
Exemple pratique : migration de CommonJS vers les modules ES
Disons qu'on veut migrer une partie de notre backend de CommonJS vers les modules ES. Voici à quoi pourrait ressembler une telle migration :
Version CommonJS d'origine (dinosaurController.js)
1// controllers/dinosaurController.js (CommonJS)
2const Dinosaur = require('../models/dinosaur');
3const logger = require('../utils/logger');
4
5function getAllDinosaurs(req, res) {
6 // implémentation
7}
8
9function getDinosaurById(req, res) {
10 // implémentation
11}
12
13module.exports = {
14 getAllDinosaurs,
15 getDinosaurById
16};Migration vers les modules ES (dinosaurController.mjs)
1// controllers/dinosaurController.mjs (Modules ES)
2import Dinosaur from '../models/dinosaur.mjs';
3import logger from '../utils/logger.mjs';
4
5export function getAllDinosaurs(req, res) {
6 // implémentation
7}
8
9export function getDinosaurById(req, res) {
10 // implémentation
11}
12
13// On peut aussi exporter un objet par défaut contenant les fonctions
14export default {
15 getAllDinosaurs,
16 getDinosaurById
17};Wrapper de compatibilité
Parfois, on doit créer un wrapper qui permet d'importer un module ES depuis du code CommonJS :
1// controllers/dinosaurControllerWrapper.js (CommonJS)
2// Ce fichier charge un module ES et l'exporte en CommonJS
3const importESM = async () => {
4 const controller = await import('./dinosaurController.mjs');
5 return controller.default; // ou des fonctions sélectionnées
6};
7
8// Exporte une fonction qui renvoie une Promise contenant le contrôleur
9module.exports = importESM();Résumé
CommonJS est un système de modules très répandu dans Node.js qui permet d'organiser le code en modules séparés, ce qui est essentiel pour construire des applications évolutives comme le backend de notre système de gestion de Jurassic Park.
Les principaux concepts de CommonJS sont :
- La fonction
require()pour importer des modules - L'objet
module.exportsouexportspour exporter des éléments - Le chargement synchrone des modules
- La mise en cache des modules, essentielle pour les performances
Malgré la popularité croissante des modules ES, CommonJS reste un système de modules important, surtout dans l'écosystème Node.js. Comprendre les différences entre CommonJS et les modules ES est essentiel pour développer efficacement des applications JavaScript capables de tourner aussi bien dans le navigateur que sur le serveur.
Dans notre Jurassic Park, on utilise CommonJS pour le backend (surveillance, base de données, contrôle de la sécurité) et les modules ES pour le frontend (interface utilisateur, tableaux de bord, notifications). Cette combinaison nous donne le meilleur des deux mondes et nous permet de gérer efficacement un système qui doit être fiable - après tout, la sécurité du parc et de ses visiteurs dépend de notre code !
Dans l'exercice suivant, nous examinerons l'import dynamique de modules, qui offre une flexibilité encore plus grande pour charger du code à la demande, ce qui peut être crucial dans les situations d'urgence à Jurassic Park.
Code de cette leçon : main.js
1// CommonJS w Parku Jurajskim - Backend Node.js
2// Demonstracja systemu require/module.exports
3
4console.log("Backend Parku Jurajskiego - CommonJS");
5console.log("Node.js server management system\n");
6
7// ===========================================
8// CommonJS module simulation
9// ===========================================
10
11// === dinosaur.js - Model dinosaura (CommonJS) ===
12class Dinosaur {
13 constructor(name, species, diet, dangerLevel) {
14 this.name = name;
15 this.species = species;
16 this.diet = diet;
17 this.dangerLevel = dangerLevel;
18 this.health = 100;
19 this.id = `DINO-${Math.random().toString(36).substr(2, 9).toUpperCase()}`;
20 }
21
22 feed() {
23 this.health = Math.min(100, this.health + 20);
24 return { success: true, message: `${this.name} nakarmiony`, health: this.health };
25 }
26
27 getDetails() {
28 return {
29 id: this.id,
30 name: this.name,
31 species: this.species,
32 diet: this.diet,
33 dangerLevel: this.dangerLevel,
34 health: this.health
35 };
36 }
37}
38
39// CommonJS export - main class
40const DinosaurModule = Dinosaur;
41
42// === database.js - Simulation bazy danych (CommonJS) ===
43const database = {
44 dinosaurs: new Map(),
45 enclosures: new Map(),
46 logs: []
47};
48
49function saveDinosaur(dinosaur) {
50 database.dinosaurs.set(dinosaur.id, dinosaur);
51 database.logs.push({ action: 'SAVE_DINOSAUR', id: dinosaur.id, timestamp: Date.now() });
52 return { success: true, id: dinosaur.id };
53}
54
55function getDinosaur(id) {
56 const dino = database.dinosaurs.get(id);
57 return dino || null;
58}
59
60function getAllDinosaurs() {
61 return Array.from(database.dinosaurs.values());
62}
63
64function deleteDinosaur(id) {
65 const deleted = database.dinosaurs.delete(id);
66 if (deleted) {
67 database.logs.push({ action: 'DELETE_DINOSAUR', id, timestamp: Date.now() });
68 }
69 return { success: deleted };
70}
71
72function getDatabaseStats() {
73 return {
74 dinosaurCount: database.dinosaurs.size,
75 enclosureCount: database.enclosures.size,
76 totalLogs: database.logs.length
77 };
78}
79
80// CommonJS multiple exports
81const DatabaseModule = {
82 saveDinosaur,
83 getDinosaur,
84 getAllDinosaurs,
85 deleteDinosaur,
86 getDatabaseStats
87};
88
89// === config.js - Konfiguracja (CommonJS) ===
90const config = {
91 server: {
92 port: 3000,
93 host: 'localhost',
94 environment: 'development'
95 },
96 database: {
97 url: 'mongodb://localhost:27017/jurassic-park',
98 maxConnections: 10
99 },
100 security: {
101 maxDangerLevel: 10,
102 alertThreshold: 7
103 },
104 park: {
105 maxDinosaurs: 50,
106 maxVisitors: 1000,
107 openingHours: { start: 9, end: 18 }
108 }
109};
110
111// CommonJS config export
112const ConfigModule = config;
113
114// === logger.js - System logowania (CommonJS) ===
115const LOG_LEVELS = {
116 INFO: 'INFO',
117 WARN: 'WARN',
118 ERROR: 'ERROR',
119 DEBUG: 'DEBUG'
120};
121
122function formatLogMessage(level, message) {
123 const timestamp = new Date().toISOString();
124 return `[${timestamp}] [${level}] ${message}`;
125}
126
127function log(level, message) {
128 const formatted = formatLogMessage(level, message);
129 console.log(formatted);
130 database.logs.push({ level, message, timestamp: Date.now() });
131}
132
133function info(message) { log(LOG_LEVELS.INFO, message); }
134function warn(message) { log(LOG_LEVELS.WARN, message); }
135function error(message) { log(LOG_LEVELS.ERROR, message); }
136function debug(message) { log(LOG_LEVELS.DEBUG, message); }
137
138// CommonJS logger exports
139const LoggerModule = {
140 LOG_LEVELS,
141 log,
142 info,
143 warn,
144 error,
145 debug
146};
147
148// === api-routes.js - API endpoints (CommonJS) ===
149class DinosaurAPI {
150 static createDinosaur(req) {
151 const { name, species, diet, dangerLevel } = req.body;
152
153 LoggerModule.info(`Creating dinosaur: ${name}`);
154
155 const dinosaur = new DinosaurModule(name, species, diet, dangerLevel);
156 DatabaseModule.saveDinosaur(dinosaur);
157
158 LoggerModule.info(`Dinosaur created: ${dinosaur.id}`);
159
160 return {
161 status: 201,
162 data: dinosaur.getDetails()
163 };
164 }
165
166 static getDinosaur(req) {
167 const { id } = req.params;
168 LoggerModule.debug(`Fetching dinosaur: ${id}`);
169
170 const dinosaur = DatabaseModule.getDinosaur(id);
171
172 if (!dinosaur) {
173 LoggerModule.warn(`Dinosaur not found: ${id}`);
174 return {
175 status: 404,
176 error: 'Dinosaur not found'
177 };
178 }
179
180 return {
181 status: 200,
182 data: dinosaur.getDetails()
183 };
184 }
185
186 static listDinosaurs() {
187 LoggerModule.debug('Listing all dinosaurs');
188
189 const dinosaurs = DatabaseModule.getAllDinosaurs();
190
191 return {
192 status: 200,
193 data: dinosaurs.map(d => d.getDetails()),
194 count: dinosaurs.length
195 };
196 }
197
198 static deleteDinosaur(req) {
199 const { id } = req.params;
200 LoggerModule.info(`Deleting dinosaur: ${id}`);
201
202 const result = DatabaseModule.deleteDinosaur(id);
203
204 if (!result.success) {
205 return {
206 status: 404,
207 error: 'Dinosaur not found'
208 };
209 }
210
211 LoggerModule.info(`Dinosaur deleted: ${id}`);
212
213 return {
214 status: 200,
215 data: { deleted: true }
216 };
217 }
218
219 static getServerStats() {
220 LoggerModule.debug('Fetching server stats');
221
222 const dbStats = DatabaseModule.getDatabaseStats();
223
224 return {
225 status: 200,
226 data: {
227 ...dbStats,
228 config: {
229 maxDinosaurs: ConfigModule.park.maxDinosaurs,
230 environment: ConfigModule.server.environment
231 }
232 }
233 };
234 }
235}
236
237// CommonJS API export
238const APIModule = DinosaurAPI;
239
240// ===========================================
241// DEMONSTRATION DU SYSTEME COMMONJS
242// ===========================================
243console.log("=== INICJALIZACJA SERWERA ===\n");
244
245LoggerModule.info('Server starting...');
246LoggerModule.info(`Port: ${ConfigModule.server.port}`);
247LoggerModule.info(`Environment: ${ConfigModule.server.environment}`);
248
249console.log("\n=== CREATING DINOSAURS (API CALLS) ===\n");
250
251// API request simulation
252const req1 = {
253 body: { name: 'Rexy', species: 'T-Rex', diet: 'carnivore', dangerLevel: 9 }
254};
255const response1 = APIModule.createDinosaur(req1);
256console.log('POST /api/dinosaurs:', JSON.stringify(response1, null, 2));
257
258const req2 = {
259 body: { name: 'Blue', species: 'Velociraptor', diet: 'carnivore', dangerLevel: 7 }
260};
261const response2 = APIModule.createDinosaur(req2);
262console.log('\nPOST /api/dinosaurs:', JSON.stringify(response2, null, 2));
263
264const req3 = {
265 body: { name: 'Gentle Giant', species: 'Brachiosaurus', diet: 'herbivore', dangerLevel: 2 }
266};
267const response3 = APIModule.createDinosaur(req3);
268console.log('\nPOST /api/dinosaurs:', JSON.stringify(response3, null, 2));
269
270console.log("\n=== RÉCUPÉRATION DES DONNÉES (API CALLS) ===\n");
271
272// Liste de tous les dinosaures
273const listResponse = APIModule.listDinosaurs();
274console.log('GET /api/dinosaurs:', JSON.stringify(listResponse, null, 2));
275
276console.log("\n=== STATYSTYKI SERWERA ===\n");
277
278const statsResponse = APIModule.getServerStats();
279console.log('GET /api/stats:', JSON.stringify(statsResponse, null, 2));
280
281console.log("\n=== DIFFERENCES: CommonJS vs ES Modules ===\n");
282
283console.log("CommonJS (Node.js):");
284console.log(" - Uses: require() and module.exports");
285console.log(" - Synchronous loading");
286console.log(" - Dynamiczne importy w runtime");
287console.log(" - Value is copied\n");
288
289console.log("ES Modules (ES6+):");
290console.log(" - Uses: import and export");
291console.log(" - Asynchronous loading");
292console.log(" - Statyczne importy w compile-time");
293console.log(" - Value is a reference (live binding)\n");
294
295console.log("CommonJS example:");
296console.log(" // dinosaur.js");
297console.log(" module.exports = class Dinosaur { ... };");
298console.log("");
299console.log(" // main.js");
300console.log(" const Dinosaur = require('./dinosaur');\n");
301
302console.log("ES Modules example:");
303console.log(" // dinosaur.js");
304console.log(" export default class Dinosaur { ... }");
305console.log("");
306console.log(" // main.js");
307console.log(" import Dinosaur from './dinosaur.js';\n");
308
309LoggerModule.info('Demo completed successfully!');
310
311console.log("\nBackend Parku Jurajskiego gotowy!");Tu as repéré une erreur dans cette leçon ?
Tâches pratiques dans le jeu
- Tri horizontal
Ordonne les éléments de l'export d'une fonction :