Skip to content

Repository files navigation

ng-stylex

Kompilowany, atomowy CSS dla Angulara w duchu StyleX: style w *.sx.ts, zerowy runtime kompilacji, deterministyczne klasy, @layer zamiast wojen specyficzności. Status: spike (etap 5a planu). Nie używać produkcyjnie.

Spike 5a: zerowy runtime w stock Angular CLI

Weryfikacja hipotezy: custom builder (wrapper na @angular/build) + plugin esbuild onResolve/onLoad kompiluje *.sx.ts przed bundlowaniem, bez dotykania ngtsc.

Wynik (Angular 22.1.5, @angular/build 22.1.5, Node 22, 2026-09-08)

Kryterium Status
ng build dev i prod przechodzi OK
bundle nie zawiera sx.create( ani stubu runtime (tree-shaking) OK
style jako literały {$$css:!0,display:"sx-149y8yo",...} OK
CSS z @layer ngsx.base, ngsx.pseudo w styles.css OK
błąd kompilacji z pozycją dla nie-literału OK
sourcemapa wskazuje na src/app/app.sx.ts OK
ng serve: edycja .sx.ts -> nowy kod i CSS w ~2 s OK
ng serve: nowy plik .sx.ts bez restartu OK
Playwright e2e nie uruchomiono w sandboxie (brak dostępu do CDN z przeglądarkami); npm run e2e

Jak uruchomić

npm install          # postinstall kopiuje schematy builderów z @angular/build
npm run build        # prod
npm start            # ng serve
npm run e2e          # Playwright (wymaga: npx playwright install chromium)

Ustalenia, które zmieniły projekt względem planu

  1. executeDevServerBuilder(options, context, extensions?), bez builderSelector. Nazwa buildera z buildTarget służy tylko do walidacji opcji schematem, dlatego scripts/sync-schema.cjs kopiuje schema.json z @angular/build (jak custom-esbuild).
  2. Namespace file, ścieżka bez .ts. onLoad Angulara nie podaje namespace, więc wg dokumentacji esbuild działa w każdym. Ratuje nas filtr po rozszerzeniu: src/app/app.sx nie pasuje do regexa Angulara. Własny namespace odrzucony, bo klucz metafile ns:/abs/path trafia przez join(workspaceRoot, input) do watchera jako bogus ścieżka.
  3. Generowany CSS nie może leżeć w dot-katalogu. Watcher Angulara ignoruje ${workspaceRoot}/**/.*/**. .ngsx/ngsx.css nigdy nie było obserwowane, stąd src/ngsx.generated.css (w .gitignore).
  4. Pre-pass jest konieczny dla ng build. Kontekst globalnych stylów buduje się równolegle z kodem; bez wcześniejszego zapisu CSS pierwszy build miałby pusty arkusz.
  5. Koszt watch: dwie przebudowy na edycję (kod, potem styles po zapisie CSS w onEnd). Obie poniżej 0,5 s. Zapis tylko przy zmianie treści, inaczej pętla.

Zakres kompilatora (celowo minimalny)

Literały string/number, warunki { default, ':pseudo' }, jednostka px dla liczb poza listą unitless. Brak: zmienne, spread, defineVars, media queries, style dynamiczne. Hash FNV-1a (spike); docelowo murmurhash3 w @ngsx/shared.

Struktura

tools/ngsx-builder/src/
  compiler.cjs        ewaluacja sx.create -> literał + reguły CSS (magic-string, TS API)
  esbuild-plugin.cjs  onResolve ./x.sx -> onLoad kompilacja, watchFiles, onEnd zapis CSS
  bootstrap.cjs       pre-pass + zapis CSS przed startem builda
  application.cjs    buildApplication(options, context, { codePlugins })
  dev-server.cjs      executeDevServerBuilder(options, context, { buildPlugins })
src/ngsx/core.ts      create() tylko dla typów + attrs() (jedyny runtime, ~20 linii)
src/ngsx/sx.directive.ts  [sx] -> host [class]
src/app/app.sx.ts     style testowe; app.ts używa [sx]
e2e/spike.spec.ts     computed style, :hover, merge, brak sx.create w bundlu

Znane ograniczenia spike'u

  • Ścieżka src/app/app.sx (bez .ts) trafia do watchFiles Angulara jako nieistniejący plik. Nieszkodliwe, ale do obserwacji przy większej liczbie plików.
  • Pliki .sx.ts muszą być w tsconfig.app.json include, żeby ngtsc je typował.
  • ng test (unit-test builder) nie ma jeszcze wrappera.

About

Compiled atomic CSS for Angular, inspired by StyleX

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages