@ethisyscore/vite-plugin 1.59.0 → 1.62.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/dist/index.d.cts CHANGED
@@ -312,8 +312,29 @@ interface PlatformReactPluginOptions {
312
312
  * (see `PlatformReactSurfaceMount.tsx` in `coreconnect-web`). This pass only
313
313
  * blocks remote/absolute paths at the manifest layer so a malformed manifest
314
314
  * fails fast.
315
+ *
316
+ * Returns an ARRAY: this pass plus the plugin-CSS scoping pass it must always be
317
+ * paired with. Vite flattens nested plugin arrays (`PluginOption` is recursively
318
+ * `PluginOption[]`), so existing call sites
319
+ * (`plugins: [react(), tailwindcss(), ethisysPlatformReactPlugin()]`) are unaffected.
320
+ *
321
+ * The pairing CANNOT be done by returning `{ plugins: [...] }` from this pass's own
322
+ * `config()` hook: Vite resolves the plugin container before running config hooks, so
323
+ * a plugin injected there is dropped SILENTLY (no warning, build still green) and the
324
+ * scoping pass would never run. Anything needing the page pass on its own as a single
325
+ * `Plugin` should call `ethisysPlatformReactPagesPlugin` and compose the scoping pass
326
+ * itself.
327
+ */
328
+ declare function ethisysPlatformReactPlugin(options?: PlatformReactPluginOptions): Plugin[];
329
+ /**
330
+ * The PlatformReact page-bundle pass ALONE, as a single `Plugin`.
331
+ *
332
+ * Prefer {@link ethisysPlatformReactPlugin}, which pairs this with the plugin-CSS
333
+ * scoping pass. Reach for this only when composing the passes by hand (a plugin with
334
+ * its own scoping step, or a test that drives one pass's hooks directly) — used on its
335
+ * own it emits UNSCOPED CSS, which leaks into the host document.
315
336
  */
316
- declare function ethisysPlatformReactPlugin(options?: PlatformReactPluginOptions): Plugin;
337
+ declare function ethisysPlatformReactPagesPlugin(options?: PlatformReactPluginOptions): Plugin;
317
338
 
318
339
  /**
319
340
  * A fully-validated PlatformReact page declaration. All fields are guaranteed
@@ -534,6 +555,140 @@ declare function virtualPageEntry(page: PlatformReactPageInput, config: {
534
555
  */
535
556
  declare function buildPlatformReactPages(options?: BuildPlatformReactPagesOptions): Promise<PlatformReactBuildSummary>;
536
557
 
558
+ /**
559
+ * Scope selector prefixed onto plugin-global CSS layers.
560
+ *
561
+ * `:where()` NOT `:is()` on purpose: `:where()` contributes ZERO specificity, so a
562
+ * scoped utility keeps exactly the specificity it had unscoped. Inside a plugin
563
+ * surface the cascade therefore behaves identically to before this transform; the
564
+ * only change is that the rule no longer matches host chrome. `:is()` would have
565
+ * raised every utility by one class and could flip a plugin's own conflicts.
566
+ *
567
+ * Two scope roots: the plugin root applied by `definePlatformReactPluginPage`, and
568
+ * the portal container marker, because MUI overlays render into a portal. Phase 1
569
+ * (`PluginPortalScope`) puts that container INSIDE the plugin root, so the second
570
+ * arm is redundant for SDK-managed overlays and exists for a plugin that supplies
571
+ * its own container element and marks it.
572
+ */
573
+ declare const EHX_PLUGIN_CSS_SCOPE = ":where(.ehx-plugin-root, [data-ehx-plugin-portal])";
574
+ /**
575
+ * Replacement for a document-root selector (`:root`, `:host`) in a plugin sheet.
576
+ *
577
+ * Tailwind's `theme` layer declares its scale as custom properties on
578
+ * `:root, :host` — `--spacing`, `--text-sm--line-height`, `--ease-*`,
579
+ * `--animate-*`. Injected into the host document that lands on the HOST root and
580
+ * silently redefines the host's own Tailwind scale for every element on the page:
581
+ * the same defect as the leaking utilities, one level deeper and inherited.
582
+ *
583
+ * These declarations cannot simply be dropped — the plugin's utilities read them —
584
+ * and prefixing is meaningless (`:where(.ehx-plugin-root) :root` matches nothing).
585
+ * They are REPLACED with the plugin root, which the whole surface inherits from.
586
+ *
587
+ * `:is()` here, not `:where()`: this is a substitution for a 0,1,0 selector, so
588
+ * matching that specificity is the faithful choice (a prefix is what must stay
589
+ * specificity-neutral). Note this only works because portal containment landed
590
+ * first — before that, overlays rendered outside the plugin root and would have
591
+ * lost the whole theme scale.
592
+ */
593
+ declare const EHX_PLUGIN_ROOT_SCOPE = ":is(.ehx-plugin-root, [data-ehx-plugin-portal])";
594
+ /**
595
+ * Cascade layers whose rules a plugin emits UNSCOPED and which therefore leak into
596
+ * the host document.
597
+ *
598
+ * utilities Tailwind's generated utility classes. The leak that motivated this:
599
+ * a plugin's bare `.text-sm` is a duplicate of the host's own rule,
600
+ * so it changes nothing until the host settles a specificity tie by
601
+ * source order — then the plugin's copy, injected later, wins and
602
+ * silently restyles host chrome.
603
+ * properties Tailwind's `@property` fallback block, which sets ~40 `--tw-*`
604
+ * custom properties on `*, :before, :after, ::backdrop` for the whole
605
+ * document.
606
+ *
607
+ * The other layers are already scoped: `theme` holds custom properties on the
608
+ * plugin's own root class, and `base`/`components`/`mui` come from the SDK's
609
+ * `plugin-base.css`, which scopes everything to `.ehx-plugin-root`.
610
+ */
611
+ declare const EHX_SCOPED_CSS_LAYERS: readonly string[];
612
+ /**
613
+ * What counts as "scoped to a plugin" — the convention every plugin front-end
614
+ * adheres to, and the only thing the build guard needs to know.
615
+ *
616
+ * .ehx-plugin-root applied to every surface by the SDK page definer
617
+ * .ethisys-<name>-root the plugin's own root class (all plugins use this
618
+ * shape: ethisys-sales-root, ethisys-hr-root, …)
619
+ * [data-ehx-plugin-portal] the portal container overlays render into
620
+ * :where(…) of the above what {@link scopePluginCss} emits
621
+ *
622
+ * A selector that mentions NONE of these can match host chrome, which is the whole
623
+ * defect class. Matching anywhere in the selector (not just leftmost) is deliberate:
624
+ * `:where(.dark) .ethisys-sales-root` is scoped even though the root is not first.
625
+ */
626
+ declare const EHX_PLUGIN_SCOPE_ROOTS: readonly RegExp[];
627
+ interface ScopePluginCssOptions {
628
+ /** Descendant scope prefix. Default {@link EHX_PLUGIN_CSS_SCOPE}. */
629
+ scope?: string;
630
+ /** Replacement for `:root`/`:host`. Default {@link EHX_PLUGIN_ROOT_SCOPE}. */
631
+ rootScope?: string;
632
+ /** Layer names to scope. Default {@link EHX_SCOPED_CSS_LAYERS}. */
633
+ layers?: readonly string[];
634
+ /** What counts as scoped for the guard. Default {@link EHX_PLUGIN_SCOPE_ROOTS}. */
635
+ scopeRoots?: readonly RegExp[];
636
+ /**
637
+ * Guard behaviour when a selector would leak into the host document:
638
+ * `"error"` (default) fails the build, `"warn"` logs, `"off"` skips the check.
639
+ * Only reach for anything but `"error"` as a deliberate, time-boxed exception.
640
+ */
641
+ guard?: "error" | "warn" | "off";
642
+ }
643
+ /**
644
+ * Prefix every selector inside a plugin's UNSCOPED cascade layers with the plugin
645
+ * scope, so injecting the sheet into the host document cannot restyle host chrome.
646
+ *
647
+ * Pure and idempotent — running it twice is a no-op, so a rebuild or a second pass
648
+ * over already-scoped CSS is safe. See {@link EHX_SCOPED_CSS_LAYERS} for which
649
+ * layers are rewritten and why the others need no treatment.
650
+ */
651
+ declare function scopePluginCss(css: string, options?: ScopePluginCssOptions): string;
652
+ /**
653
+ * Every selector in `css` that mentions no plugin scope root, i.e. every selector
654
+ * that can reach host chrome once the sheet is injected into the host document.
655
+ *
656
+ * The invariant this checks is the whole convention: **a plugin never emits an
657
+ * unscoped selector.** {@link scopePluginCss} satisfies it automatically for the
658
+ * layers Tailwind generates; everything else is authored, and this is what catches
659
+ * an author (or a stray `@import "tailwindcss"` re-emitting global preflight) who
660
+ * did not. Pure, so a plugin's own build script or test can call it directly.
661
+ */
662
+ declare function findUnscopedSelectors(css: string, options?: Pick<ScopePluginCssOptions, "scopeRoots">): string[];
663
+ /** The build-failure message: what leaked, why it matters, and the usual cause. */
664
+ declare function formatUnscopedSelectorError(id: string, leaks: readonly string[]): string;
665
+ /** True for ids whose transform payload is plain CSS text we may rewrite. */
666
+ declare function isScopableCssId(id: string): boolean;
667
+ /**
668
+ * Vite plugin form of {@link scopePluginCss} PLUS the guard that makes it a
669
+ * convention rather than a courtesy, injected into every PlatformReact page
670
+ * sub-build by `buildPlatformReactPages`.
671
+ *
672
+ * Scoping Tailwind's generated layers is automatic; the guard fails the build if
673
+ * anything ELSE in the sheet is still unscoped (see
674
+ * {@link findUnscopedSelectors}). Together they are the enforceable form of one
675
+ * rule: a plugin never emits an unscoped selector into the host document. Prose in
676
+ * a standards doc did not hold across seven repos — the leak that prompted this
677
+ * shipped seven times, and the documented no-preflight rule was violated once
678
+ * without anyone noticing.
679
+ *
680
+ * Deliberately declares NO `enforce`. Vite resolves plugins in the order
681
+ * `[…prePlugins, vite:css, …normalPlugins, vite:css-post, …postPlugins]`, so a
682
+ * plugin with no `enforce` is the only position that sees compiled CSS:
683
+ *
684
+ * - `@tailwindcss/vite` is `enforce: "pre"` and returns the generated sheet, so
685
+ * the utilities exist by the time this runs.
686
+ * - `vite:css-post` serialises a `*.css?inline` import into `export default "…"`.
687
+ * `enforce: "post"` runs AFTER that and would be handed JavaScript, where the
688
+ * CSS is an escaped string literal.
689
+ */
690
+ declare function scopePluginCssPlugin(options?: ScopePluginCssOptions): Plugin;
691
+
537
692
  /**
538
693
  * Build-time codegen for a PlatformReact plugin's `feature.manifest.json` +
539
694
  * project-root `extension.manifest.json` overlay, from the plugin's `routeMeta.json`
@@ -807,4 +962,4 @@ interface EthisysPluginOptions {
807
962
  */
808
963
  declare function ethisysManifestPlugin(options?: EthisysPluginOptions): Plugin;
809
964
 
810
- export { type BuildPlatformReactPagesOptions, CONTRACT_B_IMPORT_MAP_ALLOWLIST, CONTRACT_B_RUNTIME_IMPORTS, CONTRACT_B_SEMANTIC_PRIMITIVES, type ComputePlatformReactPagesInput, type ComputePlatformReactPagesResult, type ContractAManifest, type ContractAPluginOptions, type ContractBManifest, type ContractBPluginOptions, type EthisysPluginOptions, type GeneratePlatformReactManifestConfig, type GeneratePlatformReactManifestResult, type IframeBuildContext, type IframeBuildOutputItem, type IframeBuildResult, type IframeSandboxBuildSummary, type IframeSandboxPluginOptions, type IframeViteBuild, type ManifestReactiveRuleRef, type ManifestResourceRef, PLATFORM_REACT_DEDUPE, PLATFORM_REACT_EXTERNALS, type ParsePlatformReactPagesOptions, type PlatformReactAlias, type PlatformReactBuildContext, type PlatformReactBuildSummary, type PlatformReactManifest, type PlatformReactManifestMeta, type PlatformReactManifestPaths, type PlatformReactPageDeclaration, type PlatformReactPageEntry, type PlatformReactPageInput, type PlatformReactPageOverride, type PlatformReactPageSource, type PlatformReactPluginOptions, type PlatformReactViteBuild, type ResolvedPlatformReactPage, type RouteMetaEntry, type ValidationFailure, type ValidationResult, type ViteBuildFn, buildIframeSandboxPages, buildPlatformReactPages, computePlatformReactPages, ethisysContractAPlugin, ethisysContractBPlugin, ethisysIframeSandboxPlugin, ethisysManifestPlugin, ethisysPlatformReactPlugin, generatePlatformReactManifest, parsePlatformReactPages, rewriteAliasedExternalImports, validateDeclarativeResource, validateReactiveRule, virtualPageEntry };
965
+ export { type BuildPlatformReactPagesOptions, CONTRACT_B_IMPORT_MAP_ALLOWLIST, CONTRACT_B_RUNTIME_IMPORTS, CONTRACT_B_SEMANTIC_PRIMITIVES, type ComputePlatformReactPagesInput, type ComputePlatformReactPagesResult, type ContractAManifest, type ContractAPluginOptions, type ContractBManifest, type ContractBPluginOptions, EHX_PLUGIN_CSS_SCOPE, EHX_PLUGIN_ROOT_SCOPE, EHX_PLUGIN_SCOPE_ROOTS, EHX_SCOPED_CSS_LAYERS, type EthisysPluginOptions, type GeneratePlatformReactManifestConfig, type GeneratePlatformReactManifestResult, type IframeBuildContext, type IframeBuildOutputItem, type IframeBuildResult, type IframeSandboxBuildSummary, type IframeSandboxPluginOptions, type IframeViteBuild, type ManifestReactiveRuleRef, type ManifestResourceRef, PLATFORM_REACT_DEDUPE, PLATFORM_REACT_EXTERNALS, type ParsePlatformReactPagesOptions, type PlatformReactAlias, type PlatformReactBuildContext, type PlatformReactBuildSummary, type PlatformReactManifest, type PlatformReactManifestMeta, type PlatformReactManifestPaths, type PlatformReactPageDeclaration, type PlatformReactPageEntry, type PlatformReactPageInput, type PlatformReactPageOverride, type PlatformReactPageSource, type PlatformReactPluginOptions, type PlatformReactViteBuild, type ResolvedPlatformReactPage, type RouteMetaEntry, type ScopePluginCssOptions, type ValidationFailure, type ValidationResult, type ViteBuildFn, buildIframeSandboxPages, buildPlatformReactPages, computePlatformReactPages, ethisysContractAPlugin, ethisysContractBPlugin, ethisysIframeSandboxPlugin, ethisysManifestPlugin, ethisysPlatformReactPagesPlugin, ethisysPlatformReactPlugin, findUnscopedSelectors, formatUnscopedSelectorError, generatePlatformReactManifest, isScopableCssId, parsePlatformReactPages, rewriteAliasedExternalImports, scopePluginCss, scopePluginCssPlugin, validateDeclarativeResource, validateReactiveRule, virtualPageEntry };
package/dist/index.d.ts CHANGED
@@ -312,8 +312,29 @@ interface PlatformReactPluginOptions {
312
312
  * (see `PlatformReactSurfaceMount.tsx` in `coreconnect-web`). This pass only
313
313
  * blocks remote/absolute paths at the manifest layer so a malformed manifest
314
314
  * fails fast.
315
+ *
316
+ * Returns an ARRAY: this pass plus the plugin-CSS scoping pass it must always be
317
+ * paired with. Vite flattens nested plugin arrays (`PluginOption` is recursively
318
+ * `PluginOption[]`), so existing call sites
319
+ * (`plugins: [react(), tailwindcss(), ethisysPlatformReactPlugin()]`) are unaffected.
320
+ *
321
+ * The pairing CANNOT be done by returning `{ plugins: [...] }` from this pass's own
322
+ * `config()` hook: Vite resolves the plugin container before running config hooks, so
323
+ * a plugin injected there is dropped SILENTLY (no warning, build still green) and the
324
+ * scoping pass would never run. Anything needing the page pass on its own as a single
325
+ * `Plugin` should call `ethisysPlatformReactPagesPlugin` and compose the scoping pass
326
+ * itself.
327
+ */
328
+ declare function ethisysPlatformReactPlugin(options?: PlatformReactPluginOptions): Plugin[];
329
+ /**
330
+ * The PlatformReact page-bundle pass ALONE, as a single `Plugin`.
331
+ *
332
+ * Prefer {@link ethisysPlatformReactPlugin}, which pairs this with the plugin-CSS
333
+ * scoping pass. Reach for this only when composing the passes by hand (a plugin with
334
+ * its own scoping step, or a test that drives one pass's hooks directly) — used on its
335
+ * own it emits UNSCOPED CSS, which leaks into the host document.
315
336
  */
316
- declare function ethisysPlatformReactPlugin(options?: PlatformReactPluginOptions): Plugin;
337
+ declare function ethisysPlatformReactPagesPlugin(options?: PlatformReactPluginOptions): Plugin;
317
338
 
318
339
  /**
319
340
  * A fully-validated PlatformReact page declaration. All fields are guaranteed
@@ -534,6 +555,140 @@ declare function virtualPageEntry(page: PlatformReactPageInput, config: {
534
555
  */
535
556
  declare function buildPlatformReactPages(options?: BuildPlatformReactPagesOptions): Promise<PlatformReactBuildSummary>;
536
557
 
558
+ /**
559
+ * Scope selector prefixed onto plugin-global CSS layers.
560
+ *
561
+ * `:where()` NOT `:is()` on purpose: `:where()` contributes ZERO specificity, so a
562
+ * scoped utility keeps exactly the specificity it had unscoped. Inside a plugin
563
+ * surface the cascade therefore behaves identically to before this transform; the
564
+ * only change is that the rule no longer matches host chrome. `:is()` would have
565
+ * raised every utility by one class and could flip a plugin's own conflicts.
566
+ *
567
+ * Two scope roots: the plugin root applied by `definePlatformReactPluginPage`, and
568
+ * the portal container marker, because MUI overlays render into a portal. Phase 1
569
+ * (`PluginPortalScope`) puts that container INSIDE the plugin root, so the second
570
+ * arm is redundant for SDK-managed overlays and exists for a plugin that supplies
571
+ * its own container element and marks it.
572
+ */
573
+ declare const EHX_PLUGIN_CSS_SCOPE = ":where(.ehx-plugin-root, [data-ehx-plugin-portal])";
574
+ /**
575
+ * Replacement for a document-root selector (`:root`, `:host`) in a plugin sheet.
576
+ *
577
+ * Tailwind's `theme` layer declares its scale as custom properties on
578
+ * `:root, :host` — `--spacing`, `--text-sm--line-height`, `--ease-*`,
579
+ * `--animate-*`. Injected into the host document that lands on the HOST root and
580
+ * silently redefines the host's own Tailwind scale for every element on the page:
581
+ * the same defect as the leaking utilities, one level deeper and inherited.
582
+ *
583
+ * These declarations cannot simply be dropped — the plugin's utilities read them —
584
+ * and prefixing is meaningless (`:where(.ehx-plugin-root) :root` matches nothing).
585
+ * They are REPLACED with the plugin root, which the whole surface inherits from.
586
+ *
587
+ * `:is()` here, not `:where()`: this is a substitution for a 0,1,0 selector, so
588
+ * matching that specificity is the faithful choice (a prefix is what must stay
589
+ * specificity-neutral). Note this only works because portal containment landed
590
+ * first — before that, overlays rendered outside the plugin root and would have
591
+ * lost the whole theme scale.
592
+ */
593
+ declare const EHX_PLUGIN_ROOT_SCOPE = ":is(.ehx-plugin-root, [data-ehx-plugin-portal])";
594
+ /**
595
+ * Cascade layers whose rules a plugin emits UNSCOPED and which therefore leak into
596
+ * the host document.
597
+ *
598
+ * utilities Tailwind's generated utility classes. The leak that motivated this:
599
+ * a plugin's bare `.text-sm` is a duplicate of the host's own rule,
600
+ * so it changes nothing until the host settles a specificity tie by
601
+ * source order — then the plugin's copy, injected later, wins and
602
+ * silently restyles host chrome.
603
+ * properties Tailwind's `@property` fallback block, which sets ~40 `--tw-*`
604
+ * custom properties on `*, :before, :after, ::backdrop` for the whole
605
+ * document.
606
+ *
607
+ * The other layers are already scoped: `theme` holds custom properties on the
608
+ * plugin's own root class, and `base`/`components`/`mui` come from the SDK's
609
+ * `plugin-base.css`, which scopes everything to `.ehx-plugin-root`.
610
+ */
611
+ declare const EHX_SCOPED_CSS_LAYERS: readonly string[];
612
+ /**
613
+ * What counts as "scoped to a plugin" — the convention every plugin front-end
614
+ * adheres to, and the only thing the build guard needs to know.
615
+ *
616
+ * .ehx-plugin-root applied to every surface by the SDK page definer
617
+ * .ethisys-<name>-root the plugin's own root class (all plugins use this
618
+ * shape: ethisys-sales-root, ethisys-hr-root, …)
619
+ * [data-ehx-plugin-portal] the portal container overlays render into
620
+ * :where(…) of the above what {@link scopePluginCss} emits
621
+ *
622
+ * A selector that mentions NONE of these can match host chrome, which is the whole
623
+ * defect class. Matching anywhere in the selector (not just leftmost) is deliberate:
624
+ * `:where(.dark) .ethisys-sales-root` is scoped even though the root is not first.
625
+ */
626
+ declare const EHX_PLUGIN_SCOPE_ROOTS: readonly RegExp[];
627
+ interface ScopePluginCssOptions {
628
+ /** Descendant scope prefix. Default {@link EHX_PLUGIN_CSS_SCOPE}. */
629
+ scope?: string;
630
+ /** Replacement for `:root`/`:host`. Default {@link EHX_PLUGIN_ROOT_SCOPE}. */
631
+ rootScope?: string;
632
+ /** Layer names to scope. Default {@link EHX_SCOPED_CSS_LAYERS}. */
633
+ layers?: readonly string[];
634
+ /** What counts as scoped for the guard. Default {@link EHX_PLUGIN_SCOPE_ROOTS}. */
635
+ scopeRoots?: readonly RegExp[];
636
+ /**
637
+ * Guard behaviour when a selector would leak into the host document:
638
+ * `"error"` (default) fails the build, `"warn"` logs, `"off"` skips the check.
639
+ * Only reach for anything but `"error"` as a deliberate, time-boxed exception.
640
+ */
641
+ guard?: "error" | "warn" | "off";
642
+ }
643
+ /**
644
+ * Prefix every selector inside a plugin's UNSCOPED cascade layers with the plugin
645
+ * scope, so injecting the sheet into the host document cannot restyle host chrome.
646
+ *
647
+ * Pure and idempotent — running it twice is a no-op, so a rebuild or a second pass
648
+ * over already-scoped CSS is safe. See {@link EHX_SCOPED_CSS_LAYERS} for which
649
+ * layers are rewritten and why the others need no treatment.
650
+ */
651
+ declare function scopePluginCss(css: string, options?: ScopePluginCssOptions): string;
652
+ /**
653
+ * Every selector in `css` that mentions no plugin scope root, i.e. every selector
654
+ * that can reach host chrome once the sheet is injected into the host document.
655
+ *
656
+ * The invariant this checks is the whole convention: **a plugin never emits an
657
+ * unscoped selector.** {@link scopePluginCss} satisfies it automatically for the
658
+ * layers Tailwind generates; everything else is authored, and this is what catches
659
+ * an author (or a stray `@import "tailwindcss"` re-emitting global preflight) who
660
+ * did not. Pure, so a plugin's own build script or test can call it directly.
661
+ */
662
+ declare function findUnscopedSelectors(css: string, options?: Pick<ScopePluginCssOptions, "scopeRoots">): string[];
663
+ /** The build-failure message: what leaked, why it matters, and the usual cause. */
664
+ declare function formatUnscopedSelectorError(id: string, leaks: readonly string[]): string;
665
+ /** True for ids whose transform payload is plain CSS text we may rewrite. */
666
+ declare function isScopableCssId(id: string): boolean;
667
+ /**
668
+ * Vite plugin form of {@link scopePluginCss} PLUS the guard that makes it a
669
+ * convention rather than a courtesy, injected into every PlatformReact page
670
+ * sub-build by `buildPlatformReactPages`.
671
+ *
672
+ * Scoping Tailwind's generated layers is automatic; the guard fails the build if
673
+ * anything ELSE in the sheet is still unscoped (see
674
+ * {@link findUnscopedSelectors}). Together they are the enforceable form of one
675
+ * rule: a plugin never emits an unscoped selector into the host document. Prose in
676
+ * a standards doc did not hold across seven repos — the leak that prompted this
677
+ * shipped seven times, and the documented no-preflight rule was violated once
678
+ * without anyone noticing.
679
+ *
680
+ * Deliberately declares NO `enforce`. Vite resolves plugins in the order
681
+ * `[…prePlugins, vite:css, …normalPlugins, vite:css-post, …postPlugins]`, so a
682
+ * plugin with no `enforce` is the only position that sees compiled CSS:
683
+ *
684
+ * - `@tailwindcss/vite` is `enforce: "pre"` and returns the generated sheet, so
685
+ * the utilities exist by the time this runs.
686
+ * - `vite:css-post` serialises a `*.css?inline` import into `export default "…"`.
687
+ * `enforce: "post"` runs AFTER that and would be handed JavaScript, where the
688
+ * CSS is an escaped string literal.
689
+ */
690
+ declare function scopePluginCssPlugin(options?: ScopePluginCssOptions): Plugin;
691
+
537
692
  /**
538
693
  * Build-time codegen for a PlatformReact plugin's `feature.manifest.json` +
539
694
  * project-root `extension.manifest.json` overlay, from the plugin's `routeMeta.json`
@@ -807,4 +962,4 @@ interface EthisysPluginOptions {
807
962
  */
808
963
  declare function ethisysManifestPlugin(options?: EthisysPluginOptions): Plugin;
809
964
 
810
- export { type BuildPlatformReactPagesOptions, CONTRACT_B_IMPORT_MAP_ALLOWLIST, CONTRACT_B_RUNTIME_IMPORTS, CONTRACT_B_SEMANTIC_PRIMITIVES, type ComputePlatformReactPagesInput, type ComputePlatformReactPagesResult, type ContractAManifest, type ContractAPluginOptions, type ContractBManifest, type ContractBPluginOptions, type EthisysPluginOptions, type GeneratePlatformReactManifestConfig, type GeneratePlatformReactManifestResult, type IframeBuildContext, type IframeBuildOutputItem, type IframeBuildResult, type IframeSandboxBuildSummary, type IframeSandboxPluginOptions, type IframeViteBuild, type ManifestReactiveRuleRef, type ManifestResourceRef, PLATFORM_REACT_DEDUPE, PLATFORM_REACT_EXTERNALS, type ParsePlatformReactPagesOptions, type PlatformReactAlias, type PlatformReactBuildContext, type PlatformReactBuildSummary, type PlatformReactManifest, type PlatformReactManifestMeta, type PlatformReactManifestPaths, type PlatformReactPageDeclaration, type PlatformReactPageEntry, type PlatformReactPageInput, type PlatformReactPageOverride, type PlatformReactPageSource, type PlatformReactPluginOptions, type PlatformReactViteBuild, type ResolvedPlatformReactPage, type RouteMetaEntry, type ValidationFailure, type ValidationResult, type ViteBuildFn, buildIframeSandboxPages, buildPlatformReactPages, computePlatformReactPages, ethisysContractAPlugin, ethisysContractBPlugin, ethisysIframeSandboxPlugin, ethisysManifestPlugin, ethisysPlatformReactPlugin, generatePlatformReactManifest, parsePlatformReactPages, rewriteAliasedExternalImports, validateDeclarativeResource, validateReactiveRule, virtualPageEntry };
965
+ export { type BuildPlatformReactPagesOptions, CONTRACT_B_IMPORT_MAP_ALLOWLIST, CONTRACT_B_RUNTIME_IMPORTS, CONTRACT_B_SEMANTIC_PRIMITIVES, type ComputePlatformReactPagesInput, type ComputePlatformReactPagesResult, type ContractAManifest, type ContractAPluginOptions, type ContractBManifest, type ContractBPluginOptions, EHX_PLUGIN_CSS_SCOPE, EHX_PLUGIN_ROOT_SCOPE, EHX_PLUGIN_SCOPE_ROOTS, EHX_SCOPED_CSS_LAYERS, type EthisysPluginOptions, type GeneratePlatformReactManifestConfig, type GeneratePlatformReactManifestResult, type IframeBuildContext, type IframeBuildOutputItem, type IframeBuildResult, type IframeSandboxBuildSummary, type IframeSandboxPluginOptions, type IframeViteBuild, type ManifestReactiveRuleRef, type ManifestResourceRef, PLATFORM_REACT_DEDUPE, PLATFORM_REACT_EXTERNALS, type ParsePlatformReactPagesOptions, type PlatformReactAlias, type PlatformReactBuildContext, type PlatformReactBuildSummary, type PlatformReactManifest, type PlatformReactManifestMeta, type PlatformReactManifestPaths, type PlatformReactPageDeclaration, type PlatformReactPageEntry, type PlatformReactPageInput, type PlatformReactPageOverride, type PlatformReactPageSource, type PlatformReactPluginOptions, type PlatformReactViteBuild, type ResolvedPlatformReactPage, type RouteMetaEntry, type ScopePluginCssOptions, type ValidationFailure, type ValidationResult, type ViteBuildFn, buildIframeSandboxPages, buildPlatformReactPages, computePlatformReactPages, ethisysContractAPlugin, ethisysContractBPlugin, ethisysIframeSandboxPlugin, ethisysManifestPlugin, ethisysPlatformReactPagesPlugin, ethisysPlatformReactPlugin, findUnscopedSelectors, formatUnscopedSelectorError, generatePlatformReactManifest, isScopableCssId, parsePlatformReactPages, rewriteAliasedExternalImports, scopePluginCss, scopePluginCssPlugin, validateDeclarativeResource, validateReactiveRule, virtualPageEntry };