@lovrozagar/flare 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +11 -0
- package/package.json +146 -0
- package/scripts/download-fonts.ts +136 -0
- package/scripts/populate-fonts.ts +721 -0
- package/src/await.ts +2 -0
- package/src/boundaries/index.ts +99 -0
- package/src/broadcast/channel.ts +65 -0
- package/src/broadcast/context.ts +12 -0
- package/src/broadcast/index.ts +75 -0
- package/src/broadcast/provider.tsx +7 -0
- package/src/build-location.ts +2 -0
- package/src/caches/index.ts +384 -0
- package/src/client/index.ts +75 -0
- package/src/client.tsx +2 -0
- package/src/codegen/index.ts +41 -0
- package/src/components/await.tsx +140 -0
- package/src/components/dev-error-overlay.tsx +154 -0
- package/src/components/direction-script.tsx +22 -0
- package/src/components/index.ts +150 -0
- package/src/components/locale-script.tsx +26 -0
- package/src/components/reset-css.tsx +14 -0
- package/src/components/ssr-context.tsx +37 -0
- package/src/components/theme-script.tsx +22 -0
- package/src/components/view-transition-css.tsx +21 -0
- package/src/config/index.ts +84 -0
- package/src/create-client-only-fn.ts +1 -0
- package/src/create-isomorphic-fn.ts +2 -0
- package/src/create-layout.ts +1 -0
- package/src/create-page.ts +1 -0
- package/src/create-path-segment.ts +1 -0
- package/src/create-query-client-getter.ts +2 -0
- package/src/create-root-layout.ts +1 -0
- package/src/create-router.ts +8 -0
- package/src/create-server-only-fn.ts +1 -0
- package/src/dedupe/index.ts +151 -0
- package/src/defer/index.ts +82 -0
- package/src/direction/index.tsx +163 -0
- package/src/direction.ts +3 -0
- package/src/duration/index.ts +18 -0
- package/src/env-fn/index.ts +101 -0
- package/src/env-fn.ts +2 -0
- package/src/errors/index.ts +166 -0
- package/src/fonts/abeezee.ts +50 -0
- package/src/fonts/abel.ts +26 -0
- package/src/fonts/abril-fatface.ts +34 -0
- package/src/fonts/acme.ts +26 -0
- package/src/fonts/advent-pro.ts +92 -0
- package/src/fonts/alegreya-sans.ts +228 -0
- package/src/fonts/alegreya.ts +228 -0
- package/src/fonts/alex-brush.ts +42 -0
- package/src/fonts/alfa-slab-one.ts +42 -0
- package/src/fonts/alice.ts +48 -0
- package/src/fonts/amatic-sc.ts +94 -0
- package/src/fonts/amiri.ts +114 -0
- package/src/fonts/antic-slab.ts +26 -0
- package/src/fonts/anton.ts +42 -0
- package/src/fonts/archivo-black.ts +34 -0
- package/src/fonts/archivo-narrow.ts +114 -0
- package/src/fonts/archivo.ts +66 -0
- package/src/fonts/arimo.ts +256 -0
- package/src/fonts/arvo.ts +50 -0
- package/src/fonts/asap.ts +66 -0
- package/src/fonts/assistant.ts +64 -0
- package/src/fonts/baloo-2.ts +82 -0
- package/src/fonts/barlow-condensed.ts +114 -0
- package/src/fonts/barlow-semi-condensed.ts +114 -0
- package/src/fonts/barlow.ts +114 -0
- package/src/fonts/be-vietnam-pro.ts +114 -0
- package/src/fonts/bebas-neue.ts +34 -0
- package/src/fonts/besley.ts +82 -0
- package/src/fonts/biorhyme.ts +50 -0
- package/src/fonts/bitter.ts +94 -0
- package/src/fonts/black-ops-one.ts +49 -0
- package/src/fonts/bree-serif.ts +34 -0
- package/src/fonts/bricolage-grotesque.ts +66 -0
- package/src/fonts/cabin.ts +114 -0
- package/src/fonts/cairo.ts +66 -0
- package/src/fonts/cardo.ts +193 -0
- package/src/fonts/catamaran.ts +41 -0
- package/src/fonts/caveat.ts +78 -0
- package/src/fonts/chakra-petch.ts +142 -0
- package/src/fonts/chivo.ts +66 -0
- package/src/fonts/cinzel.ts +50 -0
- package/src/fonts/comfortaa.ts +109 -0
- package/src/fonts/commissioner.ts +64 -0
- package/src/fonts/component.tsx +27 -0
- package/src/fonts/cormorant-garamond.ts +171 -0
- package/src/fonts/cormorant.ts +170 -0
- package/src/fonts/courgette.ts +34 -0
- package/src/fonts/courier-prime.ts +82 -0
- package/src/fonts/create-font.ts +47 -0
- package/src/fonts/create-registry-font.ts +40 -0
- package/src/fonts/crete-round.ts +50 -0
- package/src/fonts/crimson-text.ts +114 -0
- package/src/fonts/css.ts +86 -0
- package/src/fonts/dancing-script.ts +66 -0
- package/src/fonts/dm-mono.ts +50 -0
- package/src/fonts/dm-sans.ts +50 -0
- package/src/fonts/dm-serif-display.ts +50 -0
- package/src/fonts/domine.ts +50 -0
- package/src/fonts/dosis.ts +66 -0
- package/src/fonts/eb-garamond.ts +228 -0
- package/src/fonts/eczar.ts +94 -0
- package/src/fonts/encode-sans.ts +42 -0
- package/src/fonts/epilogue.ts +66 -0
- package/src/fonts/exo-2.ts +94 -0
- package/src/fonts/exo.ts +66 -0
- package/src/fonts/figtree.ts +82 -0
- package/src/fonts/fira-code.ts +121 -0
- package/src/fonts/fira-sans-condensed.ts +228 -0
- package/src/fonts/fira-sans.ts +228 -0
- package/src/fonts/fjalla-one.ts +49 -0
- package/src/fonts/font-urls.gen.json +1962 -0
- package/src/fonts/fraunces.ts +66 -0
- package/src/fonts/fredoka.ts +64 -0
- package/src/fonts/gelasio.ts +114 -0
- package/src/fonts/gloria-hallelujah.ts +34 -0
- package/src/fonts/gothic-a1.ts +124 -0
- package/src/fonts/great-vibes.ts +64 -0
- package/src/fonts/gudea.ts +66 -0
- package/src/fonts/heebo.ts +57 -0
- package/src/fonts/hind-siliguri.ts +66 -0
- package/src/fonts/hind.ts +66 -0
- package/src/fonts/ibm-plex-mono.ts +172 -0
- package/src/fonts/ibm-plex-sans-arabic.ts +80 -0
- package/src/fonts/ibm-plex-sans.ts +199 -0
- package/src/fonts/ibm-plex-serif.ts +171 -0
- package/src/fonts/inconsolata.ts +66 -0
- package/src/fonts/index.ts +4 -0
- package/src/fonts/indie-flower.ts +34 -0
- package/src/fonts/instrument-sans.ts +82 -0
- package/src/fonts/instrument-serif.ts +50 -0
- package/src/fonts/inter-display.ts +52 -0
- package/src/fonts/inter-tight.ts +124 -0
- package/src/fonts/inter.ts +123 -0
- package/src/fonts/jetbrains-mono.ts +199 -0
- package/src/fonts/josefin-sans.ts +114 -0
- package/src/fonts/josefin-slab.ts +50 -0
- package/src/fonts/jost.ts +64 -0
- package/src/fonts/kalam.ts +66 -0
- package/src/fonts/kanit.ts +142 -0
- package/src/fonts/karla.ts +82 -0
- package/src/fonts/khand.ts +66 -0
- package/src/fonts/lato.ts +82 -0
- package/src/fonts/league-spartan.ts +42 -0
- package/src/fonts/lexend-deca.ts +42 -0
- package/src/fonts/lexend.ts +42 -0
- package/src/fonts/libre-baskerville.ts +82 -0
- package/src/fonts/libre-franklin.ts +95 -0
- package/src/fonts/lilita-one.ts +34 -0
- package/src/fonts/lobster-two.ts +50 -0
- package/src/fonts/lobster.ts +56 -0
- package/src/fonts/lora.ts +235 -0
- package/src/fonts/m-plus-rounded-1c.ts +138 -0
- package/src/fonts/manrope.ts +109 -0
- package/src/fonts/maven-pro.ts +66 -0
- package/src/fonts/merriweather-sans.ts +142 -0
- package/src/fonts/merriweather.ts +171 -0
- package/src/fonts/montserrat-alternates.ts +171 -0
- package/src/fonts/montserrat.ts +94 -0
- package/src/fonts/mukta.ts +66 -0
- package/src/fonts/mulish.ts +170 -0
- package/src/fonts/nanum-gothic.ts +34 -0
- package/src/fonts/nanum-myeongjo.ts +34 -0
- package/src/fonts/neuton.ts +66 -0
- package/src/fonts/newsreader.ts +114 -0
- package/src/fonts/noticia-text.ts +114 -0
- package/src/fonts/noto-sans-arabic.ts +58 -0
- package/src/fonts/noto-sans-hk.ts +49 -0
- package/src/fonts/noto-sans-jp.ts +49 -0
- package/src/fonts/noto-sans-kr.ts +49 -0
- package/src/fonts/noto-sans-sc.ts +49 -0
- package/src/fonts/noto-sans-tc.ts +49 -0
- package/src/fonts/noto-sans.ts +140 -0
- package/src/fonts/noto-serif-jp.ts +80 -0
- package/src/fonts/noto-serif.ts +140 -0
- package/src/fonts/nunito-sans.ts +170 -0
- package/src/fonts/nunito.ts +170 -0
- package/src/fonts/old-standard-tt.ts +133 -0
- package/src/fonts/onest.ts +48 -0
- package/src/fonts/open-sans.ts +340 -0
- package/src/fonts/orbitron.ts +34 -0
- package/src/fonts/oswald.ts +94 -0
- package/src/fonts/outfit.ts +34 -0
- package/src/fonts/overpass.ts +94 -0
- package/src/fonts/oxygen.ts +50 -0
- package/src/fonts/pacifico.ts +56 -0
- package/src/fonts/passion-one.ts +50 -0
- package/src/fonts/pathway-gothic-one.ts +34 -0
- package/src/fonts/permanent-marker.ts +26 -0
- package/src/fonts/philosopher.ts +172 -0
- package/src/fonts/play.ts +109 -0
- package/src/fonts/playfair-display.ts +142 -0
- package/src/fonts/plus-jakarta-sans.ts +142 -0
- package/src/fonts/poppins.ts +114 -0
- package/src/fonts/prompt.ts +142 -0
- package/src/fonts/pt-sans-narrow.ts +78 -0
- package/src/fonts/pt-sans.ts +138 -0
- package/src/fonts/pt-serif.ts +138 -0
- package/src/fonts/public-sans.ts +66 -0
- package/src/fonts/questrial.ts +42 -0
- package/src/fonts/quicksand.ts +66 -0
- package/src/fonts/rajdhani.ts +66 -0
- package/src/fonts/raleway.ts +94 -0
- package/src/fonts/recursive.ts +80 -0
- package/src/fonts/red-hat-display.ts +82 -0
- package/src/fonts/red-rose.ts +66 -0
- package/src/fonts/registry.gen.ts +173 -0
- package/src/fonts/righteous.ts +34 -0
- package/src/fonts/roboto-condensed.ts +124 -0
- package/src/fonts/roboto-flex.ts +64 -0
- package/src/fonts/roboto-mono.ts +199 -0
- package/src/fonts/roboto-slab.ts +72 -0
- package/src/fonts/roboto.ts +156 -0
- package/src/fonts/rokkitt.ts +66 -0
- package/src/fonts/rubik.ts +199 -0
- package/src/fonts/sacramento.ts +34 -0
- package/src/fonts/saira.ts +66 -0
- package/src/fonts/sarabun.ts +142 -0
- package/src/fonts/satisfy.ts +26 -0
- package/src/fonts/sawarabi-mincho.ts +41 -0
- package/src/fonts/shadows-into-light.ts +34 -0
- package/src/fonts/signika-negative.ts +66 -0
- package/src/fonts/signika.ts +66 -0
- package/src/fonts/slabo-27px.ts +34 -0
- package/src/fonts/sora.ts +50 -0
- package/src/fonts/source-code-pro.ts +228 -0
- package/src/fonts/source-sans-3.ts +228 -0
- package/src/fonts/source-serif-4.ts +199 -0
- package/src/fonts/space-grotesk.ts +66 -0
- package/src/fonts/space-mono.ts +114 -0
- package/src/fonts/spectral.ts +170 -0
- package/src/fonts/tajawal.ts +50 -0
- package/src/fonts/teko.ts +66 -0
- package/src/fonts/tenor-sans.ts +41 -0
- package/src/fonts/tinos.ts +256 -0
- package/src/fonts/titillium-web.ts +82 -0
- package/src/fonts/types.ts +43 -0
- package/src/fonts/ubuntu-condensed.ts +63 -0
- package/src/fonts/ubuntu-mono.ts +195 -0
- package/src/fonts/ubuntu.ts +195 -0
- package/src/fonts/unbounded.ts +94 -0
- package/src/fonts/unicode-ranges.gen.ts +28 -0
- package/src/fonts/urbanist.ts +50 -0
- package/src/fonts/varela-round.ts +49 -0
- package/src/fonts/vollkorn.ts +199 -0
- package/src/fonts/work-sans.ts +66 -0
- package/src/fonts/yanone-kaffeesatz.ts +128 -0
- package/src/fonts/yantramanav.ts +66 -0
- package/src/fonts/yellowtail.ts +34 -0
- package/src/fonts/young-serif.ts +34 -0
- package/src/fonts/zen-kaku-gothic-new.ts +64 -0
- package/src/fonts/zilla-slab.ts +82 -0
- package/src/form/index.tsx +205 -0
- package/src/generators/index.ts +1589 -0
- package/src/head-client/index.ts +649 -0
- package/src/history/index.ts +210 -0
- package/src/hooks.ts +61 -0
- package/src/hydrate/index.tsx +381 -0
- package/src/hydration/index.ts +187 -0
- package/src/i18n/index.ts +250 -0
- package/src/i18n/jsx.tsx +42 -0
- package/src/i18n/message-format.ts +174 -0
- package/src/image/index.tsx +473 -0
- package/src/image.ts +12 -0
- package/src/intercept-outlet/index.tsx +18 -0
- package/src/intercept-outlet.ts +2 -0
- package/src/internal/index.ts +143 -0
- package/src/internal/once-idle.ts +24 -0
- package/src/internal.ts +12 -0
- package/src/lazy/index.tsx +205 -0
- package/src/link/index.tsx +367 -0
- package/src/loader-pipeline/index.ts +639 -0
- package/src/locale/index.tsx +137 -0
- package/src/locale.ts +3 -0
- package/src/logger.ts +22 -0
- package/src/middleware/builtins/api-proxy.ts +64 -0
- package/src/middleware/builtins/cdn-proxy.ts +58 -0
- package/src/middleware/builtins/i18n.ts +256 -0
- package/src/middleware/builtins/keepalive.ts +24 -0
- package/src/middleware/builtins/markdown-negotiation.ts +141 -0
- package/src/middleware/builtins/static-assets.ts +32 -0
- package/src/middleware/index.ts +223 -0
- package/src/module-graph/index.ts +181 -0
- package/src/mount/index.ts +146 -0
- package/src/navigate.ts +1 -0
- package/src/navigation/index.ts +1278 -0
- package/src/navigation/types.ts +59 -0
- package/src/navigation-progress/index.tsx +247 -0
- package/src/navigation-progress.ts +1 -0
- package/src/ndjson-client/index.ts +376 -0
- package/src/ndjson-server/index.ts +284 -0
- package/src/outlet/index.tsx +633 -0
- package/src/outlet/types.ts +243 -0
- package/src/plugins/css-transform.ts +70 -0
- package/src/plugins/dev-cdn-cache.ts +454 -0
- package/src/plugins/dev-dashboard/client.tsx +2603 -0
- package/src/plugins/dev-dashboard/match-route-tree.ts +159 -0
- package/src/plugins/dev-dashboard/plugin.ts +404 -0
- package/src/plugins/dev-prerender.ts +168 -0
- package/src/plugins/dev-server.ts +327 -0
- package/src/plugins/generate-watch.ts +32 -0
- package/src/plugins/image-plugin.ts +171 -0
- package/src/plugins/index.ts +445 -0
- package/src/plugins/options.ts +37 -0
- package/src/plugins/parse-utils.ts +59 -0
- package/src/plugins/prerender-plugin.ts +144 -0
- package/src/plugins/purge.ts +242 -0
- package/src/plugins/server-fn.ts +641 -0
- package/src/plugins/service-worker.ts +225 -0
- package/src/plugins/solid-jsx.d.ts +12 -0
- package/src/plugins/sx-ast/atomic.ts +166 -0
- package/src/plugins/sx-ast/evaluator.ts +235 -0
- package/src/plugins/sx-ast/index.ts +372 -0
- package/src/plugins/sx-ast/parser.ts +22 -0
- package/src/plugins/sx-ast/rewrite.ts +763 -0
- package/src/plugins/sx.d.ts +14 -0
- package/src/plugins/tw-compile.ts +240 -0
- package/src/plugins/types.ts +41 -0
- package/src/plugins/virtual.d.ts +110 -0
- package/src/plugins/virtual.ts +184 -0
- package/src/preload/index.ts +77 -0
- package/src/prerender/index.ts +431 -0
- package/src/query-client/index.tsx +168 -0
- package/src/registry/index.ts +88 -0
- package/src/reset-css.ts +1 -0
- package/src/revalidation/index.ts +84 -0
- package/src/rewrite/index.ts +83 -0
- package/src/route-builder/create-layout.ts +655 -0
- package/src/route-builder/create-page.ts +806 -0
- package/src/route-builder/create-path-segment.ts +34 -0
- package/src/route-builder/create-root-layout.ts +608 -0
- package/src/route-builder/index.ts +49 -0
- package/src/route-builder/register.ts +242 -0
- package/src/route-builder/types.ts +294 -0
- package/src/router-config/index.ts +91 -0
- package/src/router-primitives/index.ts +25 -0
- package/src/router-primitives/location.ts +29 -0
- package/src/router-primitives/match-id.ts +111 -0
- package/src/router-primitives/paths.ts +109 -0
- package/src/router-primitives/tree.ts +243 -0
- package/src/router-primitives/types.ts +74 -0
- package/src/router.ts +31 -0
- package/src/search-engine/bing.ts +32 -0
- package/src/search-engine/google-jwt.ts +95 -0
- package/src/search-engine/google.ts +139 -0
- package/src/search-engine/index-now.ts +64 -0
- package/src/search-engine/index.ts +12 -0
- package/src/security/index.ts +230 -0
- package/src/server/index.ts +286 -0
- package/src/server-context/browser.ts +113 -0
- package/src/server-context/index.ts +217 -0
- package/src/server-fn/index.ts +710 -0
- package/src/server-fn-query.ts +169 -0
- package/src/server-handler/etag.ts +51 -0
- package/src/server-handler/headers.ts +5 -0
- package/src/server-handler/index.ts +1664 -0
- package/src/server-handler/types.ts +24 -0
- package/src/server-handler/validate-static-params.ts +130 -0
- package/src/server.ts +2 -0
- package/src/service-worker/template.ts +153 -0
- package/src/service-worker-hook/index.ts +70 -0
- package/src/sitemap/index.ts +231 -0
- package/src/ssr/critical-css.ts +150 -0
- package/src/ssr/head-prefix.ts +65 -0
- package/src/ssr/head.ts +259 -0
- package/src/ssr/index.tsx +879 -0
- package/src/state-parser/index.ts +266 -0
- package/src/store/filesystem.ts +131 -0
- package/src/store/index.ts +29 -0
- package/src/styles/index.ts +1200 -0
- package/src/styles/sx-types.ts +11 -0
- package/src/testing/index.ts +251 -0
- package/src/testing.ts +2 -0
- package/src/theme/index.tsx +232 -0
- package/src/theme.ts +3 -0
- package/src/tracing/index.ts +4 -0
- package/src/tracing/noop.ts +13 -0
- package/src/tracing/otel.ts +37 -0
- package/src/tracing/timing.ts +34 -0
- package/src/tracing/types.ts +9 -0
- package/src/url/index.ts +140 -0
- package/src/use-router.ts +11 -0
- package/src/use-suspense-query.ts +2 -0
- package/src/validation/index.ts +57 -0
- package/src/view-transition-css.ts +1 -0
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import type { Plugin } from "vite";
|
|
4
|
+
import { extractDeclarations, extractPrefaceCss, initTailwindCompiler } from "../tw-compile.ts";
|
|
5
|
+
import type { TailwindCompiler } from "../tw-compile.ts";
|
|
6
|
+
import { rewriteModule } from "./rewrite.ts";
|
|
7
|
+
|
|
8
|
+
export interface SxAstOptions {
|
|
9
|
+
strict?: boolean;
|
|
10
|
+
/** Absolute path prefixes that map to the "sx" layer (lib code). Default: ["/node_modules/"]. */
|
|
11
|
+
libPaths?: string[];
|
|
12
|
+
/** Override layer detection per module id. Return null to fall back to libPaths heuristic. */
|
|
13
|
+
layerOverride?: (id: string) => "sx" | "app" | null;
|
|
14
|
+
/** Emit flare-sx-manifest.json alongside CSS — for lib builds. */
|
|
15
|
+
manifest?: boolean;
|
|
16
|
+
/** Emit one .css asset per transformed component module — for lib builds. */
|
|
17
|
+
perComponent?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Scan node_modules (and workspace-linked packages) for flare-sx-manifest.json files at
|
|
20
|
+
* buildStart and omit any classes already provided by those libs from this build's CSS output.
|
|
21
|
+
* Prevents duplicate atomic rules when a lib and its consumer both run the sx plugin.
|
|
22
|
+
*/
|
|
23
|
+
pruneFromLibManifests?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Absolute path to a Tailwind CSS entry file (e.g. `src/tailwind.css`).
|
|
26
|
+
* When provided, the plugin compiles Tailwind utility tokens found in `class=` attributes.
|
|
27
|
+
* Omit to disable Tailwind compilation (pass-through mode).
|
|
28
|
+
*/
|
|
29
|
+
twCssPath?: string;
|
|
30
|
+
/**
|
|
31
|
+
* Enable Tailwind compilation using the default `@import "tailwindcss"` entry.
|
|
32
|
+
* Ignored when `twCssPath` is set. Set to `true` to enable with default config.
|
|
33
|
+
*/
|
|
34
|
+
tw?: boolean;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const LAYER_PRELUDE = "@layer reset, sx, app, user.lib, user.app, inline;";
|
|
38
|
+
|
|
39
|
+
/** CSS rule pool accumulated across all transforms in a build. */
|
|
40
|
+
interface PluginState {
|
|
41
|
+
/** className → CSS rule text */
|
|
42
|
+
classPool: Map<string, string>;
|
|
43
|
+
/** className → which @layer it belongs to */
|
|
44
|
+
layerByClass: Map<string, "sx" | "app">;
|
|
45
|
+
/** moduleId → emitted class names */
|
|
46
|
+
moduleManifest: Map<string, Set<string>>;
|
|
47
|
+
/** Classes already shipped by upstream libs — excluded from this build's CSS output. */
|
|
48
|
+
providedByLibs: Set<string>;
|
|
49
|
+
/** Tailwind compiler instance, null if not initialized or init failed. */
|
|
50
|
+
twCompiler: TailwindCompiler | null;
|
|
51
|
+
/**
|
|
52
|
+
* Full Tailwind preamble (theme + base/preflight layers) from a zero-class build.
|
|
53
|
+
* Emitted verbatim before atomic utility rules so browser defaults are normalized.
|
|
54
|
+
*/
|
|
55
|
+
twPrefaceCss: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
interface LibManifestShape {
|
|
59
|
+
classes?: string[];
|
|
60
|
+
rules?: Record<string, unknown>;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Scan node_modules for packages that published a flare-sx-manifest.json.
|
|
65
|
+
* Checks both `<pkg>/flare-sx-manifest.json` and `<pkg>/dist/flare-sx-manifest.json`.
|
|
66
|
+
* Returns the union of all class names found.
|
|
67
|
+
*/
|
|
68
|
+
function scanLibManifests(root: string): Set<string> {
|
|
69
|
+
const provided = new Set<string>();
|
|
70
|
+
const nmDir = join(root, "node_modules");
|
|
71
|
+
if (!existsSync(nmDir)) return provided;
|
|
72
|
+
|
|
73
|
+
let pkgNames: string[];
|
|
74
|
+
try {
|
|
75
|
+
pkgNames = readdirSync(nmDir);
|
|
76
|
+
} catch {
|
|
77
|
+
return provided;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
for (const pkg of pkgNames) {
|
|
81
|
+
/* Scoped packages live one level deeper */
|
|
82
|
+
if (pkg.startsWith("@")) {
|
|
83
|
+
const scopeDir = join(nmDir, pkg);
|
|
84
|
+
let scoped: string[];
|
|
85
|
+
try {
|
|
86
|
+
scoped = readdirSync(scopeDir);
|
|
87
|
+
} catch {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
for (const name of scoped) {
|
|
91
|
+
collectFromPkg(join(scopeDir, name), provided);
|
|
92
|
+
}
|
|
93
|
+
} else {
|
|
94
|
+
collectFromPkg(join(nmDir, pkg), provided);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return provided;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function collectFromPkg(pkgDir: string, out: Set<string>): void {
|
|
102
|
+
for (const rel of ["flare-sx-manifest.json", "dist/flare-sx-manifest.json"]) {
|
|
103
|
+
const p = join(pkgDir, rel);
|
|
104
|
+
try {
|
|
105
|
+
const raw = readFileSync(p, "utf-8");
|
|
106
|
+
const m = JSON.parse(raw) as LibManifestShape;
|
|
107
|
+
/* Support both {classes:[]} and {rules:{cls:rule}} manifest shapes */
|
|
108
|
+
if (Array.isArray(m.classes)) {
|
|
109
|
+
for (const cls of m.classes) out.add(cls);
|
|
110
|
+
} else if (m.rules && typeof m.rules === "object") {
|
|
111
|
+
for (const cls of Object.keys(m.rules)) out.add(cls);
|
|
112
|
+
}
|
|
113
|
+
} catch {
|
|
114
|
+
/* package doesn't have a manifest — fine */
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function resolveLayer(
|
|
120
|
+
id: string,
|
|
121
|
+
libPaths: string[],
|
|
122
|
+
override: ((id: string) => "sx" | "app" | null) | undefined,
|
|
123
|
+
): "sx" | "app" {
|
|
124
|
+
if (override) {
|
|
125
|
+
const result = override(id);
|
|
126
|
+
if (result !== null) return result;
|
|
127
|
+
}
|
|
128
|
+
for (const prefix of libPaths) {
|
|
129
|
+
if (id.includes(prefix)) return "sx";
|
|
130
|
+
}
|
|
131
|
+
return "app";
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** Compose the final CSS text from the class pool, wrapped in @layer blocks. */
|
|
135
|
+
function composeCss(
|
|
136
|
+
classPool: Map<string, string>,
|
|
137
|
+
layerByClass: Map<string, "sx" | "app">,
|
|
138
|
+
skip: Set<string>,
|
|
139
|
+
twPrefaceCss: string,
|
|
140
|
+
): string {
|
|
141
|
+
const sxRules: string[] = [];
|
|
142
|
+
const appRules: string[] = [];
|
|
143
|
+
|
|
144
|
+
for (const [cls, rule] of classPool) {
|
|
145
|
+
if (skip.has(cls)) continue;
|
|
146
|
+
/* istanbul ignore next -- layerByClass is always set alongside classPool in cssEmit */
|
|
147
|
+
const layer = layerByClass.get(cls) ?? "app";
|
|
148
|
+
if (layer === "sx") sxRules.push(rule);
|
|
149
|
+
else appRules.push(rule);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const parts: string[] = [];
|
|
153
|
+
if (twPrefaceCss) parts.push(twPrefaceCss);
|
|
154
|
+
parts.push(LAYER_PRELUDE);
|
|
155
|
+
if (sxRules.length > 0) parts.push(`@layer sx { ${sxRules.join(" ")} }`);
|
|
156
|
+
if (appRules.length > 0) parts.push(`@layer app { ${appRules.join(" ")} }`);
|
|
157
|
+
|
|
158
|
+
return parts.join("\n");
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const DEV_CSS_VIRTUAL_ID = "virtual:flare-sx-dev-css";
|
|
162
|
+
const DEV_CSS_RESOLVED_ID = "\0virtual:flare-sx-dev-css";
|
|
163
|
+
|
|
164
|
+
export function createSxAstPlugin(opts: SxAstOptions = {}, assetsBase: string = "/assets"): Plugin {
|
|
165
|
+
const libPaths = opts.libPaths ?? ["/node_modules/"];
|
|
166
|
+
/* On-disk dir mirrors URL prefix — emit must land where bundleHref points. */
|
|
167
|
+
const assetsDir = assetsBase === "" ? "assets" : assetsBase.slice(1);
|
|
168
|
+
const state: PluginState = {
|
|
169
|
+
classPool: new Map(),
|
|
170
|
+
layerByClass: new Map(),
|
|
171
|
+
moduleManifest: new Map(),
|
|
172
|
+
providedByLibs: new Set(),
|
|
173
|
+
twCompiler: null,
|
|
174
|
+
twPrefaceCss: "",
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
let mode: "dev" | "prod" = "dev";
|
|
178
|
+
let root = process.cwd();
|
|
179
|
+
|
|
180
|
+
return {
|
|
181
|
+
async buildStart(this: { environment?: { config?: { root?: string } } }) {
|
|
182
|
+
root = this.environment?.config?.root ?? process.cwd();
|
|
183
|
+
if (opts.pruneFromLibManifests) {
|
|
184
|
+
state.providedByLibs = scanLibManifests(root);
|
|
185
|
+
}
|
|
186
|
+
if ((opts.tw || opts.twCssPath) && state.twCompiler === null) {
|
|
187
|
+
try {
|
|
188
|
+
state.twCompiler = await initTailwindCompiler(opts.twCssPath);
|
|
189
|
+
/* Zero-class build captures theme vars + preflight (base layer) verbatim. */
|
|
190
|
+
state.twPrefaceCss = extractPrefaceCss(state.twCompiler.build([]));
|
|
191
|
+
} catch (e) {
|
|
192
|
+
/* Warn but don't fail the build — class= tokens pass through without CSS emit. */
|
|
193
|
+
console.warn(`[flare:sx-ast] Tailwind compiler init failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
|
|
198
|
+
configResolved(config) {
|
|
199
|
+
mode = config.command === "build" ? "prod" : "dev";
|
|
200
|
+
root = config.root ?? process.cwd();
|
|
201
|
+
},
|
|
202
|
+
|
|
203
|
+
enforce: "pre",
|
|
204
|
+
|
|
205
|
+
resolveId(id: string): string | null {
|
|
206
|
+
if (id === DEV_CSS_VIRTUAL_ID) return DEV_CSS_RESOLVED_ID;
|
|
207
|
+
return null;
|
|
208
|
+
},
|
|
209
|
+
|
|
210
|
+
load(id: string): { code: string; moduleType: string } | null {
|
|
211
|
+
if (id !== DEV_CSS_RESOLVED_ID) return null;
|
|
212
|
+
/* Each import() re-runs load — no caching — so SSR always gets latest state. */
|
|
213
|
+
const css = composeCss(state.classPool, state.layerByClass, state.providedByLibs, state.twPrefaceCss);
|
|
214
|
+
const classNames = [...state.classPool.keys()];
|
|
215
|
+
return {
|
|
216
|
+
code: `export function getDevSxCss() { return ${JSON.stringify(css)} }\nexport function getDevSxClasses() { return ${JSON.stringify(classNames)} }`,
|
|
217
|
+
moduleType: "js",
|
|
218
|
+
};
|
|
219
|
+
},
|
|
220
|
+
|
|
221
|
+
generateBundle() {
|
|
222
|
+
const self = this as unknown as { emitFile: (f: { type: string; fileName: string; source: string }) => void };
|
|
223
|
+
const css = composeCss(state.classPool, state.layerByClass, state.providedByLibs, state.twPrefaceCss);
|
|
224
|
+
self.emitFile({ fileName: `${assetsDir}/flare-global.css`, source: css, type: "asset" });
|
|
225
|
+
|
|
226
|
+
if (opts.manifest) {
|
|
227
|
+
const rules: Record<string, string> = {};
|
|
228
|
+
const layerByRule: Record<string, "sx" | "app"> = {};
|
|
229
|
+
for (const [cls, rule] of state.classPool) {
|
|
230
|
+
if (state.providedByLibs.has(cls)) continue;
|
|
231
|
+
rules[cls] = rule;
|
|
232
|
+
/* istanbul ignore next -- layerByClass always set alongside classPool */
|
|
233
|
+
layerByRule[cls] = state.layerByClass.get(cls) ?? "app";
|
|
234
|
+
}
|
|
235
|
+
const moduleManifest: Record<string, string[]> = {};
|
|
236
|
+
for (const [modId, clsSet] of state.moduleManifest) {
|
|
237
|
+
const filtered = [...clsSet].filter((c) => !state.providedByLibs.has(c));
|
|
238
|
+
if (filtered.length > 0) moduleManifest[modId] = filtered;
|
|
239
|
+
}
|
|
240
|
+
const manifest = {
|
|
241
|
+
/* bundleHref resolved at runtime from Vite manifest — placeholder during build */
|
|
242
|
+
bundleHref: `${assetsBase}/flare-global.css`,
|
|
243
|
+
hashVersion: "a1",
|
|
244
|
+
layerByRule,
|
|
245
|
+
moduleManifest,
|
|
246
|
+
rules,
|
|
247
|
+
version: 1,
|
|
248
|
+
};
|
|
249
|
+
self.emitFile({
|
|
250
|
+
fileName: "flare-sx-manifest.json",
|
|
251
|
+
source: JSON.stringify(manifest),
|
|
252
|
+
type: "asset",
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
},
|
|
256
|
+
|
|
257
|
+
name: "flare:sx-ast",
|
|
258
|
+
|
|
259
|
+
transform(code: string, id: string): { code: string; map: null } | null {
|
|
260
|
+
if (!id.endsWith(".tsx") && !id.endsWith(".jsx")) return null;
|
|
261
|
+
|
|
262
|
+
/* Quick filter — skip files that obviously have no relevant attrs */
|
|
263
|
+
if (!code.includes("sx=") && !code.includes("css=") && !code.includes("class=")) return null;
|
|
264
|
+
|
|
265
|
+
const layer = resolveLayer(id, libPaths, opts.layerOverride);
|
|
266
|
+
const emittedForModule = new Set<string>();
|
|
267
|
+
const moduleRules: Array<{ cls: string; rule: string }> = [];
|
|
268
|
+
|
|
269
|
+
const tw = state.twCompiler;
|
|
270
|
+
const result = rewriteModule(code, {
|
|
271
|
+
cssEmit: (rule) => {
|
|
272
|
+
/*
|
|
273
|
+
* Extract the first class selector from the rule regardless of wrapping.
|
|
274
|
+
* At-rule wrapped rules (`@media (...) { .cls { ... } }`) have their `.cls`
|
|
275
|
+
* after the opening brace — the leading-dot-only regex missed those entirely.
|
|
276
|
+
* Match the first `.cls` followed by whitespace, `{`, `[`, or `:`.
|
|
277
|
+
*/
|
|
278
|
+
const m = rule.match(/\.((?:[a-zA-Z0-9_-]|\\[^a-zA-Z0-9_-])+?)[\s{[:]/);
|
|
279
|
+
/* istanbul ignore next -- emitAtomic always produces .cls-prefixed rules */
|
|
280
|
+
const cls = m ? m[1].replace(/\\/g, "") : rule.slice(0, 40);
|
|
281
|
+
state.classPool.set(cls, rule);
|
|
282
|
+
state.layerByClass.set(cls, layer);
|
|
283
|
+
emittedForModule.add(cls);
|
|
284
|
+
moduleRules.push({ cls, rule });
|
|
285
|
+
},
|
|
286
|
+
layer,
|
|
287
|
+
mode,
|
|
288
|
+
sourcePath: id,
|
|
289
|
+
twCompile: tw
|
|
290
|
+
? (token: string) => {
|
|
291
|
+
const output = tw.build([token]);
|
|
292
|
+
return extractDeclarations(output, [token], tw.themeVars) || null;
|
|
293
|
+
}
|
|
294
|
+
: undefined,
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
/* Track which classes this module emitted; layer already set in cssEmit above */
|
|
298
|
+
if (result !== null) {
|
|
299
|
+
for (const cls of result.emittedClasses) {
|
|
300
|
+
emittedForModule.add(cls);
|
|
301
|
+
/* c8 ignore next -- static sx always passes through cssEmit first, setting layerByClass */
|
|
302
|
+
if (!state.layerByClass.has(cls)) state.layerByClass.set(cls, layer);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
if (emittedForModule.size > 0) {
|
|
306
|
+
state.moduleManifest.set(id, emittedForModule);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/*
|
|
310
|
+
* Dev mode: inject atomic CSS directly into a <style> element at module execution time.
|
|
311
|
+
* registerCSSAsClass is guarded by domInjectionEnabled (set after hydration) and
|
|
312
|
+
* would silently drop rules emitted at module import time. Direct DOM injection
|
|
313
|
+
* bypasses that gate — safe because dev mode never SSR-hydrates the style sheet.
|
|
314
|
+
* In build mode, rules land in flare-global.css via generateBundle.
|
|
315
|
+
*
|
|
316
|
+
* result === null means the AST needed no code rewrite (pure class= literals, no sx/spread).
|
|
317
|
+
* CSS was still collected into moduleRules via cssEmit — must still inject the snippet.
|
|
318
|
+
*/
|
|
319
|
+
if (mode === "dev" && moduleRules.length > 0) {
|
|
320
|
+
const layerName: "sx" | "app" = layer;
|
|
321
|
+
const perClassJson = JSON.stringify(moduleRules.map(({ cls, rule }) => [cls, rule]));
|
|
322
|
+
/* SSR pre-populates flare-sx-dev with the full atomic payload for the page, plus
|
|
323
|
+
* twPrefaceCss (theme vars + base preflight) and the @layer prelude. Every client
|
|
324
|
+
* module import used to re-append ALL its scanned rules, stacking duplicates —
|
|
325
|
+
* one module's .hidden then landed after another's md:flex, killing `hidden md:flex`.
|
|
326
|
+
*
|
|
327
|
+
* Fix: SSR seeds `window.__flare_sx_classes__` with the full class pool before any
|
|
328
|
+
* module runs. Module inject snippets skip classes already in the Set. Only new
|
|
329
|
+
* classes (e.g. modules loaded after SPA nav to a fresh route) get appended. No
|
|
330
|
+
* destructive sheet reset — SSR-emitted theme + preflight + layer prelude stay
|
|
331
|
+
* intact. */
|
|
332
|
+
const injectSnippet = `
|
|
333
|
+
if (typeof document !== "undefined") {
|
|
334
|
+
const __seen__ = (window.__flare_sx_classes__ ||= new Set());
|
|
335
|
+
let __buf__ = "";
|
|
336
|
+
for (const [__c__, __r__] of ${perClassJson}) {
|
|
337
|
+
if (__seen__.has(__c__)) continue;
|
|
338
|
+
__seen__.add(__c__);
|
|
339
|
+
__buf__ += "@layer ${layerName}{" + __r__ + "}";
|
|
340
|
+
}
|
|
341
|
+
if (__buf__) {
|
|
342
|
+
let __sx_el__ = document.getElementById("flare-sx-dev");
|
|
343
|
+
if (!__sx_el__) {
|
|
344
|
+
__sx_el__ = document.createElement("style");
|
|
345
|
+
__sx_el__.id = "flare-sx-dev";
|
|
346
|
+
document.head.appendChild(__sx_el__);
|
|
347
|
+
}
|
|
348
|
+
__sx_el__.textContent += __buf__;
|
|
349
|
+
}
|
|
350
|
+
}`;
|
|
351
|
+
const baseCode = result !== null ? result.code : code;
|
|
352
|
+
return { code: `${baseCode}\n${injectSnippet}`, map: null };
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (result === null) return null;
|
|
356
|
+
return { code: result.code, map: null };
|
|
357
|
+
},
|
|
358
|
+
|
|
359
|
+
/*
|
|
360
|
+
* Inject an empty <style id="flare-sx-dev"> placeholder into HTML head in dev mode.
|
|
361
|
+
* Works regardless of which SSR plugin (CF Workers, Nitro, built-in) handles requests —
|
|
362
|
+
* the placeholder is in the DOM before any module runs, so the per-module JS snippets
|
|
363
|
+
* that do `__sx_el__.textContent +=` always find the element via getElementById.
|
|
364
|
+
*/
|
|
365
|
+
transformIndexHtml(html: string): string {
|
|
366
|
+
if (mode !== "dev") return html;
|
|
367
|
+
if (!html.includes("</head>")) return html;
|
|
368
|
+
const prefaceTag = state.twPrefaceCss ? `<style id="flare-tw-preface">${state.twPrefaceCss}</style>` : "";
|
|
369
|
+
return html.replace("</head>", `${prefaceTag}<style id="flare-sx-dev"></style></head>`);
|
|
370
|
+
},
|
|
371
|
+
};
|
|
372
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { parseSync } from "oxc-parser";
|
|
2
|
+
import type { Program } from "oxc-parser";
|
|
3
|
+
|
|
4
|
+
export interface ParseResult {
|
|
5
|
+
program: Program;
|
|
6
|
+
comments: Array<{ type: "Line" | "Block"; value: string; start: number; end: number }>;
|
|
7
|
+
diagnostics: Array<{ message: string; start: number; end: number; severity: "error" | "warning" | "advice" }>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function parseSource(source: string, filename = "input.tsx"): ParseResult {
|
|
11
|
+
const result = parseSync(filename, source, { lang: "tsx", preserveParens: false });
|
|
12
|
+
return {
|
|
13
|
+
comments: result.comments,
|
|
14
|
+
diagnostics: result.errors.map((e) => ({
|
|
15
|
+
end: (e.labels[0] as { end?: number } | undefined)?.end ?? 0,
|
|
16
|
+
message: e.message,
|
|
17
|
+
severity: e.severity.toLowerCase() as "error" | "warning" | "advice",
|
|
18
|
+
start: (e.labels[0] as { start?: number } | undefined)?.start ?? 0,
|
|
19
|
+
})),
|
|
20
|
+
program: result.program,
|
|
21
|
+
};
|
|
22
|
+
}
|