diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml index fbcd466d..4c569e9f 100644 --- a/.github/workflows/pr-checks.yml +++ b/.github/workflows/pr-checks.yml @@ -126,6 +126,12 @@ jobs: - name: Build the schematics package run: pnpm schematics:build + # FSHSP-228: the only place every kit import goes through the rewrite that + # `ng add` applies. Without it, an import the table cannot re-address fails + # at the consumer's install, as `type UiIconSize` did in 0.11.0 and 0.12.0. + - name: Schematics tests + run: pnpm schematics:test + # `postinstall` has just regenerated everything `docs:config` produces, so # this step looks redundant. It is not — it enforces two things nothing # else does: diff --git a/CHANGELOG.md b/CHANGELOG.md index a8778c76..56401d70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,25 @@ Le format suit [Keep a Changelog](https://keepachangelog.com/fr/1.1.0/) et le pr ## [Unreleased] +### Fixed + +- **Schematics : sur un projet Angular 20, `ng add` s'arrêtait sur un ERESOLVE et laissait le + `package.json` à moitié migré** (FSHSP-228). Les `peerDependencies` du kit étaient recopiées + par-dessus les versions du projet : `@angular/core`, `common`, `forms`, `router` et + `platform-browser` passaient en `^22.0.0`, `compiler-cli`, `build` et `cli` restaient en 20, et + `@angular-devkit/build-angular@^22` faisait échouer npm. + - `ng add`, `add` et `update` vérifient d'abord le majeur d'Angular du projet. S'il n'est pas + celui du kit, ils s'arrêtent sans rien écrire et donnent les `ng update` à enchaîner. + - Une dépendance déjà déclarée garde sa plage. Un paquet Angular absent prend celle du + `@angular/core` du projet, les paquets du framework s'exigeant l'un l'autre à la version + exacte. +- **Schematics : copier `ui-input-date` faisait échouer `ng add`** (FSHSP-228), depuis la + 0.11.0, sur « import(s) du kit non réadressé(s) … (symbole type UiIconSize) ». La réécriture + des imports prenait le modificateur `type` en ligne (`import { UiIcon, type UiIconSize }`) + pour une partie du nom. Il est ignoré pour la recherche et conservé dans l'import réécrit. + Les schematics ont maintenant des tests (`pnpm schematics:test`, en CI), dont un qui fait + passer chaque composant par la réécriture. + ## [0.12.0] - 2026-09-25 ### Added diff --git a/package.json b/package.json index 23a119dc..3e86f41d 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "schematics:assets": "node scripts/schematics-assets.build.mjs", "schematics:build": "node scripts/schematics-package.build.mjs", "schematics:pack": "pnpm schematics:build && pnpm -C dist/ui-kit-schematics pack --pack-destination ../..", + "schematics:test": "pnpm schematics:assets && vitest run --root projects/ui-kit-schematics", "mcp:assets": "node scripts/mcp-assets.build.mjs", "mcp:bundle": "node scripts/mcp-bundle.build.mjs", "mcp:test": "vitest run --root projects/ui-kit-mcp" diff --git a/projects/ui-kit-schematics/README.md b/projects/ui-kit-schematics/README.md index 94486092..17b82837 100644 --- a/projects/ui-kit-schematics/README.md +++ b/projects/ui-kit-schematics/README.md @@ -17,6 +17,12 @@ schematics that copy them into a consuming project. ng add @4sh/ui-kit-schematics ``` +**Requires an Angular 22 project** (the kit's `peerDependencies`: `@angular/core ^22.0.0`). +On any other major, `ng add`, `add` and `update` stop before writing anything and print +the `ng update` steps to run first, one major at a time. Your Angular, RxJS and CDK +versions are never rewritten: a missing Angular package is added at your +`@angular/core` range. + One command: it lays the foundation (styles, design tokens, `angular.json`), then asks which components to copy and copies them, dependencies included. The prompt is a checkbox list — space to pick, a for all, diff --git a/projects/ui-kit-schematics/src/add/index.ts b/projects/ui-kit-schematics/src/add/index.ts index d4a32a81..4dc50947 100644 --- a/projects/ui-kit-schematics/src/add/index.ts +++ b/projects/ui-kit-schematics/src/add/index.ts @@ -13,6 +13,7 @@ import { resolveDependencies } from '../utils/dependency-graph'; import { copyUnit } from '../utils/copy'; import { emptyManifest, readManifest, today, writeManifest } from '../utils/manifest'; import { kitVersion as readKitVersion } from '../utils/kit-manifest'; +import { checkAngularCompatibility } from '../utils/angular-version'; /** * Sélection interactive : checkbox `@inquirer/prompts`, qui supporte déjà @@ -29,6 +30,8 @@ async function promptComponentNames(): Promise { export function add(options: Schema): Rule { return async (tree: Tree, context: SchematicContext) => { + // Avant le prompt : des sources Angular 22 dans un projet Angular 20 ne compilent pas. + checkAngularCompatibility(tree, context); const available = listComponents(); let selected: string[]; diff --git a/projects/ui-kit-schematics/src/ng-add/index.ts b/projects/ui-kit-schematics/src/ng-add/index.ts index 8331e49e..92e826cb 100644 --- a/projects/ui-kit-schematics/src/ng-add/index.ts +++ b/projects/ui-kit-schematics/src/ng-add/index.ts @@ -20,6 +20,7 @@ import { join } from 'node:path'; import type { Schema } from './schema'; import { addDependency, + ensureDependency, addNpmScript, detectPackageManager, readPackageJson, @@ -36,6 +37,7 @@ import { stylesFoundationDir, } from '../utils/component-registry'; import { readKitManifestInfo } from '../utils/kit-manifest'; +import { assertAngularCompatible } from '../utils/angular-version'; import { rewriteKitPaths } from '../utils/kit-paths'; import { add } from '../add'; @@ -740,7 +742,10 @@ function applicationIndexPaths(tree: Tree): string[] { * Demander la même plage laisse npm les déduire ensemble. */ function angularRange(tree: Tree): string { - return readPackageJson(tree).dependencies?.['@angular/core'] ?? '^22.0.0'; + const json = readPackageJson(tree); + return ( + json.dependencies?.['@angular/core'] ?? json.devDependencies?.['@angular/core'] ?? '^22.0.0' + ); } /** Dépendances et scripts du Storybook du consommateur. Versions alignées sur ce dépôt. */ @@ -816,8 +821,13 @@ function addRuntimeDependencies(): Rule { // Toutes les peerDependencies déjà déclarées par @4sh/ui-kit (@angular/cdk, rxjs…) // deviennent des dependencies directes chez le consommateur — pas @4sh/ui-kit // lui-même, gardé en devDependency pour piloter la CLI (voir ticket). + // Seulement celles qui manquent : la plage du projet reste la sienne. Un + // paquet Angular absent prend celle de son `@angular/core`, pas le `^22.0.0` + // du kit : les paquets du framework s'exigent l'un l'autre à la version + // exacte (voir `angularRange`). for (const [name, version] of Object.entries(peerDependencies)) { - addDependency(tree, name, version, 'dependencies'); + const range = name.startsWith('@angular/') ? angularRange(tree) : version; + ensureDependency(tree, name, range, 'dependencies'); } // FontAwesome n'est pas une peerDependency du kit (elle n'est pas importée // par le TypeScript : `ui-icon` ne pose que des classes CSS), mais la @@ -1138,6 +1148,9 @@ export function ngAdd(options: Schema): Rule { const withMcp = !(options.skipMcp ?? false); const foundation = [ + // Avant tout le reste, prompts compris : un projet d'un autre majeur + // d'Angular est refusé sans que rien ne soit écrit ni demandé. + assertAngularCompatible(), copyStylesFoundationRule(), createStyleScaffolds(), // Avant `updateAngularJson`, qui déclare `src/assets` au builder : la diff --git a/projects/ui-kit-schematics/src/update/index.ts b/projects/ui-kit-schematics/src/update/index.ts index e36bf313..97de549e 100644 --- a/projects/ui-kit-schematics/src/update/index.ts +++ b/projects/ui-kit-schematics/src/update/index.ts @@ -15,6 +15,7 @@ import { findUnit } from '../utils/component-registry'; import { renderUnitFiles } from '../utils/copy'; import { readManifest, today, writeManifest } from '../utils/manifest'; import { kitVersion as readKitVersion } from '../utils/kit-manifest'; +import { checkAngularCompatibility } from '../utils/angular-version'; type Action = 'apply' | 'skip' | 'view-diff'; @@ -88,13 +89,15 @@ function logUncoveredConcerns(context: SchematicContext): void { `d'une version plus ancienne du kit, \`ng add @4sh/ui-kit-schematics ` + `--skip-components\` réapplique ce qui peut l'être sans risque : vos composants, ` + `votre \`.mcp.json\`, votre config Prettier et vos styles retouchés sont ` + - `préservés. Seule réserve mesurée : une dépendance que vous auriez épinglée peut ` + - `y être ré-élargie vers la plage du kit.`, + `préservés. Seule réserve mesurée : une dépendance d'outillage que vous auriez épinglée ` + + `(Storybook, Prettier…) peut y être ré-élargie vers la plage du kit ; Angular et ` + + `RxJS gardent la vôtre.`, ); } export function update(options: Schema): Rule { return async (tree: Tree, context: SchematicContext) => { + checkAngularCompatibility(tree, context); const manifest = readManifest(tree); if (!manifest) { throw new SchematicsException( diff --git a/projects/ui-kit-schematics/src/utils/angular-version.spec.ts b/projects/ui-kit-schematics/src/utils/angular-version.spec.ts new file mode 100644 index 00000000..215957e7 --- /dev/null +++ b/projects/ui-kit-schematics/src/utils/angular-version.spec.ts @@ -0,0 +1,105 @@ +import { HostTree, SchematicsException } from '@angular-devkit/schematics'; +import type { SchematicContext } from '@angular-devkit/schematics'; +import { describe, expect, it, vi } from 'vitest'; +import { angularMismatch, checkAngularCompatibility, majorOf } from './angular-version'; +import { readKitManifestInfo } from './kit-manifest'; +import { ensureDependency, readPackageJson } from './package-json'; + +function projectTree(dependencies: Record): HostTree { + const tree = new HostTree(); + tree.create('/package.json', JSON.stringify({ name: 'test', dependencies })); + return tree; +} + +function fakeContext() { + const warn = vi.fn(); + return { context: { logger: { warn } } as unknown as SchematicContext, warn }; +} + +describe('majorOf', () => { + it.each([ + ['^20.3.0', 20], + ['~20.3.0', 20], + ['20.3.0', 20], + ['>=20.0.0 <23', 20], + ['20.x', 20], + ['v22', 22], + ['22.0.0-next.1', 22], + ['^22.0.0 || ^23.0.0', 22], + ])('%s → %i', (range, major) => { + expect(majorOf(range)).toBe(major); + }); + + it.each(['latest', '*', 'workspace:*', 'file:../core', 'npm:@angular/core@22', undefined])( + '%s → illisible', + (range) => { + expect(majorOf(range)).toBeUndefined(); + }, + ); +}); + +describe('angularMismatch', () => { + it("laisse passer le majeur du kit, quelle qu'en soit la mineure", () => { + expect(angularMismatch('^22.2.0', '^22.0.0', '0.12.1')).toBeUndefined(); + }); + + it('donne les montées de majeur une à une pour un projet en retard', () => { + const message = angularMismatch('^20.3.0', '^22.0.0', '0.12.1'); + expect(message).toContain('Angular 22'); + expect(message).toContain('Angular 20'); + expect(message).toContain('ng update @angular/core@21 @angular/cli@21'); + expect(message).toContain('ng update @angular/core@22 @angular/cli@22'); + expect(message).toContain("n'a rien modifié"); + }); + + it('refuse un majeur que le kit ne connaît pas encore, sans proposer de descendre', () => { + const message = angularMismatch('^23.0.0', '^22.0.0', '0.12.1'); + expect(message).toContain('ne connaît pas encore Angular 23'); + expect(message).not.toContain('ng update'); + }); +}); + +describe('checkAngularCompatibility', () => { + const kitRange = readKitManifestInfo().peerDependencies['@angular/core']; + const kitMajor = majorOf(kitRange)!; + + it('refuse un projet Angular 20 (ERESOLVE du ng add de la 0.12.0)', () => { + const { context } = fakeContext(); + expect(() => + checkAngularCompatibility(projectTree({ '@angular/core': '^20.3.0' }), context), + ).toThrow(SchematicsException); + }); + + it('accepte un projet du majeur du kit', () => { + const { context, warn } = fakeContext(); + checkAngularCompatibility(projectTree({ '@angular/core': `^${kitMajor}.1.0` }), context); + expect(warn).not.toHaveBeenCalled(); + }); + + it('avertit sans refuser quand la plage ne se lit pas', () => { + const { context, warn } = fakeContext(); + checkAngularCompatibility(projectTree({ '@angular/core': 'latest' }), context); + expect(warn).toHaveBeenCalledOnce(); + }); +}); + +describe('ensureDependency', () => { + it("ne remplace jamais la plage d'un paquet déjà déclaré", () => { + const tree = projectTree({ '@angular/core': '^20.3.0' }); + expect(ensureDependency(tree, '@angular/core', '^22.0.0')).toBe(false); + expect(readPackageJson(tree).dependencies?.['@angular/core']).toBe('^20.3.0'); + }); + + it('ne le recopie pas en dependencies quand il est en devDependencies', () => { + const tree = new HostTree(); + tree.create('/package.json', JSON.stringify({ devDependencies: { rxjs: '~7.8.0' } })); + expect(ensureDependency(tree, 'rxjs', '^7.8.0')).toBe(false); + expect(readPackageJson(tree).dependencies).toBeUndefined(); + }); + + it('ajoute un paquet absent', () => { + const tree = projectTree({}); + expect(ensureDependency(tree, '@angular/cdk', '^22.1.0')).toBe(true); + expect(readPackageJson(tree).dependencies?.['@angular/cdk']).toBe('^22.1.0'); + }); +}); diff --git a/projects/ui-kit-schematics/src/utils/angular-version.ts b/projects/ui-kit-schematics/src/utils/angular-version.ts new file mode 100644 index 00000000..3fb9ae13 --- /dev/null +++ b/projects/ui-kit-schematics/src/utils/angular-version.ts @@ -0,0 +1,107 @@ +/** + * angular-version — refuser un projet dont le majeur d'Angular n'est pas celui du kit. + * + * Les sources copiées et les dépendances que `ng add` inscrit visent le majeur + * des `peerDependencies` du kit. Posées dans un projet d'un autre majeur, elles + * ne compilent pas, et l'install s'arrête sur un ERESOLVE qui ne dit pas d'où il + * vient (Angular 20 : `@angular-devkit/build-angular@^22` contre le + * `compiler-cli@^20` du projet). Monter Angular d'un majeur est le travail + * d'`ng update`, avec ses migrations : un schematic de composants ne le fait pas + * à moitié. On s'arrête donc AVANT toute écriture, en disant quoi faire. + */ +import type { Rule, SchematicContext, Tree } from '@angular-devkit/schematics'; +import { SchematicsException } from '@angular-devkit/schematics'; +import { readKitManifestInfo } from './kit-manifest'; +import { readPackageJson } from './package-json'; + +/** + * Majeur plancher d'une plage npm : `^20.3.0`, `~20.3.0`, `20.3.0`, `>=20.0.0`, + * `20.x`, `v20` → 20. `undefined` pour ce qui ne se lit pas ainsi (`latest`, + * `*`, `file:…`, `workspace:*`, `npm:…`) : l'appelant ne devine pas. + */ +export function majorOf(range: string | undefined): number | undefined { + const match = /^\s*(?:[\^~]|>=?)?\s*v?(\d+)(?:[.\sx*]|$)/.exec(range ?? ''); + return match ? Number(match[1]) : undefined; +} + +/** Plage de `@angular/core` du projet, dépendances puis devDependencies. */ +function projectCoreRange(tree: Tree): string | undefined { + const json = readPackageJson(tree); + return json.dependencies?.['@angular/core'] ?? json.devDependencies?.['@angular/core']; +} + +/** Version installée de `@angular/core`, quand la plage déclarée ne se lit pas. */ +function installedCoreVersion(tree: Tree): string | undefined { + const buffer = tree.read('/node_modules/@angular/core/package.json'); + if (!buffer) return undefined; + try { + return JSON.parse(buffer.toString('utf8')).version; + } catch { + return undefined; + } +} + +/** + * Message d'échec, ou `undefined` si le projet est compatible ou illisible. + * Séparé de la règle pour être testé sans arbre. + */ +export function angularMismatch( + projectRange: string | undefined, + kitRange: string, + kitVersion: string, +): string | undefined { + const project = majorOf(projectRange); + const kit = majorOf(kitRange); + if (project === undefined || kit === undefined || project === kit) return undefined; + + const found = + `@4sh/ui-kit ${kitVersion} demande Angular ${kit} (@angular/core ${kitRange}), ` + + `ce projet est en Angular ${project} (@angular/core ${projectRange}).`; + const untouched = + "Le schematic n'a rien modifié. Seul `@4sh/ui-kit-schematics`, ajouté par `ng add`, " + + 'est à retirer du package.json si vous en restez là.'; + if (project > kit) { + return `${found}\nCette version du kit ne connaît pas encore Angular ${project}.\n${untouched}`; + } + const steps = Array.from({ length: kit - project }, (_, i) => project + i + 1) + .map((major) => ` ng update @angular/core@${major} @angular/cli@${major}`) + .join('\n'); + return ( + `${found}\nMettre le projet à niveau d'abord, un majeur à la fois ` + + `(https://angular.dev/update-guide) :\n${steps}\n` + + `puis relancer \`ng add @4sh/ui-kit-schematics\`.\n${untouched}` + ); +} + +/** + * À appeler avant toute écriture ET avant tout prompt. Ne lit que + * `package.json` (et, à défaut, la version installée) : un projet dont la plage + * ne se lit pas passe, avec un avertissement, plutôt que d'être refusé sur une + * supposition. + */ +export function checkAngularCompatibility(tree: Tree, context: SchematicContext): void { + const { version, peerDependencies } = readKitManifestInfo(); + const kitRange = peerDependencies['@angular/core']; + if (!kitRange) return; + + const declared = projectCoreRange(tree); + const range = majorOf(declared) !== undefined ? declared : installedCoreVersion(tree); + if (majorOf(range) === undefined) { + context.logger.warn( + `Version d'Angular du projet illisible (@angular/core « ${declared ?? 'absent'} ») : ` + + `le kit demande ${kitRange}, vérification sautée.`, + ); + return; + } + + const mismatch = angularMismatch(range, kitRange, version); + if (mismatch) throw new SchematicsException(mismatch); +} + +/** {@link checkAngularCompatibility} en règle, pour la tête d'une chaîne. */ +export function assertAngularCompatible(): Rule { + return (tree, context) => { + checkAngularCompatibility(tree, context); + return tree; + }; +} diff --git a/projects/ui-kit-schematics/src/utils/copy.spec.ts b/projects/ui-kit-schematics/src/utils/copy.spec.ts new file mode 100644 index 00000000..b5228238 --- /dev/null +++ b/projects/ui-kit-schematics/src/utils/copy.spec.ts @@ -0,0 +1,24 @@ +import { describe, expect, it } from 'vitest'; +import { listComponents, listSharedBases } from './component-registry'; +import { renderUnitFiles } from './copy'; + +// Le seul endroit où TOUS les imports du kit passent par la réécriture. Sans +// lui, un import que la table ne sait pas réadresser n'échoue que chez le +// consommateur, au `ng add` : c'est ainsi que `type UiIconSize` (ui-input-date) +// est parti en 0.11.0 et 0.12.0. +describe('renderUnitFiles, sur chaque unité du kit', () => { + const units = [...listComponents(), ...listSharedBases()]; + + it('trouve les unités (assets/ généré)', () => { + // Une liste vide ferait passer la suite sans rien vérifier. + expect(listComponents().length).toBeGreaterThan(40); + expect(listSharedBases().length).toBeGreaterThan(0); + }); + + it.each(units.map((unit) => [unit.name, unit] as const))( + '%s : aucun import du kit non réadressé', + (_name, unit) => { + expect(() => renderUnitFiles(unit, '0.0.0-test', { withStorybook: true })).not.toThrow(); + }, + ); +}); diff --git a/projects/ui-kit-schematics/src/utils/package-json.ts b/projects/ui-kit-schematics/src/utils/package-json.ts index ad86fcae..de1daf34 100644 --- a/projects/ui-kit-schematics/src/utils/package-json.ts +++ b/projects/ui-kit-schematics/src/utils/package-json.ts @@ -30,6 +30,24 @@ export function addDependency( writePackageJson(tree, json); } +/** + * Comme {@link addDependency}, mais n'écrit rien si le paquet est déjà déclaré, + * en dependencies comme en devDependencies. Pour ce que le projet possède déjà + * (Angular, RxJS), sa plage est la bonne : la remplacer par celle du kit a + * monté la moitié d'Angular d'un majeur dans un projet Angular 20. + */ +export function ensureDependency( + tree: Tree, + name: string, + version: string, + target: 'dependencies' | 'devDependencies' = 'dependencies', +): boolean { + const json = readPackageJson(tree); + if (json.dependencies?.[name] ?? json.devDependencies?.[name]) return false; + addDependency(tree, name, version, target); + return true; +} + export function addNpmScript(tree: Tree, name: string, command: string): void { const json = readPackageJson(tree); if (json.scripts?.[name]) return; // ne jamais écraser un script déjà défini par le consommateur diff --git a/projects/ui-kit-schematics/src/utils/rewrite-imports.spec.ts b/projects/ui-kit-schematics/src/utils/rewrite-imports.spec.ts new file mode 100644 index 00000000..e3ed9977 --- /dev/null +++ b/projects/ui-kit-schematics/src/utils/rewrite-imports.spec.ts @@ -0,0 +1,46 @@ +import { describe, expect, it } from 'vitest'; +import { rewriteKitImports, symbolName } from './rewrite-imports'; + +const FROM = 'src/app/shared/components/ui/forms/ui-input-date/ui-input-date.ts'; + +describe('symbolName', () => { + it.each([ + ['UiIcon', 'UiIcon'], + ['UiIcon as Icon', 'UiIcon'], + ['type UiIconSize', 'UiIconSize'], + [' type UiIconSize as Size ', 'UiIconSize'], + ])('%s → %s', (clause, expected) => { + expect(symbolName(clause)).toBe(expected); + }); +}); + +describe('rewriteKitImports', () => { + it('réadresse un symbole importé avec le modificateur `type` en ligne', () => { + const { content, unresolved } = rewriteKitImports( + "import { UiIcon, type UiIconSize } from '@4sh/ui-kit/base/ui-icon';", + FROM, + ); + expect(unresolved).toEqual([]); + expect(content).not.toContain('@4sh/ui-kit'); + // Le modificateur survit à la réécriture : l'import reste valide pour + // `verbatimModuleSyntax` / `isolatedModules`. + expect(content).toMatch(/import \{ UiIcon, type UiIconSize \} from '\.\.?\/[^']*ui-icon';/); + }); + + it('garde un import `type` complet en `import type`', () => { + const { content, unresolved } = rewriteKitImports( + "import type { UiIconSize } from '@4sh/ui-kit/base/ui-icon';", + FROM, + ); + expect(unresolved).toEqual([]); + expect(content).toMatch(/^import type \{ UiIconSize \} from '/); + }); + + it('signale un symbole que le kit n’exporte pas, sans deviner', () => { + const { unresolved } = rewriteKitImports( + "import { NotAKitSymbol } from '@4sh/ui-kit/base/ui-icon';", + FROM, + ); + expect(unresolved).toEqual(['base/ui-icon (symbole NotAKitSymbol)']); + }); +}); diff --git a/projects/ui-kit-schematics/src/utils/rewrite-imports.ts b/projects/ui-kit-schematics/src/utils/rewrite-imports.ts index 862d711a..431433f5 100644 --- a/projects/ui-kit-schematics/src/utils/rewrite-imports.ts +++ b/projects/ui-kit-schematics/src/utils/rewrite-imports.ts @@ -53,10 +53,15 @@ function relativeSpecifier(fromFileTargetPath: string, toTargetPath: string): st return rel.startsWith('.') ? rel : `./${rel}`; } -/** Un symbole importé, avec son éventuel alias : `UiIcon`, `UiIcon as Icon`. */ -function symbolName(clause: string): string { +/** + * Un symbole importé, avec son éventuel alias et son modificateur `type` + * en ligne : `UiIcon`, `UiIcon as Icon`, `type UiIconSize`. Le modificateur est + * retiré pour la recherche dans la table, et conservé dans l'import réécrit. + */ +export function symbolName(clause: string): string { return clause .trim() + .replace(/^type\s+/, '') .split(/\s+as\s+/)[0] .trim(); } diff --git a/projects/ui-kit-schematics/vitest.config.ts b/projects/ui-kit-schematics/vitest.config.ts new file mode 100644 index 00000000..693aea8d --- /dev/null +++ b/projects/ui-kit-schematics/vitest.config.ts @@ -0,0 +1,13 @@ +import { defineConfig } from 'vitest/config'; + +// Package Node pur, comme projects/ui-kit-mcp : Vitest tourne directement sur +// les `.spec.ts` (exclus du build par tsconfig.json). Les specs lisent `assets/`, +// produit par `pnpm schematics:assets` : lancer `pnpm schematics:test`, qui le +// régénère d'abord. +export default defineConfig({ + test: { + environment: 'node', + include: ['src/**/*.spec.ts'], + passWithNoTests: false, + }, +}); diff --git a/projects/ui-kit/README.md b/projects/ui-kit/README.md index a74387f0..83f08686 100644 --- a/projects/ui-kit/README.md +++ b/projects/ui-kit/README.md @@ -59,6 +59,11 @@ Published on the **public npm registry**, under the **`4sh`** organization: These are declared as `peerDependencies`: the version already present in your application is the one used, never a second copy of Angular. +On an older Angular, **npm refuses** the install (`ERESOLVE`), but **pnpm only warns** +and installs anyway (`✕ unmet peer @angular/core@^22.0.0`): that line means the project +must be upgraded first (`ng update @angular/core@22 @angular/cli@22`, one major at a +time), not that the kit is ready to use. + ### Or copy the sources instead This package gives you **compiled** components: you import them and follow the