excel-sync
v0.1.3
Published
Node.js/TypeScript orchestrator for Excel VBA sync via PowerShell bridge
Readme
Excel Sync (Node.js + TypeScript + PowerShell)
Ce projet fournit un point de départ pour synchroniser du VBA Excel avec le file system en gardant les opérations COM Excel côté PowerShell.
Principe
Node.js/TypeScriptgère l'orchestration:- synchronisation initiale Excel <->
vba/avec detection des ecarts - choix interactif de la source de verite en cas de differences
- watcher bidirectionnel (file system -> Excel et Excel -> file system)
- polling Excel toutes les 2 secondes
- resolution de conflits LWW (last write wins), egalite -> file system
- anti-boucle avec suppression temporaire des evenements rebonds
- logique métier/comparaison (à enrichir ici)
- synchronisation initiale Excel <->
PowerShellgère uniquement Excel COM:- connexion à l'instance Excel active
- lecture des modules VBA (
inspect) - écriture de code dans un module (
import) - renvoi de codes d'erreur techniques (sans message métier)
Node.js/TypeScriptgère l'affichage des erreurs utilisateur
Structure
scripts/excel-bridge.ps1: bridge PowerShell (JSON in/out)src/infra/: exécution PowerShell + client bridgesrc/app/sync/: logique sync par domaine (scan, apply events, sync initiale, utilitaires)src/app/watcher/: helpers runtime du watchersrc/app/syncService.ts: facade de re-export pour l'appsrc/index.ts: point d'entrée
Prérequis
- Windows + Excel Desktop ouvert avec un classeur actif
- Option Excel activée:
- "Accès approuvé au modèle d'objet du projet VBA"
- Node.js 20+
- PowerShell (
powershell.exeoupwsh)
Installation
pnpm installLancer
Mode dev (sans build):
pnpm run devMode build + exécution:
pnpm run build
pnpm startOption CLI
Par defaut, le watch est unidirectionnel (file system -> Excel).
Pour activer aussi Excel -> file system (polling Excel), utilisez l'opt-in:
pnpm run dev -- --excel-to-fsOu avec le binaire compile:
pnpm start -- --excel-to-fsHelp:
pnpm start -- --helpUtilisation via pnpm dlx
Apres publication NPM:
pnpm dlx excel-sync --help
pnpm dlx excel-sync --excel-to-fsVérification TypeScript
pnpm run checkNotes
- Variable optionnelle:
EXCEL_SYNC_PWSHpour forcer le binaire PowerShell. - Le watcher traite
added/updated/deleted/typeChangedenfile system -> Excel, et enExcel -> file systemsi--excel-to-fsest active. - Le watcher est verrouille sur le classeur actif au demarrage (path+name). Si le classeur change, la synchronisation s'arrete automatiquement pour eviter toute ecriture sur un mauvais fichier.
- Point d'extension recommandé:
src/app/syncService.tspour la future logique de comparaison Excel vs file system. - Les fichiers exportes sont ecrits en UTF-8 avec BOM pour une detection fiable dans les editeurs.
- A l'import, le code est lu en UTF-8 puis valide/converti vers Windows-1252 avant injection dans Excel VBA.
- Le flux
Excel -> file systemest desactive par defaut et s'active via--excel-to-fs.
Architecture et Flux de Synchronisation
Cette section explique visuellement comment fonctionne la synchronisation entre Excel et le système de fichiers.
Vue d'ensemble du système
┌─────────────────────────────────────────────────────────────────┐
│ EXCEL SYNC │
│ (Node.js + TypeScript) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Watcher │ │ Initial │ │ Bridge │ │
│ │ (chokidar) │ │ Sync │ │ PowerShell │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │ │
│ │ Événements FS │ Comparaison │ Commandes │
│ │ (add/change/del) │ (MD5 + date) │ COM Excel │
│ ▼ ▼ ▼ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Gestionnaire de conflits (LWW) │ │
│ │ (Last Write Wins - égalité → File System) │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
│
│ PowerShell Remoting
▼
┌─────────────────────────────────────────────────────────────────┐
│ EXCEL (COM API) │
│ ┌────────────┐ ┌────────────┐ ┌────────────────────────┐ │
│ │ Classeur │──│ Modules │──│ Code VBA │ │
│ │ Actif │ │ VBA │ │ (Standard/Class) │ │
│ └────────────┘ └────────────┘ └────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
│ Export/Import
▼
┌─────────────────────────────────────────────────────────────────┐
│ SYSTÈME DE FICHIERS │
│ (dossier vba/) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Standard.bas MyClass.cls Module1.bas │ │
│ │ (modules standards) (classes) (modules...) │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘Synchronisation initiale (au démarrage)
Au lancement du programme, une synchronisation initiale est effectuée pour détecter les écarts entre Excel et le dossier vba/.
┌─────────────────┐
│ DÉMARRAGE │
└────────┬────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ SYNCHRONISATION INITIALE │
└──────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ ÉTAPE 1: SCAN COMPLET │
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ EXCEL │ │ FILE SYSTEM │ │
│ │ │ │ (vba/) │ │
│ │ • Module1 │ │ • Module1.bas│ │
│ │ • Module2 │ │ • Module2.bas│ │
│ │ • ClassA │ │ • ClassA.cls │ │
│ └──────────────┘ └──────────────┘ │
└──────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ ÉTAPE 2: COMPARAISON │
│ │
│ Module │ Excel Hash │ FS Hash │ Action │
│ ───────────┼────────────┼────────────┼─────────────────── │
│ Module1 │ ABC123 │ ABC123 │ ✅ Identique │
│ Module2 │ DEF456 │ DEF456 │ ✅ Identique │
│ ClassA │ GHI789 │ ─ │ 📥 Import dans FS │
│ Module3 │ ─ │ JKL012 │ 📤 Export vers Excel │
│ OldMod │ MNO345 │ MNO345_old │ ⚠️ Conflit détecté │
└──────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ ÉTAPE 3: RÉSOLUTION (si conflits) │
│ │
│ ⚠️ Conflit détecté sur "OldMod" │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Le module "OldMod" est différent des deux côtés │ │
│ │ │ │
│ │ Excel: modifié le 28/02/2026 à 10:30 │ │
│ │ Fichier: modifié le 28/02/2026 à 11:45 │ │
│ │ │ │
│ │ Que voulez-vous faire ? │ │
│ │ │ │
│ │ [1] Garder la version Excel (→ écraser fichier) │ │
│ │ [2] Garder la version fichier (→ écraser Excel) │ │
│ │ [3] Ignorer (ne rien faire) │ │
│ └──────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ ÉTAPE 4: APPLICATION DES CHANGEMENTS │
│ │
│ Excel ───────────────────────► File System │
│ • ClassA.cls créé │
│ │
│ File System ─────────────────► Excel │
│ • Module3 importé │
│ • OldMod mis à jour (selon choix utilisateur) │
│ │
│ ✅ Synchronisation initiale terminée ! │
└──────────────────────────────────────────────────────────────┘Mode unidirectionnel (par défaut): File System → Excel
Par défaut, seuls les changements du système de fichiers vers Excel sont synchronisés.
SYSTÈME DE FICHIERS (vba/) EXCEL
│ │
│ 1. Création d'un fichier .bas/.cls │
│ └── NouveauModule.bas │
│────────────────────────────────────────────►│
│ │
│ 2. Import automatique dans Excel │
│ → Module créé avec le code │
│ ▼
│ ┌───────────────┐
│ │ NouveauModule │
│ │ (dans VBA) │
│ └───────────────┘
│
│ 3. Modification du fichier
│ └── Contenu changé
│────────────────────────────────────────────►
│
│ 4. Excel mis à jour automatiquement
│ → Code remplacé
│ ▼
│ ┌───────────────┐
│ │ Code mis à │
│ │ jour dans │
│ │ le module │
│ └───────────────┘
│
│ 5. Suppression du fichier
│ └── Fichier supprimé
│────────────────────────────────────────────►
│
│ 6. Module supprimé dans Excel
│ ▼
│ ┌───────────────┐
│ │ Module │
│ │ supprimé ❌ │
│ └───────────────┘
│
▼
⚠️ IMPORTANT: Les modifications faites DIRECTEMENT dans Excel
ne sont PAS synchronisées vers le système de fichiers
en mode unidirectionnel !Mode bidirectionnel: File System ↔ Excel (--excel-to-fs)
Avec le flag --excel-to-fs, la synchronisation fonctionne dans les deux sens.
┌─────────────────────────────┐
│ SYNCHRONISATION │
│ BIDIRECTIONNELLE │
│ (--excel-to-fs) │
└─────────────────────────────┘
│
┌──────────────────────────────┼──────────────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐
│ FILE SYSTEM → EXCEL│ │ GESTIONNAIRE DE │ │ EXCEL → FILE SYSTEM│
│ (événements FS) │ │ CONFLITS LWW │ │ (polling 2s) │
└─────────────────────┘ │ (Last Write Wins) │ └─────────────────────┘
│ └─────────────────────┘ │
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ add → import │ │ │ │ export vers │
│ change→update│◄──────────►│ Résolution │◄──────────►│ fichier .bas │
│ unlink→delete│ │ conflits │ │ ou .cls │
└──────────────┘ └──────────────┘ └──────────────┘Flux détaillé du mode bidirectionnel
EXCEL (VBA Editor) SYSTÈME DE FICHIERS (vba/)
│ │
│ 1. Vous modifiez du code │
│ dans Excel │
│ │
▼ │
┌───────────────┐ │
│ Code changé │ │
│ dans Module1 │ │
└───────┬───────┘ │
│ │
│ 2. Polling Excel toutes les 2s │
│ détecte le changement │
│ │
└────────────────────────────────────────►│
│
│ 3. Comparaison des timestamps
│ Excel: 11:30:15
│ Fichier: 11:30:00
│
│ 4. Excel est PLUS RÉCENT
│ → Export vers fichier
│
▼
┌────────────────┐
│ Module1.bas │
│ mis à jour │
│ (code Excel) │
└────────────────┘
────────────────────────────────────────────────────────────────────────────
EXCEL (VBA Editor) SYSTÈME DE FICHIERS (vscode/)
│ │
│ │ 1. Vous modifiez le fichier
│ │ dans VS Code
│ │
│ ▼
│ ┌────────────────┐
│ │ Module1.bas │
│ │ modifié │
│ └───────┬────────┘
│ │
│ 2. Événement fichier détecté │
│◄───────────────────────────────────────┘
│
│ 3. Comparaison des timestamps
│ Fichier: 11:35:20
│ Excel: 11:30:15
│
│ 4. Fichier est PLUS RÉCENT
│ → Import dans Excel
▼
┌────────────────┐
│ Module1 │
│ mis à jour │
│ (code fichier) │
└────────────────┘Gestion des conflits (Last Write Wins)
Quand une modification est détectée des deux côtés simultanément, le système utilise l'algorithme LWW (Last Write Wins).
CONFLIT DÉTECTÉ
│
▼
┌─────────────────────────────┐
│ Comparaison timestamps │
│ (dernière modification) │
└──────────────┬──────────────┘
│
┌──────────────┼──────────────┐
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ Excel PLUS RÉCENT│ │ Fichier PLUS │
│ (date_excel > │ │ RÉCENT │
│ date_fichier) │ │ (date_fichier > │
└────────┬────────┘ │ date_excel) │
│ └────────┬────────┘
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ Excel gagne │ │ Fichier gagne │
│ → Export vers │ │ → Import dans │
│ fichier │ │ Excel │
└─────────────────┘ └─────────────────┘
│ │
└──────────────┬──────────────┘
│
▼
┌─────────────────────────────┐
│ ÉGALITÉ (dates identiques) │
│ │
│ → File System gagne │
│ (valeur par défaut) │
└─────────────────────────────┘Tableau récapitulatif des actions
| Événement | Source | Direction | Action dans l'autre système | | -------------------------------- | ----------- | ------------ | ------------------------------------------------- | | Création fichier (.bas/.cls) | File System | FS → Excel | Import dans Excel (nouveau module VBA) | | Modification fichier | File System | FS → Excel | Mise à jour du code dans le module Excel existant | | Suppression fichier | File System | FS → Excel | Suppression du module VBA dans Excel | | Création module dans VBA | Excel | Excel → FS* | Export vers nouveau fichier .bas/.cls | | Modification code dans VBA | Excel | Excel → FS* | Mise à jour du fichier existant | | Suppression module dans VBA | Excel | Excel → FS* | Suppression du fichier |
Note: Le flux
Excel → File System(marqué *) nécessite le flag--excel-to-fs
Anti-boucle et protection
Pour éviter les boucles infinies de synchronisation, le système utilise un mécanisme de suppression temporaire des événements rebonds.
Sans protection (BOUCLE INFINIE):
─────────────────────────────────
1. Vous modifiez Module1.bas dans VS Code
│
▼
2. Fichier changé → Import dans Excel
│
▼
3. Excel modifié → Export vers fichier
│
▼
4. Fichier changé → Import dans Excel
│
▼
5. Excel modifié → Export vers fichier
│
▼
... ET ÇA CONTINUE À L'INFINI !
Avec protection (ANTI-BOUCLE):
──────────────────────────────
1. Vous modifiez Module1.bas dans VS Code
│
▼
2. Fichier changé → Import dans Excel
│
├──► 🚫 Marque "ignoreNextExcelChange"
│
▼
3. Excel modifié → VÉRIFICATION
│
├──► ✅ "ignoreNextExcelChange" est actif
│ → Ne pas propager vers fichier
│ → 🗑️ Supprime le flag
│
▼
4. ✅ Pas de boucle ! Modification terminée.Sécurité: Verrouillage sur le classeur actif
Le système est verrouillé sur le classeur actif au démarrage pour éviter les erreurs.
Démarrage
│
▼
┌────────────────────────────────────┐
│ Capturer ID du classeur actif │
│ (nom + chemin complet) │
└──────────────┬─────────────────────┘
│
▼
┌────────────────────────────────────┐
│ Watcher démarré │
└──────────────┬─────────────────────┘
│
▼
Événement détecté
│
▼
┌────────────────┐
│ Classeur actif │
│ est-il le même │
│ qu'au démarrage│
└───────┬────────┘
│
┌────────┴────────┐
│ │
▼ ▼
┌──────────┐ ┌──────────────┐
│ OUI │ │ NON │
│ │ │ │
│ Traiter │ │ 🛑 ARRÊT │
│ l'event │ │ IMMÉDIAT │
│ │ │ │
│ │ │ Protection │
│ │ │ contre │
│ │ │ écriture sur │
│ │ │ mauvais │
│ │ │ fichier ! │
└──────────┘ └──────────────┘Résumé visuel du flux complet
┌────────────────────────────────────────────────────────────────────────────┐
│ FLUX COMPLET DU PROGRAMME │
└────────────────────────────────────────────────────────────────────────────┘
┌──────────────┐
│ LANCEMENT │
└──────┬───────┘
│
▼
┌──────────────────────┐
│ Synchronisation │
│ initiale │
│ (scan + résolution) │
└──────────┬───────────┘
│
▼
┌──────────────┐
│ Choix mode │
└──────┬───────┘
│
┌──────┴──────┐
│ │
▼ ▼
┌────────┐ ┌─────────────┐
│ Mode │ │ Mode │
│ Uni │ │ Bidirection │
│(défaut)│ │(--excel-to- │
│ │ │ fs) │
└───┬────┘ └──────┬──────┘
│ │
│ ┌─────────┘
│ │
▼ ▼
┌──────────────────────────────────────────────────────────────┐
│ WATCHER ACTIF │
│ │
│ ┌─────────────────┐ ┌──────────────────────────┐ │
│ │ File System │ │ Excel (si --excel-to-fs) │ │
│ │ • add file │ │ • Polling toutes les 2s │ │
│ │ • change file │ │ • Détection changements │ │
│ │ • delete file │ │ code VBA │ │
│ └────────┬────────┘ └────────────┬─────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ COMPARAISON + RÉSOLUTION LWW │ │
│ │ (Last Write Wins, égalité → File System) │ │
│ └──────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ SYNCHRONISATION CIBLE │ │
│ │ │ │
│ │ File System ───────────► Excel (import) │ │
│ │ Excel ─────────────────► File System (export) │ │
│ │ │ │
│ └──────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────┘
│
│ Ctrl+C ou erreur
▼
┌──────────────┐
│ ARRÊT │
└──────────────┘Exemple de session typique
$ pnpm run dev -- --excel-to-fs
🚀 Démarrage Excel Sync (mode bidirectionnel)
═══════════════════════════════════════════════════════════════
📊 Étape 1/3: Connexion à Excel...
✅ Connecté au classeur: MonProjet.xlsm
🔍 Étape 2/3: Scan initial...
Excel: 3 modules trouvés
Fichiers: 3 fichiers trouvés dans vba/
🔄 Étape 3/3: Comparaison...
✅ Module1: identique
✅ Module2: identique
⚠️ ClassA: différence détectée
┌────────────────────────────────────────────────────────────┐
│ Conflit détecté sur "ClassA" │
│ │
│ Excel: modifié le 28/02/2026 10:30:15 │
│ Fichier: modifié le 28/02/2026 10:35:42 │
│ │
│ [1] Garder Excel [2] Garder fichier [3] Ignorer │
└────────────────────────────────────────────────────────────┘
➤ Choix: 2
✅ ClassA: synchronisé (fichier → Excel)
✨ Synchronisation initiale terminée !
👁️ Watcher démarré (surveillance active):
• File System → Excel: ACTIVÉ
• Excel → File System: ACTIVÉ (--excel-to-fs)
───────────────────────────────────────────────────────────────
📝 11:45:23 - [FS → Excel] Module1.bas modifié → Excel mis à jour
📝 11:47:15 - [Excel → FS] ClassA.cls modifié dans VBA → Fichier exporté
📝 11:50:01 - [FS → Excel] NouveauModule.bas créé → Import dans Excel
───────────────────────────────────────────────────────────────
👋 Arrêt demandé (Ctrl+C)
🛑 Watcher arrêté proprement