mnfst 0.5.91 → 0.5.93

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.
@@ -21,6 +21,6 @@
21
21
  "manifest.toasts.js": "sha384-ytd5rDbax/Ou9z23uedFXPZbxDPsk2E/pxCTq4WLvfv+os1qTI6kELp0kPp07g24",
22
22
  "manifest.tooltips.js": "sha384-Hhip5ZN66xhDw3m0XBrKLKLpcVRz3Z9RszPKqo6xvFF0mrUgQBVZ+mZjZsXgOOjS",
23
23
  "manifest.url.parameters.js": "sha384-FIufiClqDx1rJpU/QUc9z/D43qClQ6Qm8rBahipbJl9BDHUvhrOsUDegmTWW7Tuf",
24
- "manifest.utilities.js": "sha384-JzIkeYogrK/qabMCyBx3RQc9j+QYGGsgVZVQGrFNqoLMhEVUSogEqap2JqHlruyk",
24
+ "manifest.utilities.js": "sha384-x07Pfi4UxK9tbdcOZgZ5jkveseT1xBUTFtO08ORcokCH64FHsUceoiAICAsRJMle",
25
25
  "manifest.js": "sha384-CUFN8gbtQW6+fnxiBnx0nPw6kKDA21+He0KFoDE9FabfJPR4lKRXaT2PWpOOKUS+"
26
26
  }
@@ -1588,41 +1588,9 @@ TailwindCompiler.prototype.getUsedClasses = function () {
1588
1588
  }
1589
1589
  };
1590
1590
 
1591
- // True for Manifest framework stylesheets (the compiled bundle and its plugin
1592
- // partials, on the CDN or self-hosted), false for the developer-overridable
1593
- // theme entry-point, inline <style> blocks, and any non-framework CSS.
1594
- //
1595
- // Used to split fetchThemeContent's output into two buckets: CSS *variables*
1596
- // are read from every sheet (developers can declare custom properties
1597
- // anywhere), but custom utility *class* declarations are only harvested from
1598
- // non-framework sheets — framework semantic classes like .brand / .row / .col
1599
- // already live in the static cascade and re-emitting them at runtime causes
1600
- // duplicate #manifest-styles entries.
1601
- TailwindCompiler.prototype.isFrameworkStylesheet = function (source) {
1602
- if (typeof source !== 'string' || source.startsWith('inline:')) return false;
1603
- const lower = source.toLowerCase();
1604
- // manifest.theme.css ships with the framework but is developer-overridable —
1605
- // always scan it for class declarations.
1606
- if (/\/manifest\.theme\.css(?:[?#]|$)/.test(lower)) return false;
1607
- // Framework CDN bundles (jsdelivr/unpkg/etc. publishing manifestjs or mnfst).
1608
- if (lower.includes('manifestjs') || lower.includes('mnfst')) return true;
1609
- // Self-hosted framework files: manifest.css, manifest.min.css,
1610
- // manifest.<plugin>.css. Heuristic on basename — a developer who names their
1611
- // own bundle "manifest.foo.css" will get caught by this, which is rare and
1612
- // easy to work around by renaming.
1613
- if (/\/manifest(?:\.[\w-]+)*\.css(?:[?#]|$)/.test(lower)) return true;
1614
- return false;
1615
- };
1616
-
1617
- // Fetch theme content from CSS files.
1618
- //
1619
- // Returns { all, userScannable }:
1620
- // - all: every loaded stylesheet, used for CSS-variable extraction.
1621
- // - userScannable: same minus framework stylesheets (see isFrameworkStylesheet).
1622
- // Used for custom-utility class extraction.
1591
+ // Fetch theme content from CSS files
1623
1592
  TailwindCompiler.prototype.fetchThemeContent = async function () {
1624
- const allContents = new Set();
1625
- const userContents = new Set();
1593
+ const themeContents = new Set();
1626
1594
  const fetchPromises = [];
1627
1595
 
1628
1596
  // If we haven't discovered CSS files yet, do it now
@@ -1632,7 +1600,6 @@ TailwindCompiler.prototype.fetchThemeContent = async function () {
1632
1600
 
1633
1601
  // Process all files concurrently
1634
1602
  for (const source of this.cssFiles) {
1635
- const isFramework = this.isFrameworkStylesheet(source);
1636
1603
  const fetchPromise = (async () => {
1637
1604
  try {
1638
1605
  let content = '';
@@ -1691,10 +1658,7 @@ TailwindCompiler.prototype.fetchThemeContent = async function () {
1691
1658
  }
1692
1659
 
1693
1660
  if (content) {
1694
- allContents.add(content);
1695
- if (!isFramework) {
1696
- userContents.add(content);
1697
- }
1661
+ themeContents.add(content);
1698
1662
  }
1699
1663
  } catch (error) {
1700
1664
  console.warn(`Error fetching CSS from ${source}:`, error);
@@ -1706,10 +1670,7 @@ TailwindCompiler.prototype.fetchThemeContent = async function () {
1706
1670
  // Wait for all fetches to complete
1707
1671
  await Promise.all(fetchPromises);
1708
1672
 
1709
- return {
1710
- all: Array.from(allContents).join('\n'),
1711
- userScannable: Array.from(userContents).join('\n')
1712
- };
1673
+ return Array.from(themeContents).join('\n');
1713
1674
  };
1714
1675
 
1715
1676
  // Extract CSS variables from CSS text
@@ -2184,6 +2145,32 @@ TailwindCompiler.prototype.extractCustomUtilities = function (cssText) {
2184
2145
  // Tolerate parsing errors; this is best-effort
2185
2146
  }
2186
2147
 
2148
+ // Dedupe captured entries per class. The four parser passes above (flat
2149
+ // regex, :where() extractor, compound-selector fallback, universal nested
2150
+ // resolver) can each capture the same source rule with slightly different
2151
+ // whitespace or selector ordering. Without this, the generator emits
2152
+ // duplicate variant blocks at runtime (e.g. four `.\!brand { … }` rules
2153
+ // where one suffices). Normalize whitespace before comparing so trivial
2154
+ // formatting differences collapse.
2155
+ for (const [className, value] of utilities.entries()) {
2156
+ if (!Array.isArray(value)) continue;
2157
+ const seen = new Set();
2158
+ const deduped = [];
2159
+ for (const entry of value) {
2160
+ if (!entry || typeof entry !== 'object') continue;
2161
+ const sel = String(entry.selector || '').replace(/\s+/g, ' ').trim();
2162
+ const css = String(entry.css || '').replace(/\s+/g, ' ').trim();
2163
+ if (!css) continue;
2164
+ const key = `${sel}${css}`;
2165
+ if (seen.has(key)) continue;
2166
+ seen.add(key);
2167
+ deduped.push(entry);
2168
+ }
2169
+ if (deduped.length > 0) {
2170
+ utilities.set(className, deduped);
2171
+ }
2172
+ }
2173
+
2187
2174
  return utilities;
2188
2175
  };
2189
2176
 
@@ -3120,18 +3107,20 @@ TailwindCompiler.prototype.compile = async function () {
3120
3107
 
3121
3108
  // Fetch CSS content once for initial compilation
3122
3109
  const themeCss = await this.fetchThemeContent();
3123
- if (themeCss && themeCss.all) {
3124
- // Extract custom utility classes from non-framework CSS only.
3125
- // Framework semantic classes (.brand, .row, .col, etc.) already
3126
- // live in the compiled manifest.css; re-emitting them here would
3127
- // duplicate rules in #manifest-styles.
3128
- const discoveredCustomUtilities = this.extractCustomUtilities(themeCss.userScannable);
3110
+ if (themeCss) {
3111
+ // Extract and cache custom utilities. We scan framework CSS too
3112
+ // because the generator needs to know about semantic classes
3113
+ // like .brand / .row / .col to emit their responsive/state
3114
+ // variants (e.g. md:row, hover:brand). Base-form re-emission is
3115
+ // suppressed in generateCustomUtilities ("Skip generating base
3116
+ // utility - it already exists in the CSS"); duplicate captures
3117
+ // are collapsed at the end of extractCustomUtilities.
3118
+ const discoveredCustomUtilities = this.extractCustomUtilities(themeCss);
3129
3119
  for (const [name, value] of discoveredCustomUtilities.entries()) {
3130
3120
  this.customUtilities.set(name, value);
3131
3121
  }
3132
3122
 
3133
- // CSS variables can live in any stylesheet — scan everything.
3134
- const variables = this.extractThemeVariables(themeCss.all);
3123
+ const variables = this.extractThemeVariables(themeCss);
3135
3124
  for (const [name, value] of variables.entries()) {
3136
3125
  this.currentThemeVars.set(name, value);
3137
3126
  }
@@ -3152,7 +3141,7 @@ TailwindCompiler.prototype.compile = async function () {
3152
3141
  }
3153
3142
 
3154
3143
  // Generate both variable-based and custom utilities
3155
- const varUtilities = this.generateUtilitiesFromVars(themeCss.all, staticUsedData);
3144
+ const varUtilities = this.generateUtilitiesFromVars(themeCss, staticUsedData);
3156
3145
  const customUtilitiesGenerated = this.generateCustomUtilities(staticUsedData);
3157
3146
 
3158
3147
  let allUtilities = [varUtilities, customUtilitiesGenerated].filter(Boolean).join('\n\n');
@@ -3181,7 +3170,7 @@ TailwindCompiler.prototype.compile = async function () {
3181
3170
  this.lastClassesHash = staticUsedData.classes.sort().join(',');
3182
3171
 
3183
3172
  // Save to cache for next page load
3184
- const themeHash = this.generateThemeHash(themeCss.all);
3173
+ const themeHash = this.generateThemeHash(themeCss);
3185
3174
  const cacheKey = `${this.lastClassesHash}-${themeHash}`;
3186
3175
  this.cache.set(cacheKey, {
3187
3176
  css: finalCss,
@@ -3208,20 +3197,19 @@ TailwindCompiler.prototype.compile = async function () {
3208
3197
  if (dynamicClassesHash !== this.lastClassesHash || !this.hasInitialized) {
3209
3198
  // Fetch CSS content for dynamic compilation
3210
3199
  const themeCss = await this.fetchThemeContent();
3211
- if (!themeCss || !themeCss.all) {
3200
+ if (!themeCss) {
3212
3201
  this.isCompiling = false;
3213
3202
  return;
3214
3203
  }
3215
3204
 
3216
- // Update custom utilities cache from non-framework CSS only — see
3217
- // first compile path for rationale.
3218
- const discoveredCustomUtilities = this.extractCustomUtilities(themeCss.userScannable);
3205
+ // Update custom utilities cache if needed
3206
+ const discoveredCustomUtilities = this.extractCustomUtilities(themeCss);
3219
3207
  for (const [name, value] of discoveredCustomUtilities.entries()) {
3220
3208
  this.customUtilities.set(name, value);
3221
3209
  }
3222
3210
 
3223
3211
  // Check for variable changes
3224
- const variables = this.extractThemeVariables(themeCss.all);
3212
+ const variables = this.extractThemeVariables(themeCss);
3225
3213
  let hasVariableChanges = false;
3226
3214
  for (const [name, value] of variables.entries()) {
3227
3215
  const currentValue = this.currentThemeVars.get(name);
@@ -3235,7 +3223,7 @@ TailwindCompiler.prototype.compile = async function () {
3235
3223
  if (hasVariableChanges || dynamicClassesHash !== this.lastClassesHash) {
3236
3224
 
3237
3225
  // Generate both variable-based and custom utilities
3238
- const varUtilities = this.generateUtilitiesFromVars(themeCss.all, usedData);
3226
+ const varUtilities = this.generateUtilitiesFromVars(themeCss, usedData);
3239
3227
  const customUtilitiesGenerated = this.generateCustomUtilities(usedData);
3240
3228
 
3241
3229
  let allUtilities = [varUtilities, customUtilitiesGenerated].filter(Boolean).join('\n\n');
@@ -3264,7 +3252,7 @@ TailwindCompiler.prototype.compile = async function () {
3264
3252
  this.lastClassesHash = dynamicClassesHash;
3265
3253
 
3266
3254
  // Save to cache for next page load
3267
- const themeHash = this.generateThemeHash(themeCss.all);
3255
+ const themeHash = this.generateThemeHash(themeCss);
3268
3256
  const cacheKey = `${this.lastClassesHash}-${themeHash}`;
3269
3257
  this.cache.set(cacheKey, {
3270
3258
  css: finalCss,
@@ -3438,54 +3426,19 @@ TailwindCompiler.prototype.setupComponentLoadListener = function () {
3438
3426
  });
3439
3427
  };
3440
3428
 
3441
- // Start processing with initial compilation and observer setup
3429
+ // Start processing with initial compilation. DOM observation is owned by
3430
+ // setupComponentLoadListener (called separately from main.js init) — that one
3431
+ // uses an incremental staticClassCache lookup per mutation, which scales to
3432
+ // thousands of elements. A previous version of this method also installed its
3433
+ // own MutationObserver here that called getUsedClasses() on EVERY mutation
3434
+ // (a full document-wide O(N) DOM scan), so on a busy page with N≥3000 the
3435
+ // scan cost exceeded the inter-mutation interval and the main thread froze
3436
+ // (~100% CPU on docs site, 3042 elements). That observer was redundant with
3437
+ // the incremental one and has been removed.
3442
3438
  TailwindCompiler.prototype.startProcessing = async function () {
3443
3439
  if (this.usesStaticPrerenderUtilities) return;
3444
3440
  try {
3445
- // Start initial compilation immediately
3446
- const initialCompilation = this.compile();
3447
-
3448
- // Set up observer while compilation is running
3449
- this.observer = new MutationObserver((mutations) => {
3450
- const relevantMutations = mutations.filter(mutation => {
3451
- if (mutation.type === 'attributes' &&
3452
- mutation.attributeName === 'class') {
3453
- return true;
3454
- }
3455
- if (mutation.type === 'childList') {
3456
- return Array.from(mutation.addedNodes).some(node =>
3457
- node.nodeType === Node.ELEMENT_NODE);
3458
- }
3459
- return false;
3460
- });
3461
-
3462
- if (relevantMutations.length === 0) return;
3463
-
3464
- // Check if there are any new classes that need processing
3465
- const newClasses = this.getUsedClasses();
3466
- if (newClasses.classes.length === 0) return;
3467
-
3468
- if (this.compileTimeout) {
3469
- clearTimeout(this.compileTimeout);
3470
- }
3471
- this.compileTimeout = setTimeout(() => {
3472
- if (!this.isCompiling) {
3473
- this.compile();
3474
- }
3475
- }, this.options.debounceTime);
3476
- });
3477
-
3478
- // Start observing immediately
3479
- this.observer.observe(document.documentElement, {
3480
- childList: true,
3481
- subtree: true,
3482
- attributes: true,
3483
- attributeFilter: ['class']
3484
- });
3485
-
3486
- // Wait for initial compilation
3487
- await initialCompilation;
3488
-
3441
+ await this.compile();
3489
3442
  this.hasInitialized = true;
3490
3443
  } catch (error) {
3491
3444
  console.error('Error starting Tailwind compiler:', error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mnfst",
3
- "version": "0.5.91",
3
+ "version": "0.5.93",
4
4
  "private": false,
5
5
  "workspaces": [
6
6
  "templates/starter",