Guide de Contribution — GeoLeaf.js
Bienvenue ! Merci de votre intérêt pour contribuer à GeoLeaf.js. Ce guide vous aidera à démarrer.
Version produit : GeoLeaf Platform V2 Version : 2.0.0 Dernière mise à jour : mars 2026
Convention de versioning : Platform V2 est le label produit ; le SemVer technique des packages/releases est en 2.0.x. Voir VERSIONING_POLICY.md.
Table des matières
- Code de Conduite
- Comment Contribuer
- Configuration de Développement
- Standards de Code
- Processus de Pull Request
- Architecture et Modules
- Tests
- Documentation
- Versioning
Code de Conduite
Nos Engagements
GeoLeaf.js s'engage à maintenir une communauté ouverte, accueillante et inclusive. Nous attendons de tous les contributeurs qu'ils :
- Respectent tous les participants, indépendamment de leur niveau d'expérience
- Acceptent les critiques constructives avec grâce
- Se concentrent sur ce qui est le mieux pour la communauté
- Communiquent de manière professionnelle et courtoise
Comportements Inacceptables
- Langage ou imagerie sexualisés, attention non désirée
- Trolling, commentaires insultants/désobligeants
- Harcèlement public ou privé
- Publication d'informations privées sans permission
Comment Contribuer
Types de Contributions
Nous accueillons plusieurs types de contributions :
Rapports de Bugs
- Utilisez le template d'issue GitHub
- Incluez une description claire du problème
- Fournissez des étapes pour reproduire
- Ajoutez des captures d'écran si pertinent
- Précisez la version de GeoLeaf.js
Suggestions de Fonctionnalités
- Ouvrez une issue avec le label "enhancement"
- Décrivez le cas d'usage et le problème résolu
- Proposez une solution ou des alternatives
- Soyez ouvert aux discussions
Documentation
- Corrections de typos
- Clarifications
- Nouveaux guides ou tutoriels
Code
- Corrections de bugs
- Nouvelles fonctionnalités
- Optimisations de performance
- Refactoring
Configuration de Développement
Prérequis
# Node.js >= 18.x
node --version # v18.0.0 ou supérieur
# npm >= 11.x (workspaces support requis)
npm --versionInstallation
# 1. Fork le repository sur GitHub
# 2. Clone votre fork
git clone https://github.com/VOTRE-USERNAME/geoleaf-js.git
cd geoleaf-js
# 3. Ajouter le remote upstream
git remote add upstream https://github.com/mattpottier-ship-it/GeoLeaf-Core.git
# 4. Installer les dépendances (npm workspaces — installe tous les packages)
npm install
# 5. Vérifier l'installation
npm testStructure de Branches
main # Production stable
├── feature/* # Nouvelles fonctionnalités
├── bugfix/* # Corrections de bugs
├── hotfix/* # Corrections urgentes production
└── release/* # Préparation releasesLa branche de développement actif est main ; les PR mergent directement dans main via squash merge.
Workflow Git
# 1. Créer une branche depuis main
git checkout main
git pull upstream main
git checkout -b feature/ma-nouvelle-fonctionnalite
# 2. Faire vos modifications
# ... codez, testez, commitez ...
# 3. Mettre à jour depuis upstream
git fetch upstream
git rebase upstream/main
# 4. Pousser vers votre fork
git push origin feature/ma-nouvelle-fonctionnalite
# 5. Créer une Pull Request sur GitHubStandards de Code
Style TypeScript
GeoLeaf.js utilise TypeScript strict (ES2022), ESLint et Prettier pour l'uniformité du code.
# Linter le code
npm run lint
# Fixer automatiquement
npm run lint:fix
# Formatter avec Prettier
npm run format
# Vérification de types
cd packages/core && npm run typecheckConventions de Nommage
// Variables et fonctions : camelCase
const mapInstance = map;
function createMarker() {}
// Classes et interfaces : PascalCase
class LayerManager {}
interface POIConfig {}
// Constants : UPPER_SNAKE_CASE
const MAX_ZOOM_LEVEL = 18;
const DEFAULT_CONFIG = {};
// Modules : kebab-case
// fichier : layer-manager.ts
// répertoire : content-builder/Organisation du Code
/**
* Structure d'un module GeoLeaf v2.
*/
// 1. Imports
import { dependency } from "./module.js";
// 2. Constants
const CONSTANT_VALUE = "value";
// 3. Internal state
let _initialized = false;
// 4. Private helper functions
function _helperFunction() {}
// 5. Public functions (exports)
/**
* Public function description.
* @param config - Module configuration
* @returns true on success
*/
export function publicFunction(config: Config): boolean {
// Implementation
return true;
}TSDoc et Documentation Inline
Toutes les fonctions publiques DOIVENT avoir une TSDoc complète.
/**
* Creates a POI marker on the map with custom options.
*
* @param mapId - Target map container ID
* @param poi - POI object with coordinates and metadata
* @param poi.lnglat - [longitude, latitude] pair (MapLibre GL JS convention)
* @param poi.title - Display title
* @param options - Additional options
* @param options.draggable - Whether the marker is draggable
*
* @returns The created marker instance
*
* @throws {TypeError} If mapId is not a valid string
* @throws {ValidationError} If coordinates are out of valid range
*
* @example
* const marker = createPoiMarker('my-map', {
* lnglat: [2.3522, 48.8566],
* title: 'Paris'
* }, { draggable: true });
*
* @since 2.0.0
*/
export function createPoiMarker(mapId: string, poi: POIConfig, options: MarkerOptions = {}) {
// Implementation
}Architecture Modulaire v2.0
Principes de Modularisation
- Single Responsibility Principle : Un module = une responsabilité
- Modules < 500 lignes : Soft limit — fragmenter si dépassé (hard limit : 700)
- Exports explicites : Toujours nommer les exports
- Dépendances minimales : Éviter les couplages forts
- ESM pur : Aucun
require()ni syntaxe CommonJS
Structure de Répertoires (packages/core/src/)
src/
├── bundle-esm-entry.ts ← Point d'entrée ESM (~50 exports nommés)
├── app/ ← boot, init, helpers
├── contracts/ ← Interfaces partagées inter-modules
├── lazy/ ← 10+ modules lazy-loadés via import()
├── modules/
│ ├── geoleaf.*.ts ← 16 façades publiques (namespace GeoLeaf.*)
│ ├── built-in/ ← api/, config/
│ ├── geojson/ ← Core GeoJSON, styles, clustering
│ ├── poi/ ← Markers, popup, sidepanel, renderers
│ ├── security/ ← Sanitisation XSS, CSRF, DOM security
│ ├── shared/ ← État partagé inter-modules
│ ├── ui/ ← Contrôles, filtres, content-builder
│ └── utils/ ← log, errors, constants, general-utils
└── globals.*.ts ← Orchestrateurs de chargement (B1→B11)Exemple : Créer un Nouveau Module
// src/modules/features/mon-module.ts
/**
* @module features/mon-module
* @description Module description
*/
import { Log } from "../utils/log/index.js";
import { Validators } from "../geoleaf.validators.js";
// Constants
const MODULE_NAME = "MonModule";
// Internal state
let _initialized = false;
/**
* Initializes the module with the provided configuration.
*
* @param config - Module configuration object
* @returns true on success
*/
export function init(config: ModuleConfig): boolean {
try {
Validators.validateConfig(config);
_initialized = true;
return true;
} catch (error) {
Log.error(`${MODULE_NAME} init failed`, error);
return false;
}
}
/**
* Main feature entry point.
*
* @param data - Data to process
* @returns Processed result
*/
export function mainFeature(data: unknown): unknown {
if (!_initialized) {
throw new Error(`${MODULE_NAME} not initialized`);
}
// Implementation
}Processus de Pull Request
Checklist Avant Soumission
Code Quality & Style
- [ ] Code style : Lint et format validés (
npm run lint,npm run format) - [ ] TypeScript :
npm run typecheckpasse sans erreur - [ ] Complexity : Aucune fonction >80 LOC (max : 100 LOC avec justification)
- [ ] Duplication : Pas de code dupliqué (utiliser modules partagés)
- [ ] Nomenclature : Conventions respectées (camelCase, PascalCase, UPPER_SNAKE_CASE)
Security Checklist
- [ ] XSS Prevention : Aucun usage de
innerHTMLsans sanitization- Utiliser
GeoLeaf.DOMSecurity.setSafeHTML()outextContent - Valider avec :
node scripts/audit-innerhtml.cjs
- Utiliser
- [ ] Input Validation : Toutes les entrées utilisateur validées
JSON.parse()wrappé dans try-catch- Pas de
Object.assign()avec données non fiables (prototype pollution)
- [ ] CSRF Protection : Tokens CSRF si formulaires/mutations
- [ ] No-premium-in-core : Aucune référence à
@geoleaf-plugins/*danspackages/core/src/
Tests & Coverage
- [ ] Tests : Tous les tests passent (
npm test) - [ ] Coverage : Couverture ≥ 75% pour nouveau code (
npm run test:coverage) - [ ] Edge cases : Tests pour null/undefined/empty values
- [ ] Tests ajoutés : Nouveaux tests pour nouvelles fonctionnalités
Documentation
- [ ] TSDoc : Complète sur toutes fonctions et exports publics
@paramavec types,@returns,@throwssi applicable
- [ ] CHANGELOG : Entrée ajoutée avec description
- [ ] Examples : Code examples fournis si nouvelle feature
Architecture & Performance
- [ ] Memory leaks : Pas de fuites mémoire
- Vérifier event listeners cleanup
- Vérifier setTimeout/setInterval clearés
- [ ] Bundle size : Pas d'augmentation significative (>5%)
node scripts/analyze-stats.cjs
- [ ] Boot order :
globals.*.tsnon modifié sans vérification séquence B1→B11
Git & CI/CD
- [ ] Commits : Messages clairs (Conventional Commits)
- [ ] Branch : À jour avec
upstream/main - [ ] CI/CD : Pipeline passe (lint, typecheck, tests, build)
- [ ] No warnings : Aucun warning ESLint/TypeScript
Template de Pull Request
## Description
Brève description des changements.
## Type de Changement
- [ ] Bug fix (changement non-breaking qui corrige un problème)
- [ ] New feature (changement non-breaking qui ajoute une fonctionnalité)
- [ ] Breaking change (fix ou feature qui casse la compatibilité)
- [ ] Documentation (changements de documentation uniquement)
- [ ] Refactoring (changement qui n'ajoute pas de feature ni ne fixe de bug)
- [ ] Performance (amélioration des performances)
- [ ] Tests (ajout ou correction de tests)
## Motivation et Contexte
Pourquoi ce changement est nécessaire ? Quel problème résout-il ?
## Comment Tester
1.
2.
3.
## Checklist
- [ ] Mon code suit le style du projet
- [ ] J'ai effectué une auto-revue de mon code
- [ ] J'ai mis à jour la documentation
- [ ] Mes changements ne génèrent pas de warnings
- [ ] J'ai ajouté des tests qui prouvent que mon fix fonctionne
- [ ] Tous les tests passent localement
## Issues Liées
Fixes #(issue_number)Revue de Code
Les Pull Requests seront reviewées selon ces critères :
- Qualité du Code — Conventions, lisibilité, performance
- Tests — Couverture adéquate, pas de régression
- Documentation — TSDoc complète, exemples si nécessaire
- Architecture — Patterns établis, modules bien découplés, no-premium-in-core
Processus de Merge
- Review approuvée par au moins 1 mainteneur
- CI/CD green (typecheck, lint, tests, build)
- Conflits résolus avec main
- Squash merge dans main (commits nettoyés)
Tests
Framework de Tests
- Vitest 3 : Tests unitaires et d'intégration (ESM, provider Istanbul)
- Playwright : Tests E2E (end-to-end, Chromium)
Commandes de Tests
# Tous les tests unitaires (via Turborepo)
npm test
# Tests unitaires (Vitest direct, dans packages/core)
cd packages/core && npm run test:vitest
# Tests avec couverture
npm run test:coverage
# Tests E2E (nécessite deploy/ construit)
npm run test:e2e
# Tests spécifiques (pattern Vitest)
cd packages/core && npx vitest run --testPathPattern=content-builderÉcrire des Tests
Tests Unitaires (Vitest)
// packages/core/__tests__/features/mon-module.test.ts
import { init, mainFeature } from "../../src/modules/features/mon-module";
describe("MonModule", () => {
describe("init()", () => {
it("should initialize with valid config", () => {
const config = { key: "value" };
const result = init(config);
expect(result).toBe(true);
});
it("should reject invalid config", () => {
const result = init(null as any);
expect(result).toBe(false);
});
});
describe("mainFeature()", () => {
beforeEach(() => {
init({ key: "value" });
});
it("should process data correctly", () => {
const data = { test: "data" };
const result = mainFeature(data);
expect(result).toBeDefined();
});
it("should throw if not initialized", () => {
expect(() => mainFeature({})).toThrow();
});
});
});Tests E2E (Playwright)
// e2e/mon-feature.spec.ts
import { test, expect } from "@playwright/test";
test.describe("Ma Fonctionnalité", () => {
test.beforeEach(async ({ page }) => {
// Each spec targets its own deploy variant (ports 8766-8768)
await page.goto("http://localhost:8766");
await page.waitForSelector("#geoleaf-map");
});
test("should render the map", async ({ page }) => {
const canvas = await page.locator("canvas.maplibregl-canvas");
await expect(canvas).toBeVisible();
});
});Couverture de Tests
Objectif : ≥ 75% couverture globale (cible atteinte en v2.0.0)
- Fonctions critiques : 100% couverture recommandée
- Utilitaires : >90% couverture
- UI components : >70% couverture
Documentation
Types de Documentation
- TSDoc Inline : Dans le code source (obligatoire pour les APIs publiques)
- Guides : Documentation dans
packages/core/docs/ - API Reference : Générée depuis TSDoc via TypeDoc (
npm run docs:api) - Exemples : Profils JSON dans
profiles/et démos danspackages/core/demo/
Mettre à Jour la Documentation
# 1. Mettre à jour TSDoc dans le code source
# 2. Régénérer la référence API
cd packages/core && npm run docs:api
# 3. Mettre à jour le guide concerné dans docs/
# 4. Mettre à jour _docs_internes/projet/CHANGELOG.mdVersioning
GeoLeaf.js suit le Semantic Versioning 2.0.0 :
Format : MAJOR.MINOR.PATCH
- MAJOR : Changements incompatibles (breaking changes)
- MINOR : Nouvelles fonctionnalités compatibles
- PATCH : Corrections de bugs compatibles
Exemples
2.0.0 → 2.0.1 # Bug fix
2.0.0 → 2.1.0 # New feature
2.x.x → 3.0.0 # Breaking changeMessages de Commit
Format : Conventional Commits
# Format
<type>(<scope>): <description courte>
[corps optionnel]
[footer optionnel]Types de Commits
feat: Nouvelle fonctionnalitéfix: Correction de bugdocs: Documentation uniquementstyle: Formatage, point-virgules manquants, etc.refactor: Refactoring sans changer le comportementperf: Amélioration des performancestest: Ajout ou correction de testschore: Maintenance, build, dépendances
Exemples
# Feature
git commit -m "feat(content-builder): add template caching system"
# Bug fix
git commit -m "fix(poi): correct marker positioning on zoom"
# Breaking change
git commit -m "feat(api)!: change init signature to accept options object
BREAKING CHANGE: init() now requires options object instead of individual parameters"Bonnes Pratiques
Performance
- Lazy Loading : Charger à la demande via
GeoLeaf._loadModule() - Debounce/Throttle : Sur événements fréquents (scroll, resize, input)
- Cache : Mettre en cache les résultats coûteux
- Memory Management :
- Cleanup event listeners dans destroy()
- Clear setTimeout/setInterval
- Éviter circular references (utiliser WeakMap si nécessaire)
Sécurité
XSS Prevention
// FORBIDDEN
element.innerHTML = userInput;
// CORRECT
GeoLeaf.DOMSecurity.setSafeHTML(element, userInput);
element.textContent = userText;Input Validation
// JSON.parse with error handling
try {
const config = JSON.parse(jsonString);
if (!validateSchema(config)) {
throw new Error("Invalid schema");
}
} catch (e) {
Log.error("Parse error:", (e as Error).message);
config = DEFAULT_CONFIG;
}Prototype Pollution Prevention
// FORBIDDEN
const merged = Object.assign({}, baseConfig, userConfig);
// CORRECT
const ALLOWED_KEYS = ["id", "label", "data"];
const safe: Record<string, unknown> = {};
Object.keys(userConfig).forEach((key) => {
if (ALLOWED_KEYS.includes(key) && key !== "__proto__" && key !== "constructor") {
safe[key] = (userConfig as any)[key];
}
});
const merged = { ...baseConfig, ...safe };Security Tools
# Audit XSS vulnerabilities
node scripts/audit-innerhtml.cjs
# Check dependencies
npm audit
# Validate code
npm run lintMaintenabilité
- DRY : Don't Repeat Yourself
- KISS : Keep It Simple, Stupid
- YAGNI : You Aren't Gonna Need It
- Documentation : Expliquer le "pourquoi", pas le "comment"
Besoin d'Aide ?
Ressources
- Documentation :
packages/core/docs/ - Issues GitHub : Issues
- Contact : Mattieu Pottier — support@geonatwork.fr
Licensing
License Header
All TypeScript/JavaScript files contributed to GeoLeaf Core must include the MIT license header:
/*!
* GeoLeaf Core
* © 2026 Mattieu Pottier
* Released under the MIT License
* https://geoleaf.dev
*/Important : This header must be placed before all other code and comments in the file.
License Agreement
By contributing to GeoLeaf Core, you agree that :
- Your contributions are licensed under the MIT License
- You have the right to license your contributions
- Your contributions do not infringe on any third-party rights
- You understand the distinction between GeoLeaf Core (open source) and the commercial plugins
See the LICENSE file for the complete license text.
Merci de contribuer à GeoLeaf.js !
Votre aide fait de GeoLeaf.js un meilleur outil pour tous.
