mnfst 0.5.199-next.4 → 0.5.199

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.
@@ -2,7 +2,7 @@
2
2
 
3
3
  (function () {
4
4
 
5
- const MANIFEST_BUILD_VERSION = '0.5.199-next.4';
5
+ const MANIFEST_BUILD_VERSION = '0.5.199';
6
6
 
7
7
  /* Manifest Data Sources - Appwrite Integration */
8
8
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  (function () {
4
4
 
5
- const MANIFEST_BUILD_VERSION = '0.5.199-next.4';
5
+ const MANIFEST_BUILD_VERSION = '0.5.199';
6
6
 
7
7
  /* Manifest Data Sources - Configuration */
8
8
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "manifest.appwrite.auth.js": "sha384-HMJmRXnvdDBWyuteXL+DTxWM2llSCdjl4EDamEI7iHNAVlqeOCE4GDTm8ZHipPRj",
3
- "manifest.appwrite.data.js": "sha384-avAqJJn65+6k2tbvGja/S0ZsEOtCEhFG93LgqODbuaP4kLaWFkVrZQwnnPJLxbIR",
3
+ "manifest.appwrite.data.js": "sha384-ETPZTFqMOrmrriXrTAZqJljFmwfs316c4C2GEqNs295HdyYu7oZN8BDTLG7Bmotj",
4
4
  "manifest.appwrite.presences.js": "sha384-JyVDWFT69b7Svq8J8p9tNWe2v9PZNQBY5UyhtOl1vnuHFAX0CRQ3MA9SiKq5wzNh",
5
5
  "manifest.bindings.js": "sha384-lPtMf8Ykaic9gNVZKq8Scia1881ZeMAyxer3WCIkUNwwrgzGBWto1OXADUwjvORF",
6
6
  "manifest.charts.js": "sha384-qQrjhWkIDi4oU5uQrpy8DdlMjY+gfiivBuX6mssNiFdkawgkVBvKneGgp+nd3PYJ",
@@ -11,7 +11,7 @@
11
11
  "manifest.combobox.js": "sha384-SN3XHumoebxcduFQqx5zt65/Cd10y8nuf9Gkco6UnsZ42sadRd+gvVLfWPdkkuPH",
12
12
  "manifest.components.js": "sha384-DLGSqCbnGkS87AWJ8GpKoAFP4nCosgC5SFSg6C10hH/nWu/MxI+0ntHlnFEfs++u",
13
13
  "manifest.computed.js": "sha384-GYML/e81gQfN+iDbqhPUgny8bINCyL+SrbTofoGk4hlCnLLjv51mCv42JMqq+ku5",
14
- "manifest.data.js": "sha384-Cfww6fFCDaSckoFAUvLRf7ResiGMSmmLgNeZQLe3JbqHZufiwbhRMzTyw3FaOL0l",
14
+ "manifest.data.js": "sha384-AApJ6UR3+3DpNbMaMmIBL7BpT/HwIEJaY3yYWLo19ToT20QnhgImWBLLMaCgd4hU",
15
15
  "manifest.datepicker.js": "sha384-8vTJYkLqqvgXgm1w1becuaRR/jUEpiLBjBJ6RsRbUr+ZkypvspD6EsssuDrX4SQq",
16
16
  "manifest.defer.js": "sha384-wXa60M2OHigi5rBurXQ7Oa8eyr5WTKs7EUn2GOru62KMdfs/LDyNegxCc9MNJFSe",
17
17
  "manifest.device.js": "sha384-g+txmrEszYtuOvRelTp/c1wSNR2AtllVI9rx5NPPD7DzhVUnMNughonZykv2xlBW",
@@ -19,7 +19,7 @@
19
19
  "manifest.edit.js": "sha384-oLsrH7/UkJ/KEl3UO77hexcf2rYCGgzXZd0fe2EMq3hKRE7aUqLiEnLJhSV7zTaY",
20
20
  "manifest.export.js": "sha384-HCy3n6ifrcAGIyzTRgZ5JIRvSX9q9/8ykMKRSbt1QTnhXz6PgYlV0AE2N2Yi2re+",
21
21
  "manifest.icons.js": "sha384-kKOINpX70YzNO3FvkV6umrmO3f0KIu0DNbwB+ej55a7y/lhU7v/myQtXL0bwCTbt",
22
- "manifest.localization.js": "sha384-p8MRlfpjQuMar7rerGRjfdRKqIPoU/QOwVmnwN+PjhrlEZNpetYLZhqO2i5BZn9s",
22
+ "manifest.localization.js": "sha384-EImsIw8Z3l13V5eH9ZJF/yfsOfIvR1oPRNSArANkPji7V8O3IRf6zMwsNiAGj3Hk",
23
23
  "manifest.markdown.js": "sha384-HFE2VwBRX5vhDGQ3c8XgZ10Y5ENkXxHzgX9DjmIEffqLoUDbnJYNk3TFWT9k3e+f",
24
24
  "manifest.native.js": "sha384-g+txmrEszYtuOvRelTp/c1wSNR2AtllVI9rx5NPPD7DzhVUnMNughonZykv2xlBW",
25
25
  "manifest.payments.js": "sha384-8pofk2zhxtapPMpwnFyTelPMrYhShNhDCE722uQh/EYFvZFuPLmSdgunqsarVQjv",
@@ -28,14 +28,14 @@
28
28
  "manifest.slides.js": "sha384-3uRTkyK9XPLmnxI2+igZlpi4EyPlU/7IHj5j3BZJJ2KN455vXyk99fiXV3feO/XY",
29
29
  "manifest.status.js": "sha384-nwvUSbT+gpRMqhYx6MeBYswUMH72rY92mjgxv0vrSG7Ht7l83XYpEB6fCTbXjjfK",
30
30
  "manifest.svg.js": "sha384-qr/8H9+2cIhSXNbRWRtIdrDlTOxKlNJqwEefHvCYoiBNW/kKhPTFa109Orv/EBnb",
31
- "manifest.sw.js": "sha384-evmpNHyxUu1Qsu2FP7buzG48O5Ppbf0ZlCn8V4Br4LiU6qrNCkfqh1yXxuafEg6Z",
31
+ "manifest.sw.js": "sha384-ogkuzlKwgmBIH08csCz37WQdUMUeTwrOJcGTVNXw0aPzubapYLMMOYT2XHlvXMAh",
32
32
  "manifest.tabs.js": "sha384-OIIcyaQnGS0eNYk4zgAFEDboNYect7THvUd9D0V6nqCeDOxQcoiqLrBEmmWVTTet",
33
33
  "manifest.tailwind.js": "sha384-r/Jvqb+Piz21jUts10BbVAJoKkShOdJRVFpywyi4HPIi/OfOJq5CtPyWAPbdkMgt",
34
34
  "manifest.text.edit.js": "sha384-pfhc4IPKNJokj6ldlgD+KwGLN4UoM21CNeVf2sVq/34X1Un2PUhEuyTg5itmtsXS",
35
35
  "manifest.toasts.js": "sha384-/OV32LLMJl0Iw8NoNQJ+5lgntLxjIJq/YNj5JAKS/+nUhVx4MMB9h1FOqP7zuj2M",
36
36
  "manifest.tooltips.js": "sha384-uXVVQADl6pY2Pj9i/UaSclZN7dzbKhLkmevv7Ad0+wXR8LgqodALNFrc5jZJjlwm",
37
37
  "manifest.url.parameters.js": "sha384-J2aSVBOPsoqsbafHndrWLHEDpKb4AKJAoQnVmKA4gZh5P/8HHjFWHO/lkusmG84l",
38
- "manifest.utilities.js": "sha384-RvUXxxMLv5q6Psng6jjfK5O1xw05mV6BdLjV92zQW7Zuy3XWctl3QnF5XYNuPyYq",
38
+ "manifest.utilities.js": "sha384-frvYhLP8Arx+smhzQr1nCvxuVfnjRhoG+ce8pI0H0iYqJwO7km1/3QLdBYt3uKol",
39
39
  "manifest.virtual.js": "sha384-YwwmAsZNddajnAkLhBWtrK8edHMJOhD7cJkQi3qmm5o1Thg0wbtDU0dsfrCHKTIQ",
40
- "manifest.js": "sha384-+rWocB3jT+iBErwbS1ZBwt24C/jhUavBduc5EzMSu8ZZooyO52LeGpW7UscLdLYx"
40
+ "manifest.js": "sha384-z6bjoNHfkJwD3bRcjChzuzM3y/3LrWVfc8nsApLtSXcshgJc8L4+Vz7XaV3MN6N8"
41
41
  }
package/lib/manifest.js CHANGED
@@ -388,8 +388,14 @@
388
388
  return false;
389
389
  }
390
390
 
391
+ // One fetch per URL: concurrent callers share the first call's promise
392
+ const inflightScripts = new Map();
393
+
391
394
  function injectScript(url) {
392
- return new Promise((resolve, reject) => {
395
+ const shared = inflightScripts.get(url);
396
+ if (shared) return shared;
397
+
398
+ const promise = new Promise((resolve, reject) => {
393
399
  // Same src already in the DOM (author tag, prerendered HTML, second loader run)
394
400
  const existing = document.querySelector(`script[src="${url}"]`);
395
401
  if (existing) {
@@ -412,6 +418,10 @@
412
418
  script.onerror = () => { script.removeAttribute('data-mnfst-loading'); script.remove(); reject(new Error(`Failed to load ${url}`)); };
413
419
  document.head.appendChild(script);
414
420
  });
421
+
422
+ inflightScripts.set(url, promise);
423
+ promise.then(() => inflightScripts.delete(url), () => inflightScripts.delete(url)); // dedupe only while in flight
424
+ return promise;
415
425
  }
416
426
 
417
427
  // Load a plugin, falling through the CDN chain on error. A fallback insert
@@ -260,15 +260,19 @@ function initializeLocalizationPlugin() {
260
260
 
261
261
  try {
262
262
  let manifest = window.__manifestLoaded || window.ManifestComponentsRegistry?.manifest;
263
- // A partially-seeded global can be truthy yet lack `data` — fetch the
264
- // real manifest so locale detection sees every source.
263
+ // A partially-seeded global can be truthy yet lack `data` — await the
264
+ // shared in-flight fetch (or start one, one request per boot) so locale
265
+ // detection sees every source.
265
266
  if (!manifest || !manifest.data) {
266
- const manifestUrl = (document.querySelector('link[rel="manifest"]')?.getAttribute('href')) || '/manifest.json';
267
- const response = await fetch(manifestUrl);
268
- if (!response.ok) {
269
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
267
+ if (window.__manifestPromise) manifest = await window.__manifestPromise.catch(() => null);
268
+ if (!manifest || !manifest.data) {
269
+ const manifestUrl = (document.querySelector('link[rel="manifest"]')?.getAttribute('href')) || '/manifest.json';
270
+ window.__manifestPromise = fetch(manifestUrl).then(r => {
271
+ if (!r.ok) throw new Error(`HTTP ${r.status}: ${r.statusText}`);
272
+ return r.json();
273
+ }).then(m => { window.ManifestDataConfig?.interpolateManifest?.(m); return m; });
274
+ manifest = await window.__manifestPromise;
270
275
  }
271
- manifest = await response.json();
272
276
  }
273
277
 
274
278
  if (!manifest || typeof manifest !== 'object') {
@@ -16,7 +16,7 @@
16
16
 
17
17
  // Build stamps the published version here; unstamped (dev) falls back to the
18
18
  // registration URL's `v`, then 'dev'.
19
- const BUILD_VERSION = '0.5.199-next.4';
19
+ const BUILD_VERSION = '0.5.199';
20
20
  const CACHE_PREFIX = 'mnfst-sw:';
21
21
  const META_CACHE = CACHE_PREFIX + 'meta';
22
22
  const SWR_CAP = 200;
@@ -471,6 +471,7 @@ class TailwindCompiler {
471
471
  this.compileTimeout = null;
472
472
  this.cache = new Map();
473
473
  this.hasInitialized = true;
474
+ this.staticUtilitiesCoveredClasses = null;
474
475
  // manifest.code.js (and others) may still register ignore rules; mirror full constructor defaults.
475
476
  this.ignoredClassPatterns = [
476
477
  /^hljs/, /^language-/, /^copy$/, /^copied$/, /^lines$/, /^selected$/
@@ -555,6 +556,11 @@ class TailwindCompiler {
555
556
  // Cache for parsed class names (must be before addCriticalBlockingStylesSync)
556
557
  this.classCache = new Map();
557
558
 
559
+ // Read any publish/render-provided static utilities sheet before the
560
+ // first compile — its classes are skipped rather than regenerated.
561
+ this.staticUtilitiesCoveredClasses = null;
562
+ this.detectStaticUtilitiesSheet();
563
+
558
564
  // Add critical styles IMMEDIATELY - don't wait for anything
559
565
  this.addCriticalBlockingStylesSync();
560
566
 
@@ -1194,7 +1200,11 @@ TailwindCompiler.prototype.loadAndApplyCache = function () {
1194
1200
 
1195
1201
  if (cacheToUse && cacheToUse.css) {
1196
1202
  const applyCacheStart = performance.now();
1197
- this.styleElement.textContent = cacheToUse.css;
1203
+ // The cached CSS may predate the static utilities sheet (or come
1204
+ // from a visitor without one) — strip anything it already covers
1205
+ // so we never re-emit those rules.
1206
+ const cachedCss = this.stripCoveredRulesFromCss(cacheToUse.css);
1207
+ this.styleElement.textContent = cachedCss;
1198
1208
  this.ensureUtilityStylesLast();
1199
1209
  this.scheduleEnsureUtilityStylesLast();
1200
1210
  this.lastThemeHash = cacheToUse.themeHash;
@@ -1202,7 +1212,7 @@ TailwindCompiler.prototype.loadAndApplyCache = function () {
1202
1212
  // Also apply cache to critical style element
1203
1213
  // Extract utilities from @layer utilities block and apply directly (no @layer)
1204
1214
  if (this.criticalStyleElement && !this.criticalStyleElement.textContent) {
1205
- let criticalCss = cacheToUse.css;
1215
+ let criticalCss = cachedCss;
1206
1216
  // Remove @layer utilities wrapper if present
1207
1217
  criticalCss = criticalCss.replace(/@layer\s+utilities\s*\{/g, '').replace(/\}\s*$/, '').trim();
1208
1218
  if (criticalCss) {
@@ -1478,8 +1488,9 @@ TailwindCompiler.prototype.scanStaticClasses = async function () {
1478
1488
 
1479
1489
  // Extract classes from HTML content
1480
1490
  TailwindCompiler.prototype.extractClassesFromHTML = function (html, classSet) {
1481
- // Match class attributes: class="..." or class='...'
1482
- const classRegex = /class=["']([^"']+)["']/g;
1491
+ // Plain class attributes only — the leading boundary excludes Alpine's
1492
+ // :class / x-bind:class bindings (JS expressions, not token lists)
1493
+ const classRegex = /(?:^|[\s"'`>/])(?:[a-zA-Z][\w.-]*-)?class=["']([^"']+)["']/g;
1483
1494
  let match;
1484
1495
 
1485
1496
  while ((match = classRegex.exec(html)) !== null) {
@@ -1590,7 +1601,7 @@ TailwindCompiler.prototype.getUsedClasses = function () {
1590
1601
  }
1591
1602
 
1592
1603
  const result = {
1593
- classes: Array.from(allClasses),
1604
+ classes: this.filterStaticallyCoveredClasses(Array.from(allClasses)),
1594
1605
  variableSuffixes: Array.from(usedVariableSuffixes)
1595
1606
  };
1596
1607
 
@@ -3185,9 +3196,10 @@ TailwindCompiler.prototype.compile = async function () {
3185
3196
  this.currentThemeVars.set(name, value);
3186
3197
  }
3187
3198
 
3188
- // Generate utilities for all static classes
3199
+ // Generate utilities for all static classes (minus anything the
3200
+ // static utilities sheet already covers).
3189
3201
  const staticUsedData = {
3190
- classes: Array.from(this.staticClassCache),
3202
+ classes: this.filterStaticallyCoveredClasses(Array.from(this.staticClassCache)),
3191
3203
  variableSuffixes: []
3192
3204
  };
3193
3205
  // Process static classes for variable suffixes
@@ -3345,6 +3357,191 @@ TailwindCompiler.prototype.compile = async function () {
3345
3357
 
3346
3358
 
3347
3359
 
3360
+ // Static utilities sheet detection
3361
+ // Publish/render may ship a precompiled utilities sheet — `<link rel="stylesheet"
3362
+ // data-mnfst-utilities>` or `<style data-mnfst-utilities>`. Read the classes it
3363
+ // already covers once so compile() only generates/patches what's left.
3364
+
3365
+ TailwindCompiler.prototype.findStaticUtilitiesElement = function () {
3366
+ try {
3367
+ return document.querySelector('link[rel="stylesheet"][data-mnfst-utilities], style[data-mnfst-utilities]');
3368
+ } catch (e) {
3369
+ return null;
3370
+ }
3371
+ };
3372
+
3373
+ // Top-level selector text only (skips declaration bodies, so values like
3374
+ // `margin: .5rem` can't be mistaken for a `.5` class selector).
3375
+ TailwindCompiler.prototype.extractSelectorsFromCssText = function (cssText) {
3376
+ const selectors = [];
3377
+ const len = cssText.length;
3378
+ let i = 0;
3379
+ while (i < len) {
3380
+ if (cssText[i] === '/' && cssText[i + 1] === '*') {
3381
+ const end = cssText.indexOf('*/', i + 2);
3382
+ i = end === -1 ? len : end + 2;
3383
+ continue;
3384
+ }
3385
+ if (cssText[i] === '@') {
3386
+ let j = i;
3387
+ while (j < len && cssText[j] !== '{' && cssText[j] !== ';') j++;
3388
+ if (j >= len || cssText[j] === ';') { i = j + 1; continue; }
3389
+ i = j + 1;
3390
+ let depth = 1;
3391
+ const start = i;
3392
+ while (i < len && depth > 0) {
3393
+ if (cssText[i] === '{') depth++;
3394
+ else if (cssText[i] === '}') depth--;
3395
+ i++;
3396
+ }
3397
+ selectors.push(...this.extractSelectorsFromCssText(cssText.slice(start, i - 1)));
3398
+ continue;
3399
+ }
3400
+ const selStart = i;
3401
+ while (i < len && cssText[i] !== '{' && cssText[i] !== '}') i++;
3402
+ if (i >= len || cssText[i] === '}') { i++; continue; }
3403
+ const selector = cssText.slice(selStart, i).trim();
3404
+ i++;
3405
+ let depth = 1;
3406
+ while (i < len && depth > 0) {
3407
+ if (cssText[i] === '{') depth++;
3408
+ else if (cssText[i] === '}') depth--;
3409
+ i++;
3410
+ }
3411
+ if (selector) selectors.push(selector);
3412
+ }
3413
+ return selectors;
3414
+ };
3415
+
3416
+ // Selectors escape every non-alphanumeric/hyphen char (see escapeClassName),
3417
+ // so `.hover\:bg-brand` unescapes back to the `hover:bg-brand` token form
3418
+ // used everywhere else (class attributes, usedClasses, parseClassName).
3419
+ TailwindCompiler.prototype.classNamesFromCssText = function (cssText) {
3420
+ const classSet = new Set();
3421
+ const classRe = /\.((?:\\.|[a-zA-Z0-9_-])+)/g;
3422
+ for (const selector of this.extractSelectorsFromCssText(cssText)) {
3423
+ let m;
3424
+ classRe.lastIndex = 0;
3425
+ while ((m = classRe.exec(selector)) !== null) {
3426
+ classSet.add(m[1].replace(/\\(.)/g, '$1'));
3427
+ }
3428
+ }
3429
+ return classSet;
3430
+ };
3431
+
3432
+ // Read the static sheet's covered classes once. Same-origin `<link>`/`<style>`
3433
+ // rules are read synchronously via CSSOM; if the sheet isn't accessible yet
3434
+ // (cross-origin, or not finished loading) this falls back to fetching the
3435
+ // `href` as text and re-populating the covered set when it resolves.
3436
+ TailwindCompiler.prototype.detectStaticUtilitiesSheet = function () {
3437
+ this.staticUtilitiesCoveredClasses = null;
3438
+ const el = this.findStaticUtilitiesElement();
3439
+ if (!el) return;
3440
+
3441
+ if (el.tagName === 'STYLE') {
3442
+ this.staticUtilitiesCoveredClasses = this.classNamesFromCssText(el.textContent || '');
3443
+ return;
3444
+ }
3445
+
3446
+ try {
3447
+ const sheet = Array.from(document.styleSheets).find(s => s.ownerNode === el);
3448
+ const rules = sheet && sheet.cssRules;
3449
+ if (rules && rules.length > 0) {
3450
+ const cssText = Array.from(rules).map(r => r.cssText).join('\n');
3451
+ this.staticUtilitiesCoveredClasses = this.classNamesFromCssText(cssText);
3452
+ return;
3453
+ }
3454
+ } catch (e) {
3455
+ // Cross-origin (or not yet parsed) — fall through to fetch.
3456
+ }
3457
+
3458
+ // Placeholder so callers treat "not yet known" as "nothing covered" rather
3459
+ // than null, until the fetch below resolves and replaces it.
3460
+ this.staticUtilitiesCoveredClasses = new Set();
3461
+ const href = el.getAttribute('href');
3462
+ if (!href || typeof fetch !== 'function') return;
3463
+ fetch(href).then(r => (r.ok ? r.text() : '')).then(text => {
3464
+ if (text) this.staticUtilitiesCoveredClasses = this.classNamesFromCssText(text);
3465
+ }).catch(() => {});
3466
+ };
3467
+
3468
+ // Drop classes already covered by the static sheet before generating rules
3469
+ // for them again — the JIT should only ever patch what's left uncovered.
3470
+ TailwindCompiler.prototype.filterStaticallyCoveredClasses = function (classes) {
3471
+ const covered = this.staticUtilitiesCoveredClasses;
3472
+ if (!covered || covered.size === 0) return classes;
3473
+ return classes.filter(c => !covered.has(c));
3474
+ };
3475
+
3476
+ // The localStorage cache (manifest.utilities.cache.js) stores a full compiled
3477
+ // stylesheet from a previous visit — one that may predate the static sheet, or
3478
+ // come from a visitor without one. Re-applying it verbatim would re-emit every
3479
+ // rule the static sheet already covers, so strip those rules out first.
3480
+ TailwindCompiler.prototype.stripCoveredRulesFromCss = function (cssText) {
3481
+ const covered = this.staticUtilitiesCoveredClasses;
3482
+ if (!covered || covered.size === 0 || !cssText) return cssText;
3483
+
3484
+ const strip = (text) => {
3485
+ const out = [];
3486
+ const len = text.length;
3487
+ let i = 0;
3488
+ while (i < len) {
3489
+ if (text[i] === '/' && text[i + 1] === '*') {
3490
+ const end = text.indexOf('*/', i + 2);
3491
+ const stop = end === -1 ? len : end + 2;
3492
+ out.push(text.slice(i, stop));
3493
+ i = stop;
3494
+ continue;
3495
+ }
3496
+ if (text[i] === '@') {
3497
+ let j = i;
3498
+ while (j < len && text[j] !== '{' && text[j] !== ';') j++;
3499
+ if (j >= len || text[j] === ';') { out.push(text.slice(i, j + 1)); i = j + 1; continue; }
3500
+ const atHead = text.slice(i, j + 1);
3501
+ i = j + 1;
3502
+ let depth = 1;
3503
+ const start = i;
3504
+ while (i < len && depth > 0) {
3505
+ if (text[i] === '{') depth++;
3506
+ else if (text[i] === '}') depth--;
3507
+ i++;
3508
+ }
3509
+ out.push(`${atHead}${strip(text.slice(start, i - 1))}}`);
3510
+ continue;
3511
+ }
3512
+ const selStart = i;
3513
+ while (i < len && text[i] !== '{' && text[i] !== '}') i++;
3514
+ if (i >= len || text[i] === '}') { i++; continue; }
3515
+ const selector = text.slice(selStart, i).trim();
3516
+ const bodyStart = i;
3517
+ i++;
3518
+ let depth = 1;
3519
+ while (i < len && depth > 0) {
3520
+ if (text[i] === '{') depth++;
3521
+ else if (text[i] === '}') depth--;
3522
+ i++;
3523
+ }
3524
+ const fullRule = selector + text.slice(bodyStart, i);
3525
+ // Match tokens directly against the bare selector text — it has no
3526
+ // rule body for classNamesFromCssText's selector-then-`{` scan to key off.
3527
+ const classes = [];
3528
+ const classRe = /\.((?:\\.|[a-zA-Z0-9_-])+)/g;
3529
+ let cm;
3530
+ while ((cm = classRe.exec(selector)) !== null) {
3531
+ classes.push(cm[1].replace(/\\(.)/g, '$1'));
3532
+ }
3533
+ // Only drop a rule once every class it references is covered — a
3534
+ // mixed selector (`:where(.row, .col)`) stays if either is new.
3535
+ const isFullyCovered = classes.length > 0 && classes.every(c => covered.has(c));
3536
+ if (!isFullyCovered) out.push(fullRule);
3537
+ }
3538
+ return out.join('\n');
3539
+ };
3540
+
3541
+ return strip(cssText);
3542
+ };
3543
+
3544
+
3348
3545
  // DOM observation: watch for changes and trigger recompilation
3349
3546
 
3350
3547
  // Setup component load listener and MutationObserver