@pie-players/pie-players-shared 0.3.66 → 0.3.67

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.
@@ -17,7 +17,7 @@
17
17
  *
18
18
  * The CSS text is passed in rather than imported here: this package builds with
19
19
  * plain `tsc`, so it cannot inline a stylesheet. Bundler-built player packages
20
- * import it with Vite's `?inline` and hand the text over.
20
+ * import it with Vite's `?raw` and hand the text over.
21
21
  */
22
22
  export type ContentStylesResult = "installed" | "already-installed" | "opted-out" | "no-document";
23
23
  /**
@@ -26,8 +26,16 @@ export type ContentStylesResult = "installed" | "already-installed" | "opted-out
26
26
  */
27
27
  export declare function contentStylesOptedOut(): boolean;
28
28
  /**
29
- * True when `components.css` is applied to the document, by any route. Reads the
30
- * sentinel custom property the stylesheet declares on `:root`.
29
+ * True when `components.css` is applied to the document, by any route.
30
+ *
31
+ * Two probes, because neither alone covers both deliveries. The computed
32
+ * sentinel on `<html>` is authoritative for a stylesheet applying to the whole
33
+ * document. A host that confines its copy to its player subtree — `@scope
34
+ * (.item-content) { … }`, the documented remedy for these rules reaching host
35
+ * chrome — puts the stylesheet's `:root` rule somewhere it can never match,
36
+ * since `<html>` is not a descendant of the scoping root; the property then
37
+ * reads empty while the stylesheet is present and working. So an empty read
38
+ * falls back to scanning the document's sheets for the sentinel.
31
39
  *
32
40
  * Only meaningful once the document's stylesheets have been applied — a host
33
41
  * that loads CSS via an async `<link>` reads as missing until it lands.
@@ -17,14 +17,15 @@
17
17
  *
18
18
  * The CSS text is passed in rather than imported here: this package builds with
19
19
  * plain `tsc`, so it cannot inline a stylesheet. Bundler-built player packages
20
- * import it with Vite's `?inline` and hand the text over.
20
+ * import it with Vite's `?raw` and hand the text over.
21
21
  */
22
22
  /** Marks a `<style>` element this module owns, and keeps installs idempotent. */
23
23
  const MARKER_ATTRIBUTE = "data-pie-content-styles";
24
24
  /**
25
25
  * Declared by `components.css` itself, so it is observable no matter how the
26
- * stylesheet arrived — our injection or a host import. Used only to tell a host
27
- * that opted out but then shipped nothing.
26
+ * stylesheet arrived — our injection or a host import. Diagnostics only: it
27
+ * tells a host that opted out and then shipped nothing, and it spots a host copy
28
+ * sitting alongside ours.
28
29
  */
29
30
  const SENTINEL_PROPERTY = "--pie-content-styles";
30
31
  // Svelte's dev-mode custom-element reset expands `all: unset` into individual
@@ -43,7 +44,22 @@ const declaresContentStylesSentinel = (rule) => {
43
44
  ?.getPropertyValue(SENTINEL_PROPERTY)
44
45
  .trim()
45
46
  .toLowerCase();
46
- return Boolean(value && !CSS_WIDE_RESET_VALUES.has(value));
47
+ if (value)
48
+ return !CSS_WIDE_RESET_VALUES.has(value);
49
+ // Grouping rules hold no declarations of their own, so the sentinel sits one
50
+ // or more levels down. A host that confines its copy — `@scope
51
+ // (.item-content) { … }`, `@layer pie-content { … }` — presents exactly one
52
+ // top-level rule with an empty `.style`, and a top-level-only scan reads that
53
+ // as "no copy here". That made both detection paths blind to the one host
54
+ // configuration this module most needs to recognise.
55
+ const nested = rule.cssRules;
56
+ if (!nested)
57
+ return false;
58
+ for (const child of Array.from(nested)) {
59
+ if (declaresContentStylesSentinel(child))
60
+ return true;
61
+ }
62
+ return false;
47
63
  };
48
64
  /** `<html data-pie-content-styles="host">` opts a host out of installation. */
49
65
  const OPT_OUT_ATTRIBUTE = "data-pie-content-styles";
@@ -59,8 +75,16 @@ export function contentStylesOptedOut() {
59
75
  return (document.documentElement.getAttribute(OPT_OUT_ATTRIBUTE) === OPT_OUT_VALUE);
60
76
  }
61
77
  /**
62
- * True when `components.css` is applied to the document, by any route. Reads the
63
- * sentinel custom property the stylesheet declares on `:root`.
78
+ * True when `components.css` is applied to the document, by any route.
79
+ *
80
+ * Two probes, because neither alone covers both deliveries. The computed
81
+ * sentinel on `<html>` is authoritative for a stylesheet applying to the whole
82
+ * document. A host that confines its copy to its player subtree — `@scope
83
+ * (.item-content) { … }`, the documented remedy for these rules reaching host
84
+ * chrome — puts the stylesheet's `:root` rule somewhere it can never match,
85
+ * since `<html>` is not a descendant of the scoping root; the property then
86
+ * reads empty while the stylesheet is present and working. So an empty read
87
+ * falls back to scanning the document's sheets for the sentinel.
64
88
  *
65
89
  * Only meaningful once the document's stylesheets have been applied — a host
66
90
  * that loads CSS via an async `<link>` reads as missing until it lands.
@@ -71,7 +95,9 @@ export function contentStylesPresent() {
71
95
  const value = getComputedStyle(document.documentElement)
72
96
  .getPropertyValue(SENTINEL_PROPERTY)
73
97
  .trim();
74
- return value !== "";
98
+ if (value !== "")
99
+ return true;
100
+ return countContentStyleSheets({ excludeInstalled: false }) > 0;
75
101
  }
76
102
  /**
77
103
  * Installs `cssText` as a document-level stylesheet, once per document.
@@ -109,21 +135,22 @@ export function installContentStyles(cssText, source) {
109
135
  return "installed";
110
136
  }
111
137
  /**
112
- * Counts content stylesheets in the document that this module did not install —
113
- * i.e. copies the host loaded itself. Detected by the sentinel property rather
114
- * than by URL, so a copy arriving as a `<link>`, a bundler-injected `<style>`, or
115
- * anything else all count the same.
138
+ * Counts content stylesheets in the document, detected by the sentinel property
139
+ * rather than by URL, so a copy arriving as a `<link>`, a bundler-injected
140
+ * `<style>`, or anything else all count the same. `excludeInstalled` narrows the
141
+ * count to copies the host loaded itself.
116
142
  *
117
143
  * Cross-origin sheets throw on `cssRules` access and are skipped; a host copy
118
144
  * served from another origin therefore reads as absent. That only costs a
119
145
  * diagnostic, never correctness.
120
146
  */
121
- const countHostContentStyleSheets = () => {
147
+ const countContentStyleSheets = ({ excludeInstalled, }) => {
122
148
  // Walks the owning elements rather than document.styleSheets: the marker
123
149
  // attribute lives on the element, and CSSStyleSheet.ownerNode is not
124
150
  // universally implemented (happy-dom omits it), which would make our own
125
151
  // installed copy look like a host copy.
126
- const nodes = document.querySelectorAll(`style:not([${MARKER_ATTRIBUTE}]), link[rel~="stylesheet"]:not([${MARKER_ATTRIBUTE}])`);
152
+ const exclusion = excludeInstalled ? `:not([${MARKER_ATTRIBUTE}])` : "";
153
+ const nodes = document.querySelectorAll(`style${exclusion}, link[rel~="stylesheet"]${exclusion}`);
127
154
  let count = 0;
128
155
  for (const node of Array.from(nodes)) {
129
156
  let rules;
@@ -144,6 +171,8 @@ const countHostContentStyleSheets = () => {
144
171
  }
145
172
  return count;
146
173
  };
174
+ /** Copies the host loaded itself, i.e. not the one this module installed. */
175
+ const countHostContentStyleSheets = () => countContentStyleSheets({ excludeInstalled: true });
147
176
  let auditWarningIssued = false;
148
177
  const pendingChecks = [];
149
178
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pie-players/pie-players-shared",
3
- "version": "0.3.66",
3
+ "version": "0.3.67",
4
4
  "type": "module",
5
5
  "description": "Shared runtime + UI utilities for PIE players",
6
6
  "license": "MIT",