/*! © 2026 Yahoo, Inc. UDS v0.0.0-development */
import { buildScopedColorModeSelectorList, getScopedSelfOrParentAlternativeSelector, injectIsolationExclusion, isNonRestStateSelector } from "./postcss.helpers.js";
//#region ../css/dist/css/postcss.mjs
/*! © 2026 Yahoo, Inc. UDS CSS v0.0.0-development */
const fixScopedSelfOrParentSelectorsPlugin = (scopeClass) => {
	return {
		postcssPlugin: "fix-scoped-self-or-parent-selectors",
		Once(root) {
			root.walkRules((rule) => {
				if (!rule.selector.includes(scopeClass)) return;
				const nextSelectors = rule.selectors.reduce((acc, selector) => {
					const sameElementSelector = getScopedSelfOrParentAlternativeSelector(selector, scopeClass);
					if (sameElementSelector && !rule.selectors.includes(sameElementSelector)) return [
						...acc,
						selector,
						sameElementSelector
					];
					return [...acc, selector];
				}, []);
				if (nextSelectors.length !== rule.selectors.length) rule.selectors = nextSelectors;
			});
		}
	};
};
/**
* PostCSS plugin to fix color mode selectors for scoped CSS.
*
* postcss-scope converts selectors like:
*   `.uds-color-mode-dark .foo` → `.scope .uds-color-mode-dark .foo`
*
* But the color mode class is typically on <html> or <body>, which is an
* ANCESTOR of the scope, not a descendant. This plugin adds alternative
* selectors so color modes work correctly:
*
*   `.scope .uds-color-mode-dark .foo` → `.scope .uds-color-mode-dark .foo, .uds-color-mode-dark .scope .foo, .uds-color-mode-dark.scope .foo`
*
* This ensures styles apply whether the color mode class is inside or outside the scope.
*/
const fixScopedColorModeSelectorsPlugin = (scopeClass) => {
	const colorModePattern = /\.(uds-color-mode-(?:dark|light))/;
	return {
		postcssPlugin: "fix-scoped-color-mode-selectors",
		Once(root) {
			root.walkRules((rule) => {
				if (!rule.selector.includes(scopeClass) || !colorModePattern.test(rule.selector)) return;
				const nextSelector = buildScopedColorModeSelectorList(rule.selector, scopeClass);
				if (nextSelector !== rule.selector) rule.selector = nextSelector;
			});
		}
	};
};
/**
* True when any declaration in the rule references a UDS token variable
* (`var(--uds-…)`) — i.e. the rule is theme-driven. See
* {@link appendIsolationExclusionPlugin} for why only those rules are isolated.
*/
const ruleReferencesUdsToken = (rule) => {
	let found = false;
	rule.walkDecls((decl) => {
		if (decl.value.includes("var(--uds-")) {
			found = true;
			return false;
		}
	});
	return found;
};
/**
* PostCSS plugin to enforce per-bundle style isolation by appending an
* exclusion suffix to *theme-driven* rules' selectors.
*
* Each qualifying rule's deepest simple selector gets augmented with
*   `:not(:where(:is(.A, .B, …) *))`
*
* where `.A`, `.B`, … are the scope classes of OTHER scoped bundles loaded
* alongside this one (i.e. every `scoped:` entry in `uds.theme.ts` except
* this bundle's own scope) plus any non-bundled selectors declared via
* `css.excludeBoundaries`. This prevents this bundle's rules from matching
* elements that live inside another scoped library's subtree, so each
* scoped package gets a clean cascade inside its own scope-class wrapper
* without bleed-through from neighboring bundles.
*
* **Why "theme-driven" only:** rules that reference a UDS token variable
* (`var(--uds-…)` in any declaration value) carry committed-theme values
* that would wrongly leak into a preview cell when the draft theme leaves
* a property undefined (cascade order can't undo a value that isn't being
* overridden). Rules that do NOT reference a token — Tailwind utilities,
* structural resets, `display: flex` on a layout class, padding helpers,
* etc. — are theme-independent: blocking them inside a preview boundary
* is collateral damage that strips needed structural/utility behavior from
* the rendered content. The plugin therefore leaves non-token-referencing
* rules untouched (see {@link ruleReferencesUdsToken}).
*
* **Why non-rest only:** among token-referencing rules, only non-rest
* STATE rules (`:hover`, `:focus-within`, `:has(…)`, etc.) need the fence.
* A rest theme rule that leaks into a nested foreign scope resolves its
* `var(--uds-*)` to that scope's own tokens (postcss-scope rewrites
* `:root`→scope), so it is value-safe and fencing it would only add bytes
* to the wire. The harmful leak is a non-rest state rule whose higher
* specificity wins over the nested scope's own state rule, overriding a
* value the nested scope explicitly set. Only those selectors are augmented
* (see {@link isNonRestStateSelector}).
*
* Bundles whose neighbor list is empty (no other scoped libraries and no
* `excludeBoundaries` entries are configured) get the selector unchanged —
* the plugin is a no-op there and the bundle is byte-identical to its
* pre-isolation output.
*
* `:where()` contributes (0, 0, 0) specificity, so the augmented selector
* preserves the original's specificity exactly.
*
* NOTE: A single CSS `@scope (.<scope>) to (.A, .B, …)` declaration at the
* top of the bundle would be the cleaner expression of this isolation — one
* declaration instead of N selector tweaks — but `@scope` lacked sufficient
* browser support when this was implemented (May 2026 — requires Chrome
* 118+, Safari 17.4+, Firefox 128+). When the support floor moves up, this
* plugin can be replaced with an `@scope` wrapper around the bundle,
* dropping the per-rule suffix.
*/
const appendIsolationExclusionPlugin = (otherBoundaryScopes) => {
	return {
		postcssPlugin: "append-isolation-exclusion",
		Once(root) {
			if (otherBoundaryScopes.length === 0) return;
			root.walkRules((rule) => {
				if (!ruleReferencesUdsToken(rule)) return;
				const nextSelectors = rule.selectors.map((selector) => isNonRestStateSelector(selector) ? injectIsolationExclusion(selector, otherBoundaryScopes) : selector);
				if (nextSelectors.some((selector, idx) => selector !== rule.selectors[idx])) rule.selectors = nextSelectors;
			});
		}
	};
};
//#endregion
export { appendIsolationExclusionPlugin, fixScopedColorModeSelectorsPlugin, fixScopedSelfOrParentSelectorsPlugin };
