Preserve the implicit `src/**/*.ts` coverage when `tsconfig.app.json` has no
`include` key, so seeding the array for the generated libs no longer drops the
app's own `src` from the editor program.
Use a strict `!== null` guard before pinning `embla-carousel-angular` so the
intent is explicit rather than relying on Angular majors never being 0.
ng g @spartan-ng/cli:ui generates libraries under the configured directory (e.g.
libs/ui), outside the app's src. The app still builds - the Angular builder
follows the path-mapped imports into the libs - but the TS language server treats
the generated files as orphans compiled without the root paths mapping, so
cross-lib imports (@spartan-ng/helm/utils, ...) show as "cannot find module" in
the editor.
Add the components base dir to tsconfig.app.json's include so the generated files
belong to the app project and resolve in-editor. Idempotent and a no-op when there
is no tsconfig.app.json.
The carousel primitive declared embla-carousel-angular as ">=21.0.0 <23.0.0".
That package's major tracks the Angular major (v21 -> Angular 21, v22 -> Angular
22), so on an Angular 21 workspace npm resolved the highest match (22.x), whose
peer is @angular/common >=22.0.0, and the install failed with ERESOLVE.
A static range in supported-ui-libraries.json can't know the consumer's Angular
version, so pin embla-carousel-angular to the installed Angular major at generate
time. Falls back to the declared range when Angular can't be resolved.
The dependency healthcheck hardcoded registry.npmjs.org when resolving the
latest @spartan-ng/brain and @spartan-ng/cli versions. Under cli-smoke this
flagged the locally published packages as outdated, and `--auto-fix` then
rewrote package.json to a version absent from the local registry, so the
Angular CLI's post-generate install failed and the smoke test errored.
Resolve the latest version against npm_config_registry (falling back to npmjs)
so the check matches whatever registry the project installs from - the smoke
local registry, or a private registry for real users.
Addresses review feedback:
- dedupeEntrypointGlobs now only treats a pattern as an entrypoint variant when it
contains a `/**/` segment (the shape nx prepends), so flat custom globs like
`tools/*.ts` are preserved rather than widened to `**/*.ts`.
- Load the @nx/workspace remove generator with `require(absolutePath)` instead of
dynamic import: it bypasses the package exports gate and accepts native Windows
paths without a file:// URL (this generator runs as CommonJS).
- Normalize tsconfig.lib.json before the nx secondary-entrypoint call as well as
after: a workspace left bloated by an earlier run would otherwise re-double its
arrays and could throw "Invalid string length" before the post-call cleanup runs.
- dedupeEntrypointGlobs now restores a recursive glob only when a scoped variant of
it was present, and no longer treats a root-only `*.ts` as redundant. This stops it
injecting spec/test excludes a tsconfig never had and stops it widening `*.ts` to
`**/*.ts`. Exported with unit tests covering these cases.
migrate-helm-libraries imported removeGenerator from the deep path
@nx/workspace/src/generators/remove/remove at module top level. On nx 23 that
specifier is blocked by the package's exports map (and the compiled file moved to
dist/), so the generator failed to load with "Package subpath ... is not defined by
exports" - in every mode, before doing any work. The package entry point can't be
used either: it eagerly evaluates convert-to-nx-project whose output.js crashes
("Cannot read properties of undefined (reading 'inverse')").
Load it lazily, only when a library is actually removed (generateAs: library), and
resolve the compiled file by absolute path - bypassing the exports gate and never
loading the crashing barrel - trying dist/ (nx 23+) then src/ (nx <= 22).
migrate-icon and migrate-scroll-area imported applyChangesToString/ChangeType from
the deep path @nx/devkit/src/utils/string-change. That resolves inside this monorepo
(the dev @nx/devkit has no exports map) but throws "Package subpath ./src/utils/
string-change is not defined by exports" once the CLI is installed against a devkit
that declares exports, crashing the healthcheck generator that loads these migrations.
Those helpers are not part of devkit's public entry, so vendor the small (MIT) impl
locally and import it instead.
nx's librarySecondaryEntryPointGenerator appends an entrypoint-prefixed copy of
every existing include/exclude glob each time it runs. Generating many entrypoints
into one buildable library (migrate-helm-libraries or ui with "all") doubles these
arrays on every primitive until JSON.stringify throws "Invalid string length",
surfaced as "NX Invalid string length". A few primitives stay under the limit, so
the failure only showed up with ~22+ components.
Collapse the redundant prefixed globs back to their recursive equivalents after each
secondary entrypoint is generated. The library's recursive **/*.ts include already
matches every entrypoint subdirectory, so the per-entrypoint copies add nothing. The
normalization is idempotent, so the arrays stay bounded regardless of primitive count.
Angular CLI is unaffected: it never generates buildable secondary entrypoints.
Moving popover-backed controls off CDK Dialog dropped the dialog's
autoFocus: 'first-tabbable', so popovers no longer moved focus to their
content on open (form fields were left unfocused, comboboxes could not be
typed into immediately).
Add an opt-in autoFocus option to BrnOverlay that focuses the first tabbable
element once the content renders, and enable it in the popover defaults. The
select keeps focus on its trigger because its listbox has no tabbable element,
preserving the aria-activedescendant keyboard model. Re-expose the autoFocus
input on the helm popover wrapper and its CLI template, and refresh the docs
API snapshot.
The drawer landed on main against the old BrnDialog API. Reconcile it with
this branch's refactor:
- override getPositionStrategy() for the directional position instead of the
removed mutablePositionStrategy/positionBuilder members (matches BrnSheet)
- constrain BrnDrawerContent<T extends Record<string, unknown>> to satisfy
BrnDialogContent's tightened generic
- drop the removed closeDelay-era 'delay' input from the drawer close wrapper
and its CLI template
Resolve brn-dialog.service.ts conflict: drop the deprecated injectBrnDialogCtx
alias (removed on main during the pre-1.0 audit) and keep the typed
injectBrnDialogContext with the unknown default.
* fix(cli): handle interrupts in healthcheck prompt and harden generators
Replace the raw-stdin prompt in the healthcheck generator with the standard
enquirer confirm prompt so Ctrl+C correctly aborts the run instead of leaving
the generator running. Isolate each healthcheck so one throwing does not abort
the rest, and report registry fetch failures instead of throwing.
Make getImportAlias throw on a present-but-invalid config rather than silently
falling back to the default alias, guard against the coerce() NPE in
version-utils, and switch migrate-hlm to readJson. Add unit tests across these
paths and stub network access so the suite runs offline.
Closes#906
* fix(cli): guard version healthcheck fix against missing dependencies section
The fix writeback read packageJson.dependencies[dep] directly, which threw when
a package.json had no dependencies section and the dependency lived only in
devDependencies. Guard both sections with optional chaining so the writeback is
symmetrical and never touches an undefined section.
Remove the mis-cased `HlmBreadCrumbImports` alias in favour of the canonical
`HlmBreadcrumbImports`, dropping it before 1.0 rather than carrying it on the stable line. The
NgModule migration map is repointed to the canonical name so `nx g @spartan-ng/cli:migrate-module-
imports` still rewrites `HlmBreadCrumbModule` to an export that exists.
BrnAvatarImage/BrnAvatarFallback and their helm wrappers all used the same
unprefixed exportAs ('avatarImage'/'avatarFallback'). Since the helm directive
host-applies the brain one on the same <img>, a template ref was ambiguous, and
the names broke the brn*/hlm* convention every other exportAs follows. Rename to
brnAvatarImage/brnAvatarFallback and hlmAvatarImage/hlmAvatarFallback before the
exportAs surface is locked at 1.0. Regenerated the CLI avatar templates to match.
Drawer shipped as a helm lib with docs but was missing from the CLI, so
`nx g @spartan-ng/cli:ui drawer` failed. Register it in the Primitive union and
dependency map, generate its templates + supported-ui-libraries entry, and add a
drift-guard spec asserting the CLI primitive set equals the installable helm
components (with a generator template each) so this cannot recur.
Replace fixed close delays with lifecycle-driven disposal so overlays remain responsive.
Wait only for exit animations that actually start and centralize Escape, backdrop, and
outside-pointer dismissal.
BREAKING CHANGE: remove the closeDelay input and configuration option. Remove explicit delay
values; closed-state exit animations are now detected automatically.
The cli generator specs drive real nx generators, which log copiously
("Couldn't find .gitignore", "NX workspace root does not exist", etc.) - ~3k
lines across 142 specs, enough to truncate the CI log and bury the run summary,
making a passing suite look like it failed. tools has the same class of chatter.
Add `onConsoleLog: () => false` to both configs so that intercepted console
output is dropped (the specs assert on the generated tree and on console spies,
not on this logging; test failures are still reported by the reporter). cli's
output drops from ~3160 to ~530 lines and the summary is visible again.
Add reporters: ['default'] to the mcp, cli, tools, registry and cli-smoke vite
configs (brain and helm already had it). Without a default reporter the @nx/vitest
NxReporter is the only one attached, so these projects printed no summary in CI -
which is how mcp's silently-undiscovered specs stayed invisible. The counts are
now shown so test execution is verifiable at a glance.
The mcp vite config lacked `root: __dirname`, so the @nx/vitest executor (which
runs from the workspace root) resolved `src/**/*.spec.ts` against the workspace
root, found nothing, and `passWithNoTests` passed it silently - mcp's 19 tests
never ran.
Pin mcp's root, and drop `passWithNoTests` from every project that has specs
(mcp, cli, tools, brain, helm) so a future test-discovery break fails loudly
instead of going green. registry keeps the flag - it genuinely has no specs.
- base spec: replace hoisted vi.unmock + require with test-scoped vi.importActual
to load the real @nx/angular/generators for that one test without leaking mock
state across the file
- migrate-brain-accordion-trigger spec: remove a stray debug console.log
- refresh a stale jest-era comment on the @angular/core stub
Replace Jest with Vitest across the workspace. The brain and helm Angular suites
now run in a real browser (Playwright/Chromium via Vitest browser mode) so they
exercise real layout, focus, pointer events and computed styles that jsdom could
not. The cli, tools, mcp, registry and cli-smoke suites run on Vitest's node
environment.
- Add per-project vite configs and switch test targets to @nx/vitest:test
- Convert jest APIs to vi.* and jest module mocks to vi.mock with importOriginal
- Replace jest-axe with a browser-native axe-core helper and toHaveNoViolations
- Regenerate snapshots in vitest format (content unchanged, format-only)
- Remove all jest packages, configs, the preset and the @nx/jest target default
- Install Playwright Chromium in the CI unit job for the browser suites
Also fixes a brain avatar test that asserted inside a fire-and-forget setTimeout,
which passed vacuously under jsdom and only surfaced once a real browser loaded
the image.
* fix(cli): avoid @nx/workspace barrel import in migrate-helm-libraries
Import removeGenerator from its module directly instead of the @nx/workspace
barrel. The barrel eagerly evaluates convert-to-nx-project, whose output.js
crashes on nx 22 ("Cannot read properties of undefined (reading 'inverse')"),
so the generator threw before doing any work.
Add a regression test that runs the generator and asserts it does not throw.
* test(cli): tidy migrate-helm-libraries regression test
Assert the generator resolves to undefined instead of using the
resolves.not.toThrow matcher, which does not fit a Promise<void>, and
restore mocks after each test to keep the suite hermetic.
* test(sidebar): update generate-ui-docs snapshot for HlmSidebarTrigger
The HlmSidebarTrigger gained an `srOnlyText` input, but the generate-ui-docs
stable-output snapshot was never regenerated, so `tools:test` failed whenever
it ran uncached. Regenerate the snapshot to match the current component API.
spartan/ui is now Tailwind CSS v4 only. The v3 compatibility layer added
complexity across the brain preset, the CLI generators and the docs, all of
which is removed here.
Brain:
- Delete the v3 JavaScript preset (hlm-tailwind-preset.js) and its package
export; the v4 CSS preset (hlm-tailwind-preset.css) is now the only preset.
- Bump the tailwindcss peer dependency to >=4.0.0 and declare tw-animate-css
as a peer, since it is imported by the CSS preset.
CLI:
- Remove Tailwind version detection and the v3 confirmation prompt; the theme
generator always emits the v4 stylesheet (preset import plus OKLCH theme).
- Drop the legacy HSL theme table, the v3-only CDK overlay import branch, the
vestigial addCdkStyles option and the unused registryItemTailwindSchema.
Docs and tooling:
- Remove the Tailwind v3 installation and theming sections and snippets.
- Drop autoprefixer; Tailwind v4 handles vendor prefixing via @tailwindcss/postcss.
BREAKING CHANGE: Tailwind CSS v3 is no longer supported. The
@spartan-ng/brain/hlm-tailwind-preset (JS) package export has been removed; use
the @spartan-ng/brain/hlm-tailwind-preset.css preset with Tailwind CSS v4. The
tailwindcss peer dependency now requires >=4.0.0.
- Remove the acceptTailwindV3 flag from the theme and init generators. It was a leftover from
when the matrix covered Tailwind v3; the suite is v4-only and v3 support is being dropped.
- Publish packages via execFileSync with an argument array so the registry and npmrc values
are never interpreted by a shell.
- Drop the explanatory embla-carousel import comments from the carousel template and helm source.
- await the local-registry stop in global-teardown and the global-setup error
path, so verdaccio is fully stopped before the process exits
- restore the dist package.json byte-for-byte after publishing, so a local
`pnpm smoke` leaves the build artifacts untouched
- pass the nx affected args via execFileSync (no shell) so NX_BASE/NX_HEAD are
never shell-interpreted
- note that setupTailwindCss is an internal-only theme option
The generated scroll-area directive imports provideScrollbarOptions from
ngx-scrollbar, but the ui generator did not install it, so a fresh scroll-area
failed to compile (TS2307). Add it to the scroll-area peer dependencies.
embla-carousel-angular v22 no longer re-exports EmblaOptionsType,
EmblaEventType and EmblaPluginType, so a fresh carousel failed to compile
(TS2459). Import them from the base embla-carousel package, which exports them
in both v21 and v22, and add it as a peer dependency.
The generators assumed a tsconfig.base.json, so they failed on a standalone nx
workspace (single app, no base tsconfig) with "File not found:
tsconfig.base.json". Resolve the root tsconfig with getRootTsConfigPathInTree
instead, and stop tying the eslint-editing ts-morph Project to an on-disk
tsconfig that may not exist yet during generation.
Passing --theme opts the init and ui-theme generators into non-interactive
mode: the project is auto-selected when there is a single app, the styles
entry point is auto-detected, and the Tailwind v3 warning is skipped. This
lets the CLI be scripted in CI. Prompts are unchanged when no theme is passed.
Adds unit tests for the non-interactive paths.
Address review feedback on the PR. Wire the directive to ngx-scrollbar's
real CSS variables at shadcn's values (transparent 10px track, 7px
bg-border thumb) and default visibility to hover so the thumb only shows
on hover, scroll or focus like shadcn.
Move the container radius into the registry per style now that the demo
no longer hardcodes it: rounded-md for vega/nova/mira, square for lyra
via --radius 0, and rounded-2xl for maia/luma. Replace the dead
radix-era marker rules with a single spartan-scroll-area rule.
Declare ngx-scrollbar as a helm peer dependency since the directive now
imports from it, and mirror the changes in the CLI template.