bundleDependencies d'un paquet *workspace* est instable selon l'environnement npm
(mode -w, exécution en root sur un runner CI) : npm voyait shared comme un lien
workspace et n'embarquait AUCUN fichier ("bundled files: 0"), publiant un tarball
cassé — `Cannot find module '@arboretum/shared'` au démarrage chez le consommateur.
C'est ce que détectait (à juste titre) le job pack-smoke.
Nouveau mécanisme, identique sur tout environnement : scripts/inline-shared.mjs
(hook prepack) copie le JS compilé de shared dans dist/_shared/ et réécrit l'import
bare '@arboretum/shared' des .js du serveur vers ce chemin relatif. shared passe en
devDependency (résolu en dev via le symlink workspace, jamais exigé du consommateur).
Plus de node_modules embarqué ni de symlink dans le tarball.
ci.yml (pack-smoke) packe désormais en -w (comme release.yml) et asserte l'autonomie
(dist/_shared présent + zéro import bare) avant le boot smoke.
Validé de bout en bout sur checkout propre : npm ci + build + pack -w + install dans
projet vierge + boot -> HTTP 401, et 263 tests verts.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
72 lines
3.4 KiB
JavaScript
72 lines
3.4 KiB
JavaScript
#!/usr/bin/env node
|
|
// Inline @arboretum/shared (paquet workspace NON publié) directement dans le dist du serveur,
|
|
// pour que le tarball npm soit 100 % autonome. Branché sur le hook "prepack".
|
|
//
|
|
// Pourquoi PAS bundleDependencies : embarquer une dépendance qui est aussi un *workspace* via
|
|
// bundleDependencies est instable selon l'environnement npm (mode -w, exécution en root sur un
|
|
// runner CI, version d'arborist) — npm voit le nœud comme un lien workspace et n'embarque parfois
|
|
// AUCUN fichier ("bundled files: 0"), produisant un paquet cassé chez le consommateur. On élimine
|
|
// donc toute magie de bundling : on copie le JS compilé de shared dans dist/_shared et on réécrit
|
|
// l'import bare '@arboretum/shared' du serveur vers ce chemin relatif. Zéro node_modules embarqué,
|
|
// zéro symlink, résultat identique partout.
|
|
import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
import { join, dirname, relative, sep } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const serverDir = join(dirname(fileURLToPath(import.meta.url)), '..');
|
|
const serverDist = join(serverDir, 'dist');
|
|
const sharedDist = join(serverDir, '..', 'shared', 'dist');
|
|
const inlineDir = join(serverDist, '_shared');
|
|
|
|
for (const [label, p] of [['dist serveur', serverDist], ['dist shared', sharedDist]]) {
|
|
if (!existsSync(p)) {
|
|
console.error(`inline-shared: ${label} introuvable (${p}) — lance "npm run build" avant le pack.`);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
// 1) Copier le JS compilé de shared dans dist/_shared (uniquement *.js : seul le runtime compte ;
|
|
// les .d.ts/.map ne sont de toute façon pas publiés via le glob `files`). index.js réexporte
|
|
// ./protocol.js et ./api.js en relatif → la copie complète préserve la résolution interne.
|
|
rmSync(inlineDir, { recursive: true, force: true });
|
|
mkdirSync(inlineDir, { recursive: true });
|
|
let copied = 0;
|
|
for (const name of readdirSync(sharedDist)) {
|
|
if (name.endsWith('.js')) {
|
|
cpSync(join(sharedDist, name), join(inlineDir, name));
|
|
copied++;
|
|
}
|
|
}
|
|
if (copied === 0) {
|
|
console.error(`inline-shared: aucun .js dans ${sharedDist} — shared n'est pas compilé.`);
|
|
process.exit(1);
|
|
}
|
|
|
|
// 2) Réécrire l'import bare '@arboretum/shared' de chaque .js du serveur vers le chemin relatif
|
|
// (POSIX) pointant sur dist/_shared/index.js, calculé par fichier (profondeur variable).
|
|
const walk = (dir) =>
|
|
readdirSync(dir, { withFileTypes: true }).flatMap((e) => {
|
|
const p = join(dir, e.name);
|
|
if (e.isDirectory()) return p === inlineDir ? [] : walk(p); // ne pas se réécrire soi-même
|
|
return e.name.endsWith('.js') ? [p] : [];
|
|
});
|
|
|
|
let rewritten = 0;
|
|
for (const file of walk(serverDist)) {
|
|
const src = readFileSync(file, 'utf8');
|
|
if (!src.includes('@arboretum/shared')) continue;
|
|
let rel = relative(dirname(file), join(inlineDir, 'index.js')).split(sep).join('/');
|
|
if (!rel.startsWith('.')) rel = `./${rel}`;
|
|
const out = src.replaceAll(`'@arboretum/shared'`, `'${rel}'`).replaceAll(`"@arboretum/shared"`, `"${rel}"`);
|
|
if (out !== src) {
|
|
writeFileSync(file, out);
|
|
rewritten++;
|
|
}
|
|
}
|
|
if (rewritten === 0) {
|
|
console.error(`inline-shared: aucun import '@arboretum/shared' réécrit dans ${serverDist} — build manquant ?`);
|
|
process.exit(1);
|
|
}
|
|
|
|
console.log(`inline-shared: ${copied} fichier(s) shared -> dist/_shared, import réécrit dans ${rewritten} fichier(s) serveur`);
|