diff --git a/.github/workflows/develop.yml b/.github/workflows/develop.yml index c75df819..8257adfb 100644 --- a/.github/workflows/develop.yml +++ b/.github/workflows/develop.yml @@ -8,18 +8,26 @@ on: branches: - develop +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + jobs: # ---------------------------------------------------------------- - # Job 1 — Tests unitaires et intégration (Vitest jsdom) + # Job 1 — Lint (ESLint, non destructif) # ---------------------------------------------------------------- - unit-and-integration: - name: Tests Vitest (jsdom) — Node 24 + lint: + name: Lint — Node 24 runs-on: ubuntu-latest + timeout-minutes: 10 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Setup Node 24 - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version: '24' cache: 'npm' @@ -27,20 +35,21 @@ jobs: - name: Install dependencies run: npm ci - - name: Run tests - run: NODE_ENV=test npm test + - name: Lint check + run: npm run lint:check # ---------------------------------------------------------------- - # Job 2 — Tests web components en browser mode (Vitest + Playwright) + # Job 2 — TypeScript strict # ---------------------------------------------------------------- - web-components: - name: Web components (browser mode) — Node 24 + typecheck: + name: TypeScript strict — Node 24 runs-on: ubuntu-latest + timeout-minutes: 10 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Setup Node 24 - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version: '24' cache: 'npm' @@ -48,23 +57,21 @@ jobs: - name: Install dependencies run: npm ci - - name: Install Playwright chromium - run: npx playwright install chromium --with-deps - - - name: Run web component tests - run: npm run test:components + - name: TypeScript strict check + run: npx tsc --noEmit # ---------------------------------------------------------------- - # Job 3 — Tests E2E Playwright + # Job 3 — Vite build (production) # ---------------------------------------------------------------- - e2e: - name: E2E Playwright — Node 24 + build-check: + name: Vite build — Node 24 runs-on: ubuntu-latest + timeout-minutes: 10 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Setup Node 24 - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version: '24' cache: 'npm' @@ -72,23 +79,23 @@ jobs: - name: Install dependencies run: npm ci - - name: Install Playwright chromium - run: npx playwright install chromium --with-deps - - - name: Run E2E tests - run: npm run test:e2e + - name: Build (production) + run: npm run build # ---------------------------------------------------------------- - # Job 4 — Coverage + # Job 4 — Coverage (jsdom + browser + merge) : seul job qui lance les + # suites Vitest. Remplace les anciens jobs unit-and-integration et + # web-components pour ne plus exécuter chaque suite 2× par run (étape 16). # ---------------------------------------------------------------- coverage: name: Coverage check — Node 24 runs-on: ubuntu-latest + timeout-minutes: 20 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Setup Node 24 - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version: '24' cache: 'npm' @@ -96,23 +103,40 @@ jobs: - name: Install dependencies run: npm ci - - name: Install Playwright chromium - run: npx playwright install chromium --with-deps + - name: Get Playwright version + id: playwright-version + run: echo "version=$(node -p "require('./package-lock.json').packages['node_modules/@playwright/test'].version")" >> "$GITHUB_OUTPUT" + + - name: Cache Playwright browsers + id: playwright-cache + uses: actions/cache@v6 + with: + path: ~/.cache/ms-playwright + key: playwright-${{ runner.os }}-${{ steps.playwright-version.outputs.version }} + + - name: Install Playwright chromium (cold) + if: steps.playwright-cache.outputs.cache-hit != 'true' + run: npx playwright install --with-deps chromium + + - name: Install Playwright OS dependencies (cache hit) + if: steps.playwright-cache.outputs.cache-hit == 'true' + run: npx playwright install-deps chromium - - name: Run coverage (thresholds enforced in vitest.config.ts) + - name: Run coverage (jsdom + browser + merge — thresholds enforced in vitest.config.ts) run: NODE_ENV=test npm run coverage # ---------------------------------------------------------------- - # Job 5 — Vite build (production) + # Job 5 — Tests E2E Playwright # ---------------------------------------------------------------- - build-check: - name: Vite build — Node 24 + e2e: + name: E2E Playwright — Node 24 runs-on: ubuntu-latest + timeout-minutes: 15 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Setup Node 24 - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version: '24' cache: 'npm' @@ -120,20 +144,40 @@ jobs: - name: Install dependencies run: npm ci - - name: Build (production) - run: npm run build + - name: Get Playwright version + id: playwright-version + run: echo "version=$(node -p "require('./package-lock.json').packages['node_modules/@playwright/test'].version")" >> "$GITHUB_OUTPUT" + + - name: Cache Playwright browsers + id: playwright-cache + uses: actions/cache@v6 + with: + path: ~/.cache/ms-playwright + key: playwright-${{ runner.os }}-${{ steps.playwright-version.outputs.version }} + + - name: Install Playwright chromium (cold) + if: steps.playwright-cache.outputs.cache-hit != 'true' + run: npx playwright install --with-deps chromium + + - name: Install Playwright OS dependencies (cache hit) + if: steps.playwright-cache.outputs.cache-hit == 'true' + run: npx playwright install-deps chromium + + - name: Run E2E tests + run: npm run test:e2e # ---------------------------------------------------------------- - # Job 6 — TypeScript strict + # Job 6 — Smoke test du serveur Express # ---------------------------------------------------------------- - typecheck: - name: TypeScript strict — Node 24 + server-smoke: + name: Server smoke test — Node 24 runs-on: ubuntu-latest + timeout-minutes: 5 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Setup Node 24 - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version: '24' cache: 'npm' @@ -141,20 +185,45 @@ jobs: - name: Install dependencies run: npm ci - - name: TypeScript strict check - run: npx tsc --noEmit + - name: Start server in background + run: npm run smoke:server & + + - name: Wait for server on port 3001 + run: npx wait-on tcp:3001 --timeout 30000 + + - name: Check server responds (socket.io handshake) + run: | + status=$(curl -s -o /tmp/smoke.out -w '%{http_code}' http://localhost:3001/socket.io/) + echo "HTTP status: $status" + cat /tmp/smoke.out + echo + if [ "$status" != "400" ] || ! grep -q "Transport unknown" /tmp/smoke.out; then + echo "::error::Réponse inattendue de /socket.io/ (status=$status) — le serveur ne répond pas correctement" + exit 1 + fi # ---------------------------------------------------------------- - # Job 7 — Smoke test du serveur Express + # Job 7 — Déploiement de la documentation VitePress (GitHub Pages) + # Uniquement sur push develop, et seulement si toute la CI est verte. # ---------------------------------------------------------------- - server-smoke: - name: Server smoke test — Node 24 + docs-deploy: + name: Deploy docs — GitHub Pages + if: github.event_name == 'push' && github.ref == 'refs/heads/develop' + needs: [lint, typecheck, build-check, coverage, e2e, server-smoke] runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: read + pages: write + id-token: write + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Setup Node 24 - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version: '24' cache: 'npm' @@ -162,14 +231,17 @@ jobs: - name: Install dependencies run: npm ci - - name: Start server in background - run: npm run smoke:server & + - name: Build docs (API reference + VitePress) + run: npm run docs:build - - name: Wait for server on port 3001 - run: npx wait-on tcp:3001 --timeout 30000 + - name: Configure GitHub Pages + uses: actions/configure-pages@v6 - - name: Check server responds - run: curl -sf http://localhost:3001/ || curl -sf http://localhost:3001/socket.io/ || true + - name: Upload docs artifact + uses: actions/upload-pages-artifact@v5 + with: + path: docs-dist - - name: Server started successfully - run: echo "Server smoke test passed — port 3001 is open" + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v5 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..dc92d8fd --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,76 @@ +name: Release + +on: + push: + tags: + - 'v*' + +permissions: + contents: read + +jobs: + # ---------------------------------------------------------------- + # Job 1 — Build + test avant toute release (pas de tag cassé) + # ---------------------------------------------------------------- + build-and-test: + name: Build + test — Node 24 + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/checkout@v7 + + - name: Setup Node 24 + uses: actions/setup-node@v6 + with: + node-version: '24' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: TypeScript strict check + run: npx tsc --noEmit + + - name: Lint check + run: npm run lint:check + + - name: Run tests + run: NODE_ENV=test npm test + + - name: Build (production) + run: npm run build + + # ---------------------------------------------------------------- + # Job 2 — Release GitHub avec notes tirées du CHANGELOG (pas de publish npm, + # le projet n'est pas une lib — cf. étape 02) + # ---------------------------------------------------------------- + release: + name: Create GitHub release + needs: build-and-test + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: write + steps: + - uses: actions/checkout@v7 + + - name: Extract CHANGELOG section for this tag + id: changelog + run: | + version="${GITHUB_REF_NAME#v}" + awk -v ver="$version" ' + $0 ~ "^## \\[" ver "\\]" { found=1; next } + found && $0 ~ "^## \\[" { exit } + found { print } + ' CHANGELOG.md > /tmp/release-notes.md + echo "notes_found=$([ -s /tmp/release-notes.md ] && echo true || echo false)" >> "$GITHUB_OUTPUT" + + - name: Create release + env: + GH_TOKEN: ${{ github.token }} + run: | + if [ "${{ steps.changelog.outputs.notes_found }}" = "true" ]; then + gh release create "$GITHUB_REF_NAME" --title "$GITHUB_REF_NAME" --notes-file /tmp/release-notes.md + else + gh release create "$GITHUB_REF_NAME" --title "$GITHUB_REF_NAME" --generate-notes + fi diff --git a/.gitignore b/.gitignore index 512a05b3..86ccbfc3 100644 --- a/.gitignore +++ b/.gitignore @@ -54,7 +54,7 @@ showcase/resources/tc-component/ # Documentation docs-dist -docs-sources/developers/code/parts/ +docs-sources/developers/code/ docs-sources/.vitepress/cache/ # Jetbrains diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4897afac..e25c7993 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -53,11 +53,11 @@ Unfortunately, we are not able to investigate / fix bugs without a minimal repro Before you submit your Pull Request (PR) consider the following guidelines: -1. Search [GitHub](https://github.com/sfeir-open-source/talk-control/pulls) for an open or closed PR +1. Search [GitHub](https://github.com/TalkControl/talk-control/pulls) for an open or closed PR that relates to your submission. You don't want to duplicate effort. 1. Be sure that an issue describes the problem you're fixing, or documents the design for the feature you'd like to add. Discussing the design up front helps to ensure that we're ready to accept your work. -1. Fork the sfeir-open-source/talk-control repo. +1. Fork the TalkControl/talk-control repo. 1. Make your changes in a new git branch, based on our develop branch: ```shell diff --git a/README.md b/README.md index b3b4e32a..5e2a2350 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ # TalkControl -![version](https://img.shields.io/github/package-json/v/sfeir-open-source/talk-control?color=blue) -![license](https://img.shields.io/github/license/sfeir-open-source/talk-control) -![open issues](https://img.shields.io/github/issues-raw/sfeir-open-source/talk-control) -![CI develop](https://github.com/sfeir-open-source/talk-control/actions/workflows/develop.yml/badge.svg?branch=develop) +![version](https://img.shields.io/github/package-json/v/TalkControl/talk-control?color=blue) +![license](https://img.shields.io/github/license/TalkControl/talk-control) +![open issues](https://img.shields.io/github/issues-raw/TalkControl/talk-control) +![CI develop](https://github.com/TalkControl/talk-control/actions/workflows/develop.yml/badge.svg?branch=develop) TalkControl lets speakers remotely control their slideshow from any device — phone, tablet, or second screen — via a web interface synchronized with the presentation. @@ -15,7 +15,7 @@ TalkControl lets speakers remotely control their slideshow from any device — p ## Getting started ```bash -npm install --legacy-peer-deps +npm install npm start ``` @@ -96,7 +96,7 @@ npm run docs:build # Build static docs ## Contribute See [CONTRIBUTING.md](CONTRIBUTING.md) for commit conventions and pull request guidelines. -Issues: [github.com/sfeir-open-source/talk-control/issues](https://github.com/sfeir-open-source/talk-control/issues) +Issues: [github.com/TalkControl/talk-control/issues](https://github.com/TalkControl/talk-control/issues) ## Troubleshoot diff --git a/docs-sources/.vitepress/config.js b/docs-sources/.vitepress/config.js index 6668e844..b7a3b3f0 100644 --- a/docs-sources/.vitepress/config.js +++ b/docs-sources/.vitepress/config.js @@ -14,7 +14,7 @@ export default { '/developers/': [{ text: 'Developer Guide', link: '/developers/' }], }, editLink: { - pattern: 'https://github.com/sfeir-open-source/talk-control/edit/develop/docs-sources/:path', + pattern: 'https://github.com/TalkControl/talk-control/edit/develop/docs-sources/:path', }, sidebarMenuLabel: 'Menu', }, diff --git a/docs-sources/developers/code/index.md b/docs-sources/developers/code/index.md deleted file mode 100644 index 4356bc7e..00000000 --- a/docs-sources/developers/code/index.md +++ /dev/null @@ -1,45 +0,0 @@ -# API Reference - -## Client - -- [engine-resolver](parts/client/engines/engine-resolver.md) -- [generic-client-engine](parts/client/engines/generic-client-engine.md) -- [revealjs-client-engine](parts/client/engines/revealjs-client-engine.md) -- [index](parts/client/tc-component/index.md) -- [tc-component](parts/client/tc-component/tc-component.md) -- [bootstrap](parts/client/tc-controller/bootstrap.md) -- [tc-controller](parts/client/tc-controller/tc-controller.md) - -## Common - -- [event-bus-component](parts/common/event-bus/event-bus-component.md) -- [event-bus-proxy](parts/common/event-bus/event-bus-proxy.md) -- [event-bus-resolver](parts/common/event-bus/event-bus-resolver.md) -- [event-bus](parts/common/event-bus/event-bus.md) -- [event-bus-postmessage](parts/common/event-bus/postmessage/event-bus-postmessage.md) -- [event-bus-websockets-client](parts/common/event-bus/websockets/event-bus-websockets-client.md) -- [event-bus-websockets-server](parts/common/event-bus/websockets/event-bus-websockets-server.md) -- [logger](parts/common/services/logger.md) -- [plugin](parts/common/services/plugin.md) -- [presentation](parts/common/services/presentation.md) -- [url](parts/common/services/url.md) - -## Environment - -- [config-file](parts/environment/services/config-file.md) -- [tunnel](parts/environment/services/tunnel.md) - -## Plugins - -- [plugin-loader](parts/plugins/plugin-loader.md) - -## Server - -- [bootstrap](parts/server/bootstrap.md) -- [patcher.controller](parts/server/controllers/patcher.controller.md) -- [proxy.controller](parts/server/controllers/proxy.controller.md) -- [engine-resolver](parts/server/engines/engine-resolver.md) -- [generic-server-engine](parts/server/engines/generic-server-engine.md) -- [revealjs-server-engine](parts/server/engines/revealjs-server-engine.md) -- [index](parts/server/store/index.md) -- [tc-server](parts/server/tc-server.md) diff --git a/docs/improvements/README.md b/docs/improvements/README.md index e2fdedae..eefc6b39 100644 --- a/docs/improvements/README.md +++ b/docs/improvements/README.md @@ -93,7 +93,7 @@ Garde-fous quel que soit le modèle : exécuter réellement les 6 commandes de v | Étape | Statut | Contenu | |---|---|---| | [15 — Gaps de tests](step-15-test-gaps.md) | ✅ | Modules non testés comblés (plugin.ts, tc-controller, touch-pointer, wrappers, server/bootstrap), helpers factorisés (`test/helpers/dom.ts`), seuils remontés (80/90/70/80), `changeSlide` mort supprimé (IMP-012), doc coverage régénérée | -| [16 — CI refonte](step-16-ci.md) | ⬜ | Lint bloquant, caches Playwright, concurrency, dédup jobs, déploiement docs | +| [16 — CI refonte](step-16-ci.md) | ✅ | Lint bloquant, cache Playwright, dédup jobs (IMP-013), TypeDoc remplace jsdoc-to-markdown (IMP-014), déploiement docs GitHub Pages, release sur tag — run CI réel validé (PR #36) | ### Phase F — Agentic diff --git a/docs/improvements/decisions.md b/docs/improvements/decisions.md index 7cce77d4..59f0c7e8 100644 --- a/docs/improvements/decisions.md +++ b/docs/improvements/decisions.md @@ -600,3 +600,76 @@ pas un test tautologique — cf. `test/client/engines/generic-client-engine.spec le couvrait, donc rien à adapter ailleurs). Couverture de `generic-client-engine.ts` : 4 tests comportementaux ajoutés (init/goToSlide no-op, getSlides() → `[]`, getSlideNotes() → `null`), en plus du test de constructeur préexistant. + +--- + +## IMP-013 — Dédup des jobs tests/coverage en CI + +**Étape :** 16 | **Statut :** ✅ décidé et implémenté (2026-07-13, branche `chore/step-16-ci`) + +### Contexte +`develop.yml` faisait tourner chaque suite Vitest deux fois par run : une fois nue +(`unit-and-integration` pour jsdom, `web-components` pour le mode browser), une fois de plus à +l'intérieur du job `coverage` (`coverage:jsdom` + `coverage:browser` + `coverage:merge`, avec +seuils désactivés sur les deux premiers puis appliqués au merge). Le plan (step-16-ci.md) proposait +deux options : (a) supprimer les jobs nus et faire du job `coverage` le seul qui exécute les +suites, (b) garder les jobs nus rapides sur chaque PR et réserver `coverage` au push `develop`. + +### Décision +**Option (a).** Le job `coverage` remplace `unit-and-integration` et `web-components` : chaque +suite ne tourne plus qu'une fois par run (dans `coverage:jsdom`/`coverage:browser`), au lieu de +deux. Mesuré en local : `NODE_ENV=test npm run coverage` (jsdom + browser + merge) = ~13 s contre +un total antérieur de trois exécutions distinctes (jsdom nue + browser nue + jsdom/browser à +nouveau dans coverage) largement plus long. L'option (b) aurait gardé le feedback PR plus fin +(jobs séparés) mais au prix de dupliquer l'exécution sur chaque push `develop`, sans bénéfice net +puisque le job `coverage` seul reste rapide. + +### Conséquences +7 jobs → 6 (`lint`, `typecheck`, `build-check`, `coverage`, `e2e`, `server-smoke`) + `docs-deploy` +conditionnel. Le gate de seuils (`vitest.config.ts`, 80/90/70/80 depuis l'étape 15) reste porté par +`coverage:merge`, vérifié réellement bloquant en local (seuil `lines` remonté artificiellement à +99 → `ERROR: Coverage for lines (94.43%) does not meet global threshold (99%)`, exit 1, seuils +réels restaurés immédiatement après test). + +--- + +## IMP-014 — Remplacement de jsdoc-to-markdown par TypeDoc + +**Étape :** 16 | **Statut :** ✅ décidé et implémenté (2026-07-13, branche `chore/step-16-ci`) + +### Contexte +`scripts/jsdoc-build.mjs` scannait des `.js` sous `src/` — disparus depuis la migration TypeScript +(lit/event-bus, étapes 10-11). `jsdoc:build` généraient silencieusement 0 fichier (`generated.length +=== 0`, pas d'erreur), donc `docs:build` produisait une doc API vide sans jamais faire échouer la CI. +Vérification empirique avant de corriger : `jsdoc-to-markdown` (donc `jsdoc` en dessous) ne parse pas +la syntaxe TypeScript — passer un fichier `.ts` réel produit `SyntaxError: This experimental syntax +requires enabling one of the following parser plugin(s): "flow", "typescript"`, sans option exposée +par `jsdoc-to-markdown` pour activer ce plugin babel. Ce n'est donc pas un scan de mauvaise extension +à corriger : jsdoc est structurellement incompatible avec `src/` 100 % TypeScript. + +### Options +- **(a)** Patcher `jsdoc-to-markdown`/`jsdoc` pour accepter le TS (pas d'option exposée, aurait + nécessité de forker ou monkey-patcher une dépendance — contraire à KISS). +- **(b)** Transpiler `src/**/*.ts` vers du JS en conservant les commentaires JSDoc avant de les + passer à `jsdoc-to-markdown` — pipeline supplémentaire fragile, réinvente ce que TypeDoc fait + déjà nativement. +- **(c)** Remplacer l'outil par **TypeDoc + `typedoc-plugin-markdown`** — support TS natif (c'est + le compilateur TypeScript lui-même), génère du Markdown consommable par VitePress. + +### Décision +**(c).** `typedoc@0.28.20` + `typedoc-plugin-markdown@4.12.0` (peer-compatibles, versions courantes +au moment de l'étape). `scripts/jsdoc-build.mjs` réécrit pour utiliser l'API programmatique +(`Application.bootstrapWithPlugins` + `generateOutputs`), même nom de fichier/scripts npm conservés +(`jsdoc:build`, `docs:build`) pour ne pas propager un renommage dans le reste de la documentation +historique qui référence ces noms (`docs/migration/`, `docs/improvements/`). `entryFileName: +'index.md'` + `readme: 'none'` pour rester compatible avec le routing VitePress (`index.md` comme +page de dossier) sans dupliquer le contenu du README racine. + +### Conséquences +179 pages Markdown générées (contre 0 avant), `docs:build` produit une doc API non vide et +consommable par VitePress. `docs-sources/developers/code/` devient entièrement gitignored +(généré à chaque build, y compris son `index.md` qui était committé par erreur auparavant). +Bug additionnel découvert et corrigé au passage : `docs:build` écrivait `docs-dist` **hors du +repo** (`--outDir ../docs-dist` résolu relativement au cwd du script npm — la racine du repo — +et non à `docs-sources` comme l'intention du script le suggérait), rendant tout déploiement +impossible ; corrigé en `--outDir docs-dist`. diff --git a/docs/improvements/step-16-ci.md b/docs/improvements/step-16-ci.md index fb43a9f2..3db05eb8 100644 --- a/docs/improvements/step-16-ci.md +++ b/docs/improvements/step-16-ci.md @@ -73,3 +73,83 @@ Le `jsdoc:build` est aussi cassé (scanne des `.js` disparus) — à corriger ic - Le déploiement docs nécessite les bons `permissions` (`pages: write`, `id-token: write`) — le documenter. - Tester le workflow sur une PR de la branche elle-même (la CI se déclenche sur `develop`, donc la PR de cette étape valide directement les changements). + +## Résultats de validation (2026-07-13, branche `chore/step-16-ci`, PR #36) + +### Écart supplémentaire découvert (non listé dans le plan initial) + +Le repo `TalkControl/talk-control` (l'organisation finale — `origin` pointait sur le fork +`sfeir-open-source` en début de session, corrigé ici, `develop` poussé en fast-forward, 106 commits +jamais publiés jusqu'alors) a l'organisation entière configurée en `allowed_actions: local_only` : +**aucune Action GitHub officielle** (`actions/checkout`, `actions/setup-node`, etc.) ne pouvait +s'exécuter, sur aucun repo de l'organisation, avant cette étape — `startup_failure`, 0 job planifié, +confirmé identique avec l'ancien `develop.yml` (avant toute modification de cette étape) sur le +premier push de `develop`. Politique organisation changée par un Owner TalkControl vers « Allow +TalkControl, and select non-TalkControl, actions and reusable workflows » + « Allow actions created +by GitHub » — portée minimale (namespace `actions/*` uniquement, aucune action tierce Marketplace). +Documenté ici plutôt que dans decisions.md : c'est un blocage d'infra/organisation, pas une décision +d'architecture applicative. + +### Run CI réel observé + +Premier run réel obtenu après déblocage (PR #36, commit `9aa496a`) : **succès**, 7 jobs (6 + `docs-deploy` +correctement skippé — condition `push` sur `develop` uniquement, pas sur `pull_request`). Durées +individuelles (cache Playwright à froid, premier run avec cette clé) : + +| Job | Durée | +|---|---| +| Lint | 16s | +| TypeScript strict | 17s | +| Vite build | 20s | +| Server smoke test | 22s | +| Coverage (jsdom+browser+merge) | 1m6s | +| E2E Playwright | 1m9s | +| **Total run (parallèle, wall-clock)** | **1m9s** (borné par le plus long, E2E) | + +Avertissement de dépréciation Node 20 relevé sur ce run (`actions/checkout@v4`, `actions/setup-node@v4` +forcés sur Node 24) → actions montées à leur dernière version majeure dans un commit suivant +(`checkout` v4→v7, `setup-node` v4→v6, `cache` v4→v6, `configure-pages` v5→v6, +`upload-pages-artifact` v3→v5, `deploy-pages` v4→v5). + +### Cache Playwright (avant/après) + +- **1er run** (`9aa496a`, cache froid) : step « Install Playwright chromium (cold) » exécutée dans + `coverage` et `e2e` — téléchargement complet du binaire chromium. `coverage` 1m6s, `e2e` 1m9s. +- **2e run** (`2ba1984`, même clé de cache — lockfile inchangé) : confirmé via l'API des steps — + « Install Playwright chromium (cold) » → `skipped`, « Install Playwright OS dependencies (cache + hit) » → `success`. Le téléchargement du binaire chromium est bien sauté. `coverage` 1m1s, `e2e` + 1m1s (vs 1m6s/1m9s à froid) — run total 1m2s vs 1m9s. + +### Concurrency + +Commit `a48ac6f` poussé alors que le run précédent (déclenché par `f99c750`, run `29275092170`) +était encore `in_progress`. Résultat observé via l'API GitHub Actions : +- `29275092170` (run obsolète) → `status: completed`, `conclusion: cancelled`. +- `29275125106` (nouveau run) → passe de `pending` à `in_progress` dès l'annulation du précédent, + termine en succès. + +`cancel-in-progress: true` confirmé fonctionnel : le run devenu obsolète est bien annulé au profit +du plus récent, au lieu de tourner en parallèle et de gâcher des minutes CI. + +### Coverage gate + +Vérifié réellement bloquant **en local** (pas seulement en lecture du YAML) : seuil `lines` remonté +artificiellement de 80 à 99 dans `vitest.config.ts`, `npx vitest --mergeReports=... --coverage` +échoue avec `ERROR: Coverage for lines (94.43%) does not meet global threshold (99%)`, exit code 1. +Seuil réel (80/90/70/80, remonté à l'étape 15) restauré immédiatement après le test, jamais commité. + +### Validations locales + +``` +npx tsc --noEmit → 0 erreur +npm run lint → 0 erreur +NODE_ENV=test npm test → 392/392 +npm run test:components → 35/35 +npm run coverage → 93.02%/90.76%/86.57%/94.43% (stmt/branch/func/lines) vs seuils 80/90/70/80 +npm run build → OK +npm audit → 0 vulnérabilité +npm run test:e2e → 35/35 (échec initial dû à un process `serve` orphelin sur le port 3000, + sans lien avec les changements de cette étape — tué, tests relancés) +npm run docs:build → 179 pages Markdown générées (TypeDoc), doc API non vide +``` + diff --git a/package-lock.json b/package-lock.json index 95d811e4..9aa59dc2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -56,6 +56,8 @@ "reveal.js": "^6.0.1", "supertest": "^7.2.2", "tsx": "^4.23.0", + "typedoc": "^0.28.20", + "typedoc-plugin-markdown": "^4.12.0", "typescript": "^5.9.3", "typescript-eslint": "^8.63.0", "vite": "^8.1.4", @@ -1079,6 +1081,62 @@ "node": ">=6" } }, + "node_modules/@gerrit0/mini-shiki": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@gerrit0/mini-shiki/-/mini-shiki-3.23.0.tgz", + "integrity": "sha512-bEMORlG0cqdjVyCEuU0cDQbORWX+kYCeo0kV1lbxF5bt4r7SID2l9bqsxJEM0zndaxpOUT7riCyIVEuqq/Ynxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/engine-oniguruma": "^3.23.0", + "@shikijs/langs": "^3.23.0", + "@shikijs/themes": "^3.23.0", + "@shikijs/types": "^3.23.0", + "@shikijs/vscode-textmate": "^10.0.2" + } + }, + "node_modules/@gerrit0/mini-shiki/node_modules/@shikijs/engine-oniguruma": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.23.0.tgz", + "integrity": "sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.23.0", + "@shikijs/vscode-textmate": "^10.0.2" + } + }, + "node_modules/@gerrit0/mini-shiki/node_modules/@shikijs/langs": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.23.0.tgz", + "integrity": "sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.23.0" + } + }, + "node_modules/@gerrit0/mini-shiki/node_modules/@shikijs/themes": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.23.0.tgz", + "integrity": "sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.23.0" + } + }, + "node_modules/@gerrit0/mini-shiki/node_modules/@shikijs/types": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.23.0.tgz", + "integrity": "sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + } + }, "node_modules/@hapi/address": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/@hapi/address/-/address-5.1.1.tgz", @@ -6242,9 +6300,9 @@ } }, "node_modules/linkify-it": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.1.tgz", - "integrity": "sha512-wVoTjP4Q6R0NW5hiZkVJaFZPWgtXfoGF+6LucL3/FtiNjmcHhYjEr5f1Kqjirc1nBW07J/ZuRFumqr2oqccEWg==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.2.tgz", + "integrity": "sha512-ONTm2jCMAVZjgQa/Fy1kScXsuOoF5NPTsoFBdE1KVIZ2vAh/r9+Bqo+0jINCBYnavTPQZz38QzFTme79ENoN3Q==", "dev": true, "funding": [ { @@ -6308,6 +6366,13 @@ "dev": true, "license": "MIT" }, + "node_modules/lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "dev": true, + "license": "MIT" + }, "node_modules/magic-string": { "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", @@ -6338,9 +6403,9 @@ "license": "MIT" }, "node_modules/markdown-it": { - "version": "14.2.0", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.2.0.tgz", - "integrity": "sha512-1TGiQiJVRQ3NPmZH6sx5Cfnmg6GQm9jvC1ch4TK511NjSJvjzKLzn5pPfZRNZkRPZP0HqCioSndqH8v2nRaWVQ==", + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.3.0.tgz", + "integrity": "sha512-RCEsPjR+sr0x+AuYp601tKTkgFG4YEPLCzHST3cQ/fhlJkqAkz1L2/Qbp1j9qw5SBwQHFBoW8+hoN5xssOF0Tw==", "dev": true, "funding": [ { @@ -6355,8 +6420,8 @@ "license": "MIT", "dependencies": { "argparse": "^2.0.1", - "entities": "^4.4.0", - "linkify-it": "^5.0.1", + "entities": "^4.5.0", + "linkify-it": "^5.0.2", "mdurl": "^2.0.0", "punycode.js": "^2.3.1", "uc.micro": "^2.1.0" @@ -8678,6 +8743,43 @@ "url": "https://opencollective.com/express" } }, + "node_modules/typedoc": { + "version": "0.28.20", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.28.20.tgz", + "integrity": "sha512-uSKqkh8Cr48vllnEy+jdaAgOeR6Y+QCBW7usgUsKj7gJEfR7stw9U/fE49LBnj2tPRKPY0c0EBJSWe9Appmplg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@gerrit0/mini-shiki": "^3.23.0", + "lunr": "^2.3.9", + "markdown-it": "^14.3.0", + "minimatch": "^10.2.5", + "yaml": "^2.9.0" + }, + "bin": { + "typedoc": "bin/typedoc" + }, + "engines": { + "node": ">= 18", + "pnpm": ">= 10" + }, + "peerDependencies": { + "typescript": "5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x || 5.9.x || 6.0.x" + } + }, + "node_modules/typedoc-plugin-markdown": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-4.12.0.tgz", + "integrity": "sha512-eJDEMAfxCmede22c/Jw7d0FA13ggAQv+KkwQYKYCdqI02cin6Rc9QRwbG/7XvvHWinuFejySnZVUWDtvGk3Vbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "typedoc": "0.28.x" + } + }, "node_modules/typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", @@ -9393,6 +9495,22 @@ "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", "license": "ISC" }, + "node_modules/yaml": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -9943,6 +10061,59 @@ "@fortawesome/fontawesome-common-types": "7.3.0" } }, + "@gerrit0/mini-shiki": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@gerrit0/mini-shiki/-/mini-shiki-3.23.0.tgz", + "integrity": "sha512-bEMORlG0cqdjVyCEuU0cDQbORWX+kYCeo0kV1lbxF5bt4r7SID2l9bqsxJEM0zndaxpOUT7riCyIVEuqq/Ynxg==", + "dev": true, + "requires": { + "@shikijs/engine-oniguruma": "^3.23.0", + "@shikijs/langs": "^3.23.0", + "@shikijs/themes": "^3.23.0", + "@shikijs/types": "^3.23.0", + "@shikijs/vscode-textmate": "^10.0.2" + }, + "dependencies": { + "@shikijs/engine-oniguruma": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.23.0.tgz", + "integrity": "sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g==", + "dev": true, + "requires": { + "@shikijs/types": "3.23.0", + "@shikijs/vscode-textmate": "^10.0.2" + } + }, + "@shikijs/langs": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.23.0.tgz", + "integrity": "sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg==", + "dev": true, + "requires": { + "@shikijs/types": "3.23.0" + } + }, + "@shikijs/themes": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.23.0.tgz", + "integrity": "sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA==", + "dev": true, + "requires": { + "@shikijs/types": "3.23.0" + } + }, + "@shikijs/types": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.23.0.tgz", + "integrity": "sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ==", + "dev": true, + "requires": { + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + } + } + } + }, "@hapi/address": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/@hapi/address/-/address-5.1.1.tgz", @@ -13165,9 +13336,9 @@ "optional": true }, "linkify-it": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.1.tgz", - "integrity": "sha512-wVoTjP4Q6R0NW5hiZkVJaFZPWgtXfoGF+6LucL3/FtiNjmcHhYjEr5f1Kqjirc1nBW07J/ZuRFumqr2oqccEWg==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.2.tgz", + "integrity": "sha512-ONTm2jCMAVZjgQa/Fy1kScXsuOoF5NPTsoFBdE1KVIZ2vAh/r9+Bqo+0jINCBYnavTPQZz38QzFTme79ENoN3Q==", "dev": true, "requires": { "uc.micro": "^2.0.0" @@ -13215,6 +13386,12 @@ "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", "dev": true }, + "lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "dev": true + }, "magic-string": { "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", @@ -13242,14 +13419,14 @@ "dev": true }, "markdown-it": { - "version": "14.2.0", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.2.0.tgz", - "integrity": "sha512-1TGiQiJVRQ3NPmZH6sx5Cfnmg6GQm9jvC1ch4TK511NjSJvjzKLzn5pPfZRNZkRPZP0HqCioSndqH8v2nRaWVQ==", + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.3.0.tgz", + "integrity": "sha512-RCEsPjR+sr0x+AuYp601tKTkgFG4YEPLCzHST3cQ/fhlJkqAkz1L2/Qbp1j9qw5SBwQHFBoW8+hoN5xssOF0Tw==", "dev": true, "requires": { "argparse": "^2.0.1", - "entities": "^4.4.0", - "linkify-it": "^5.0.1", + "entities": "^4.5.0", + "linkify-it": "^5.0.2", "mdurl": "^2.0.0", "punycode.js": "^2.3.1", "uc.micro": "^2.1.0" @@ -14733,6 +14910,26 @@ } } }, + "typedoc": { + "version": "0.28.20", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.28.20.tgz", + "integrity": "sha512-uSKqkh8Cr48vllnEy+jdaAgOeR6Y+QCBW7usgUsKj7gJEfR7stw9U/fE49LBnj2tPRKPY0c0EBJSWe9Appmplg==", + "dev": true, + "requires": { + "@gerrit0/mini-shiki": "^3.23.0", + "lunr": "^2.3.9", + "markdown-it": "^14.3.0", + "minimatch": "^10.2.5", + "yaml": "^2.9.0" + } + }, + "typedoc-plugin-markdown": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-4.12.0.tgz", + "integrity": "sha512-eJDEMAfxCmede22c/Jw7d0FA13ggAQv+KkwQYKYCdqI02cin6Rc9QRwbG/7XvvHWinuFejySnZVUWDtvGk3Vbg==", + "dev": true, + "requires": {} + }, "typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", @@ -15122,6 +15319,12 @@ "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" }, + "yaml": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "dev": true + }, "yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index ce509198..22fb5b80 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "tc-controller-and-component": "vite --port 3000", "tc-server": "npx nodemon --exec \"npx tsx\" src/server/index.ts", "docs:dev": "vitepress dev docs-sources", - "docs:build": "npm run jsdoc:build && vitepress build docs-sources --outDir ../docs-dist", + "docs:build": "npm run jsdoc:build && vitepress build docs-sources --outDir docs-dist", "jsdoc:build": "node scripts/jsdoc-build.mjs", "test:components": "vitest run --config vitest.browser.config.ts", "test:e2e": "node node_modules/@playwright/test/cli.js test --config playwright.config.cjs", @@ -80,6 +80,8 @@ "reveal.js": "^6.0.1", "supertest": "^7.2.2", "tsx": "^4.23.0", + "typedoc": "^0.28.20", + "typedoc-plugin-markdown": "^4.12.0", "typescript": "^5.9.3", "typescript-eslint": "^8.63.0", "vite": "^8.1.4", diff --git a/scripts/jsdoc-build.mjs b/scripts/jsdoc-build.mjs index 8c2b5587..08774e2a 100644 --- a/scripts/jsdoc-build.mjs +++ b/scripts/jsdoc-build.mjs @@ -1,64 +1,38 @@ #!/usr/bin/env node /** - * Replaces vuepress-jsdoc: scans src/, generates per-file Markdown via jsdoc-to-markdown, - * writes to docs-sources/developers/code/parts/ and regenerates the README index. + * Generates the API reference under docs-sources/developers/code/ via TypeDoc + typedoc-plugin-markdown. + * Replaces jsdoc-to-markdown (step 16): jsdoc's parser cannot handle TypeScript syntax (interfaces, + * generics, type annotations aren't valid JS), and src/ has been 100% TypeScript since the lit/event-bus + * migration — jsdoc:build silently generated 0 files. */ -import jsdoc2md from 'jsdoc-to-markdown'; -import { mkdirSync, writeFileSync, readdirSync } from 'fs'; -import { join, relative, dirname, basename } from 'path'; +import { Application, TSConfigReader } from 'typedoc'; +import { join, dirname } from 'path'; import { fileURLToPath } from 'url'; const __dirname = dirname(fileURLToPath(import.meta.url)); const root = join(__dirname, '..'); -const srcDir = join(root, 'src'); -const partsDir = join(root, 'docs-sources/developers/code/parts'); - -/** - * Recursively collects .js source files under `dir`, excluding *.spec.js. - * - * @param {string} dir - directory to scan - * @returns {string[]} absolute paths of matching files - */ -function findJsFiles(dir) { - const entries = readdirSync(dir, { withFileTypes: true }); - return entries.flatMap(entry => { - const full = join(dir, entry.name); - if (entry.isDirectory()) return findJsFiles(full); - if (entry.isFile() && entry.name.endsWith('.js') && !entry.name.endsWith('.spec.js')) return [full]; - return []; - }); -} - -const generated = []; - -for (const file of findJsFiles(srcDir)) { - const relPath = relative(srcDir, file).replace(/\.js$/, '.md'); - const outFile = join(partsDir, relPath); - - const md = await jsdoc2md.render({ files: file }); - if (!md.trim()) continue; - - mkdirSync(dirname(outFile), { recursive: true }); - writeFileSync(outFile, md); - generated.push(relPath); -} - -// Regenerate the README index grouped by top-level directory -const byGroup = generated.reduce((acc, f) => { - const group = f.split('/')[0]; - (acc[group] ??= []).push(f); - return acc; -}, {}); - -const indexLines = ['# API Reference\n']; -for (const [group, files] of Object.entries(byGroup).sort()) { - indexLines.push(`## ${group.charAt(0).toUpperCase() + group.slice(1)}\n`); - for (const f of files.sort()) { - const name = basename(f, '.md'); - indexLines.push(`- [${name}](parts/${f})`); - } - indexLines.push(''); +const out = join(root, 'docs-sources/developers/code'); + +const app = await Application.bootstrapWithPlugins( + { + entryPoints: ['src'], + entryPointStrategy: 'expand', + tsconfig: 'tsconfig.json', + exclude: ['src/environment/**', 'src/client/layouts/**', '**/*.spec.ts', 'src/vite-env.d.ts'], + plugin: ['typedoc-plugin-markdown'], + readme: 'none', + entryFileName: 'index.md', + githubPages: false, + out + }, + [new TSConfigReader()] +); + +const project = await app.convert(); +if (!project) { + console.error('jsdoc-build: TypeDoc conversion failed'); + process.exit(1); } -writeFileSync(join(root, 'docs-sources/developers/code/index.md'), indexLines.join('\n')); -console.log(`jsdoc-build: ${generated.length} file(s) generated in docs-sources/developers/code/parts/`); +await app.generateOutputs(project); +console.log(`jsdoc-build: API reference generated in ${out}`);