Skip to content

fix(core): declare 6 phantom deps + bump 0.3.2#172

Merged
remigit55 merged 4 commits into
masterfrom
fix/core-declare-missing-deps-0.3.2
Jun 21, 2026
Merged

fix(core): declare 6 phantom deps + bump 0.3.2#172
remigit55 merged 4 commits into
masterfrom
fix/core-declare-missing-deps-0.3.2

Conversation

@remigit55

Copy link
Copy Markdown
Collaborator

Problème

@blueprint-modular/core@0.3.1 importait (via les composants bpm.* re-exportés) plusieurs modules non déclarés dans son package.json. Ces deps « fantômes » causaient des Module not found dans les apps générées par Maker — contournés temporairement côté Maker (PR #286, package-template.ts). Cette PR réalise le niveau 2 : le core déclare lui-même ses deps.

Changements

packages/core/package.json uniquement :

  • Version 0.3.10.3.2
  • Ajout au bloc dependencies des 6 deps épinglées :
"highlight.js": "11.11.1",
"idb-keyval": "6.2.5",
"qrcode.react": "4.2.0",
"react-markdown": "10.1.0",
"rehype-highlight": "7.0.2",
"remark-gfm": "4.0.1"

Versions identiques au template Maker (alignement garantit que les apps existantes n'ont pas de conflit).

Choix dependencies vs peerDependencies

Ces 6 packages sont marqués external dans vite.config.ts (non bundlés), mais contrairement à leaflet/react-leaflet (optionnels, feature-flaggés), ils sont requis par des composants core non-optionnels (QRCode, Markdown, etc.). Les déclarer en dependencies permet à npm/pnpm de les installer automatiquement côté consumer sans configuration supplémentaire. leaflet/react-leaflet restent en peerDependencies optionnelles — inchangées.

Preuve d'usage

Les 6 packages sont listés comme external dans packages/core/vite.config.ts (lignes 39–47), ce qui confirme qu'ils sont importés mais non bundlés. Les composants consommateurs : components/bpm/QRCode.tsx, components/bpm/Markdown.tsx, components/AIChat/AIChat.tsx, modules wiki (idb-keyval).

Périmètre

  • Aucune modification de code source, composants, build config.
  • Aucun npm publish — étape manuelle Rémi (Windows + Dashlane).

Séquence post-merge pour Rémi

  1. Merger cette PR.
  2. git checkout master && git pull sur Windows.
  3. cd packages/core && npm publish --access public
  4. Vérifier : npm view @blueprint-modular/core version0.3.2
  5. PR séparée sur blueprint-maker (après publication) : repointer package-template.ts vers 0.3.2 et retirer les 6 lignes devenues redondantes. Recapture fixtures G5/G6 obligatoire.

Generated by Claude Code

claude added 3 commits June 21, 2026 21:57
…gfm, rehype-highlight, highlight.js, idb-keyval, qrcode.react), bump 0.3.2

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… (Scenario A)

qrcode.react declares react as peerDep — no embedded copy. In the gate
environment, react is installed both at packages/core/node_modules (devDeps)
and at repo root (gate installs it for tsc). Vite/vitest resolves ESM for
qrcode.react to the root copy while react-dom resolves locally → two React
instances → invalid hook call.

In a generated app all packages are hoisted to a single root node_modules,
so this double-instance cannot occur in production. Fix: add resolve.dedupe
for react/react-dom in vitest.config.ts to force a single instance in tests,
matching the real-app hoisting behaviour.

package-lock.json updated to reflect the 6 new deps declared in package.json.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Copy link
Copy Markdown
Collaborator Author

CI débloqueé — Gate GREEN ✅

Scénario retenu : A — artefact de test (pas un défaut de packaging)

Preuve :

  • qrcode.react déclare react en peerDependencies (^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0) — aucune copie de React embarquée.
  • Dans l'environnement gate, React se retrouve en deux endroits : packages/core/node_modules/react (devDeps du core) ET node_modules/react (racine, installé par le gate pour tsc). Vite/vitest résolvait l'ESM de qrcode.react vers la copie racine tandis que react-dom pointait sur la copie locale → deux instances distinctes en mémoire → Invalid hook call.
  • En app générée, tous les packages sont hoistés vers un seul node_modules racine → une seule instance React → pas de conflit possible.

Correction appliquée (packages/core/vitest.config.ts) :

resolve: {
  dedupe: ["react", "react-dom", "react/jsx-runtime", "react/jsx-dev-runtime"],
}

Force vitest à utiliser une seule instance de React dans les tests, aligné sur le comportement réel d'une app générée.

Les 6 deps restent en dependencies — c'est correct : elles sont requises par des composants non-optionnels du core et déclarées external au build (non bundlées), ce qui garantit qu'une seule copie est utilisée en app.

Fichiers modifiés (3 commits)

  1. packages/core/package.json — 6 deps + bump 0.3.2 (commit initial)
  2. lib/generated/versions.json, public/llms.txt, public/llms-core.txt — régénérés pour 0.3.2
  3. packages/core/vitest.config.ts + packages/core/package-lock.json — fix dedupe + lockfile à jour

Résultat gate local

✅  GATE GREEN — all checks passed
230/230 tests smoke (dont bpm.qrCode renders without throw ✓)

Prochaine étape pour Rémi : merger cette PR → npm publish --access public depuis Windows → vérifier npm view @blueprint-modular/core version0.3.2. Ensuite PR séparée sur blueprint-maker pour retirer les 6 lignes devenues redondantes dans package-template.ts.


Generated by Claude Code

…(inline) — qrcode.react smoke green in CI

resolve.dedupe was insufficient: qrcode.react ESM is externalized by vitest
by default (loaded via Node native loader, bypassing Vite's module graph).
In a fresh CI environment, npm install in packages/core creates
packages/core/node_modules/react, then the gate installs react at root too.
Node resolves 'react' from qrcode.react's location to the root copy, while
react-dom uses packages/core's copy → two distinct React instances in memory
→ "Cannot read properties of null (reading 'useMemo')".

Fix: server.deps.inline: ['qrcode.react'] forces vitest to transform
qrcode.react through Vite instead of externalizing it. Vite's module graph
then applies resolve.dedupe, ensuring a single React instance regardless of
how many physical copies exist on disk. Verified: crash reproduced with two
React copies present, fix makes 230/230 pass in that exact condition.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Copy link
Copy Markdown
Collaborator Author

Fix itéré — pourquoi resolve.dedupe était insuffisant en CI

Pourquoi le tour précédent échouait en CI malgré le gate local vert

Local : node_modules/ racine déjà peuplé avant l'exécution du gate → npm install dans packages/core ne crée PAS de packages/core/node_modules/react (npm voit que react est déjà hoisté à la racine). Une seule copie physique de React → pas de conflit, resolve.dedupe inutile mais inoffensif → gate vert.

CI (env vierge) : aucun node_modules préexistant. Le gate fait :

  1. npm install dans packages/core → crée packages/core/node_modules/react + packages/core/node_modules/qrcode.react
  2. npm install --no-save react react-dom à la racine → crée node_modules/react racine

Résultat : deux copies physiques de React. resolve.dedupe ne s'applique qu'aux modules traités par le graphe Vite. Or qrcode.react est externalisé par défaut dans vitest (chargé par le loader natif Node/ESM, hors graphe Vite) → Node résout react depuis la localisation de qrcode.react et remonte jusqu'à la copie racine, tandis que react-dom utilise la copie packages/core. Deux instances distinctes en mémoire → useMemo null → crash.

Preuve de reproduction locale

Avec les deux copies physiques présentes, j'ai reproduit le crash exact (même chemins que le log CI) :

✗ bpm.qrCode renders without throw
→ Cannot read properties of null (reading 'useMemo')
  at /home/user/.../node_modules/react/cjs/react.development.js  ← ROOT react
  ↕ incompatible avec react-dom de packages/core/node_modules/

Correction appliquée (packages/core/vitest.config.ts)

test: {
  server: {
    deps: {
      inline: ["qrcode.react"],
    },
  },
},

server.deps.inline force vitest à transformer qrcode.react via Vite (au lieu de l'externaliser). Une fois dans le graphe Vite, resolve.dedupe: ['react', 'react-dom'] s'applique → instance React unique, quelle que soit la topologie des node_modules sur disque.

Validation locale avec la condition CI reproduite

Crash reproduit ✓ → fix appliqué → 230/230 tests passent avec deux copies physiques de React présentes, dont bpm.qrCode renders without throw ✓.

node scripts/gate.cjs (commande identique à la CI) : GATE GREEN.


Prochain run CI attendu vert. Si c'est le cas → merge → npm publish --access public depuis Windows → npm view @blueprint-modular/core version = 0.3.2.


Generated by Claude Code

@remigit55
remigit55 merged commit ccbadc1 into master Jun 21, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants