mnfst 0.5.218 → 0.5.220
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/lib/manifest.appwrite.data.js +1 -1
- package/lib/manifest.code.js +126 -46
- package/lib/manifest.css +94 -222
- package/lib/manifest.d.ts +2 -0
- package/lib/manifest.data.js +26 -3
- package/lib/manifest.defer.js +145 -34
- package/lib/manifest.edit.css +65 -27
- package/lib/manifest.edit.js +213 -28
- package/lib/manifest.integrity.json +11 -11
- package/lib/manifest.js +43 -2
- package/lib/manifest.markdown.js +22 -12
- package/lib/manifest.min.css +1 -1
- package/lib/manifest.router.js +58 -69
- package/lib/manifest.sw.js +1 -1
- package/lib/manifest.text.edit.css +29 -0
- package/lib/manifest.text.edit.js +164 -25
- package/lib/manifest.utilities.js +246 -100
- package/lib/manifest.utilities.node.mjs +195 -35
- package/package.json +1 -1
|
@@ -599,6 +599,7 @@ class TailwindCompiler {
|
|
|
599
599
|
this.lastClassesHash = ''; // Track changes in used classes
|
|
600
600
|
this.staticClassCache = new Set(); // Cache classes found in static HTML/components
|
|
601
601
|
this.dynamicClassCache = new Set(); // Cache classes that appear dynamically
|
|
602
|
+
this.quietClasses = new Set(); // Seen only inside ignored elements, not yet compiled
|
|
602
603
|
this.hasScannedStatic = false; // Track if we've done initial static scan
|
|
603
604
|
this.staticScanPromise = null; // Promise for initial static scan
|
|
604
605
|
this.ignoredClassPatterns = [ // Patterns for classes to ignore
|
|
@@ -1338,6 +1339,7 @@ TailwindCompiler.prototype.cleanupCache = function () {
|
|
|
1338
1339
|
TailwindCompiler.prototype.ensureUtilityStylesLast = function () {
|
|
1339
1340
|
if (this.styleElement && this.styleElement.parentNode && document.head.lastElementChild !== this.styleElement) {
|
|
1340
1341
|
document.head.appendChild(this.styleElement);
|
|
1342
|
+
if (this.resyncUtilitiesText) this.resyncUtilitiesText();
|
|
1341
1343
|
}
|
|
1342
1344
|
};
|
|
1343
1345
|
|
|
@@ -1714,6 +1716,8 @@ TailwindCompiler.prototype.fetchThemeContent = async function () {
|
|
|
1714
1716
|
// Wait for all fetches to complete
|
|
1715
1717
|
await Promise.all(fetchPromises);
|
|
1716
1718
|
|
|
1719
|
+
// Order-free identity (fetches land in any order) for "theme unchanged" checks
|
|
1720
|
+
this.lastThemeKey = Array.from(themeContents).sort().join('\n');
|
|
1717
1721
|
return Array.from(themeContents).join('\n');
|
|
1718
1722
|
};
|
|
1719
1723
|
|
|
@@ -2450,6 +2454,10 @@ TailwindCompiler.prototype.generateUtilitiesFromVars = function (cssText, usedDa
|
|
|
2450
2454
|
const generatedRules = new Set(); // Track generated rules to prevent duplicates
|
|
2451
2455
|
const variables = this.extractThemeVariables(cssText);
|
|
2452
2456
|
const { classes: usedClasses, variableSuffixes } = usedData;
|
|
2457
|
+
// Optional: raw rules + their full-regen order keys (incremental apply places deltas by them)
|
|
2458
|
+
const rulesOut = usedData.rulesOut, keysOut = usedData.keysOut;
|
|
2459
|
+
let slot = null;
|
|
2460
|
+
const emit = (rule, sub) => { utilities.push(rule); if (rulesOut) { rulesOut.push(rule); keysOut.push(slot.concat(slot[3] ? 2 : sub)); } }; // opacity rules order by discovery
|
|
2453
2461
|
|
|
2454
2462
|
if (variables.size === 0) {
|
|
2455
2463
|
return '';
|
|
@@ -2474,7 +2482,7 @@ TailwindCompiler.prototype.generateUtilitiesFromVars = function (cssText, usedDa
|
|
|
2474
2482
|
if (usedClasses.includes(baseClass)) {
|
|
2475
2483
|
const rule = `.${escapeClassName(baseClass)} { ${css} }`;
|
|
2476
2484
|
if (!generatedRules.has(rule)) {
|
|
2477
|
-
|
|
2485
|
+
emit(rule, 0);
|
|
2478
2486
|
generatedRules.add(rule);
|
|
2479
2487
|
}
|
|
2480
2488
|
}
|
|
@@ -2485,7 +2493,7 @@ TailwindCompiler.prototype.generateUtilitiesFromVars = function (cssText, usedDa
|
|
|
2485
2493
|
css + ' !important';
|
|
2486
2494
|
const rule = `.${escapeClassName('!' + baseClass)} { ${importantCss} }`;
|
|
2487
2495
|
if (!generatedRules.has(rule)) {
|
|
2488
|
-
|
|
2496
|
+
emit(rule, 1);
|
|
2489
2497
|
generatedRules.add(rule);
|
|
2490
2498
|
}
|
|
2491
2499
|
}
|
|
@@ -2554,26 +2562,33 @@ TailwindCompiler.prototype.generateUtilitiesFromVars = function (cssText, usedDa
|
|
|
2554
2562
|
rule;
|
|
2555
2563
|
|
|
2556
2564
|
if (!generatedRules.has(finalRule)) {
|
|
2557
|
-
|
|
2565
|
+
emit(finalRule, 2);
|
|
2558
2566
|
generatedRules.add(finalRule);
|
|
2559
2567
|
}
|
|
2560
2568
|
}
|
|
2561
2569
|
};
|
|
2562
2570
|
|
|
2571
|
+
// Every generated class ends in its variable's suffix: skip variables no used class mentions
|
|
2572
|
+
const usedBases = usedClasses.map(cls => this.parseClassName(cls).baseClass);
|
|
2573
|
+
|
|
2563
2574
|
// Generate utilities based on variable prefix
|
|
2575
|
+
let vi = -1;
|
|
2564
2576
|
for (const [varName, varValue] of variables.entries()) {
|
|
2577
|
+
vi++;
|
|
2565
2578
|
if (!varName.match(this.regexPatterns.tailwindPrefix)) {
|
|
2566
2579
|
continue;
|
|
2567
2580
|
}
|
|
2568
2581
|
|
|
2569
2582
|
const suffix = varName.split('-').slice(1).join('-');
|
|
2583
|
+
if (!usedBases.some(base => base.includes(suffix))) continue;
|
|
2570
2584
|
const value = `var(--${varName})`;
|
|
2571
2585
|
const prefix = varName.split('-')[0] + '-';
|
|
2572
2586
|
const generator = this.utilityGenerators[prefix];
|
|
2573
2587
|
|
|
2574
2588
|
if (generator) {
|
|
2575
2589
|
const utilityPairs = generator(suffix, value);
|
|
2576
|
-
for (
|
|
2590
|
+
for (let pi = 0; pi < utilityPairs.length; pi++) {
|
|
2591
|
+
const [className, css] = utilityPairs[pi];
|
|
2577
2592
|
// Check if this specific utility class is actually used (including variants and important)
|
|
2578
2593
|
const isUsed = usedClasses.some(cls => {
|
|
2579
2594
|
// Parse the class to extract the base utility name
|
|
@@ -2586,6 +2601,7 @@ TailwindCompiler.prototype.generateUtilitiesFromVars = function (cssText, usedDa
|
|
|
2586
2601
|
(baseClass.startsWith('!') && baseClass.slice(1) === className);
|
|
2587
2602
|
});
|
|
2588
2603
|
if (isUsed) {
|
|
2604
|
+
slot = [0, vi, pi, 0];
|
|
2589
2605
|
generateUtility(className, css);
|
|
2590
2606
|
}
|
|
2591
2607
|
|
|
@@ -2617,6 +2633,7 @@ TailwindCompiler.prototype.generateUtilitiesFromVars = function (cssText, usedDa
|
|
|
2617
2633
|
const opacity = opacityBaseClass.split('/')[1];
|
|
2618
2634
|
const opacityValue = `color-mix(in oklch, ${value} ${opacity}%, transparent)`;
|
|
2619
2635
|
const opacityCss = css.replace(value, opacityValue);
|
|
2636
|
+
slot = [0, vi, pi, 1];
|
|
2620
2637
|
generateUtility(opacityBaseClass, opacityCss);
|
|
2621
2638
|
}
|
|
2622
2639
|
}
|
|
@@ -2636,6 +2653,9 @@ TailwindCompiler.prototype.generateCustomUtilities = function (usedData) {
|
|
|
2636
2653
|
const utilities = [];
|
|
2637
2654
|
const generatedRules = new Set();
|
|
2638
2655
|
const { classes: usedClasses } = usedData;
|
|
2656
|
+
const rulesOut = usedData.rulesOut, keysOut = usedData.keysOut;
|
|
2657
|
+
let slot = null;
|
|
2658
|
+
const emit = (rule, sub) => { utilities.push(rule); if (rulesOut) { rulesOut.push(rule); keysOut.push(slot.concat(sub)); } };
|
|
2639
2659
|
|
|
2640
2660
|
// Helper to clean up [object Object] from CSS strings
|
|
2641
2661
|
const cleanCssString = (css) => {
|
|
@@ -2791,7 +2811,7 @@ TailwindCompiler.prototype.generateCustomUtilities = function (usedData) {
|
|
|
2791
2811
|
rule = `.${escapeClassName('!' + baseClass)} { ${importantCss} }`;
|
|
2792
2812
|
}
|
|
2793
2813
|
if (!generatedRules.has(rule)) {
|
|
2794
|
-
|
|
2814
|
+
emit(rule, 1);
|
|
2795
2815
|
generatedRules.add(rule);
|
|
2796
2816
|
}
|
|
2797
2817
|
}
|
|
@@ -2965,14 +2985,16 @@ TailwindCompiler.prototype.generateCustomUtilities = function (usedData) {
|
|
|
2965
2985
|
}
|
|
2966
2986
|
|
|
2967
2987
|
if (!generatedRules.has(finalRule)) {
|
|
2968
|
-
|
|
2988
|
+
emit(finalRule, 2);
|
|
2969
2989
|
generatedRules.add(finalRule);
|
|
2970
2990
|
}
|
|
2971
2991
|
}
|
|
2972
2992
|
};
|
|
2973
2993
|
|
|
2974
2994
|
// Generate utilities for each custom class that's actually used
|
|
2995
|
+
let ci = -1;
|
|
2975
2996
|
for (const [className, cssOrSelector] of this.customUtilities.entries()) {
|
|
2997
|
+
ci++;
|
|
2976
2998
|
// Normalize class name: if it starts with !, extract the base name
|
|
2977
2999
|
const hasImportantPrefix = className.startsWith('!');
|
|
2978
3000
|
const baseClassName = hasImportantPrefix ? className.slice(1) : className;
|
|
@@ -3012,9 +3034,12 @@ TailwindCompiler.prototype.generateCustomUtilities = function (usedData) {
|
|
|
3012
3034
|
// Generate utility with base class name (without !)
|
|
3013
3035
|
// The CSS already has !important if className started with !
|
|
3014
3036
|
if (typeof cssOrSelector === 'string') {
|
|
3037
|
+
slot = [1, ci, 0, 0];
|
|
3015
3038
|
generateUtility(baseClassName, normalizedCss, null);
|
|
3016
3039
|
} else if (Array.isArray(cssOrSelector)) {
|
|
3017
|
-
for (
|
|
3040
|
+
for (let ai = 0; ai < cssOrSelector.length; ai++) {
|
|
3041
|
+
const entry = cssOrSelector[ai];
|
|
3042
|
+
slot = [1, ci, ai, 0];
|
|
3018
3043
|
if (entry && entry.css && entry.selector) {
|
|
3019
3044
|
// Ensure entry.css is a string (not an object)
|
|
3020
3045
|
const extracted = typeof entry.css === 'string' ? entry.css :
|
|
@@ -3035,6 +3060,7 @@ TailwindCompiler.prototype.generateCustomUtilities = function (usedData) {
|
|
|
3035
3060
|
String(cssOrSelector.css));
|
|
3036
3061
|
const selectorCss = cleanCssString(extracted);
|
|
3037
3062
|
|
|
3063
|
+
slot = [1, ci, 0, 0];
|
|
3038
3064
|
generateUtility(baseClassName, selectorCss, {
|
|
3039
3065
|
selector: cssOrSelector.selector,
|
|
3040
3066
|
fullBlock: cssOrSelector.fullBlock || false
|
|
@@ -3050,49 +3076,169 @@ TailwindCompiler.prototype.generateCustomUtilities = function (usedData) {
|
|
|
3050
3076
|
}
|
|
3051
3077
|
};
|
|
3052
3078
|
|
|
3053
|
-
//
|
|
3054
|
-
TailwindCompiler.prototype.
|
|
3055
|
-
if (!utilitiesText) return '';
|
|
3056
|
-
|
|
3057
|
-
// Split into individual rules (each rule starts with . or @media)
|
|
3079
|
+
// Split generated CSS into top-level rules (each starts with . or an at-rule)
|
|
3080
|
+
TailwindCompiler.prototype.splitUtilityRules = function (utilitiesText) {
|
|
3058
3081
|
const rules = [];
|
|
3059
3082
|
let currentRule = '';
|
|
3060
|
-
const
|
|
3061
|
-
|
|
3062
|
-
for (const line of lines) {
|
|
3063
|
-
// Check if this line starts a new rule
|
|
3083
|
+
for (const line of utilitiesText.split('\n')) {
|
|
3064
3084
|
if (line.trim().match(/^(\.|@media|@layer|@supports)/)) {
|
|
3065
|
-
|
|
3066
|
-
if (currentRule.trim()) {
|
|
3067
|
-
rules.push(currentRule.trim());
|
|
3068
|
-
}
|
|
3085
|
+
if (currentRule.trim()) rules.push(currentRule.trim());
|
|
3069
3086
|
currentRule = line;
|
|
3070
3087
|
} else {
|
|
3071
|
-
// Continue current rule
|
|
3072
3088
|
currentRule += '\n' + line;
|
|
3073
3089
|
}
|
|
3074
3090
|
}
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3091
|
+
if (currentRule.trim()) rules.push(currentRule.trim());
|
|
3092
|
+
return rules;
|
|
3093
|
+
};
|
|
3094
|
+
|
|
3095
|
+
// Sort utilities so base classes come before variants (@media)
|
|
3096
|
+
TailwindCompiler.prototype.sortUtilities = function (utilitiesText) {
|
|
3097
|
+
if (!utilitiesText) return '';
|
|
3098
|
+
const rules = this.splitUtilityRules(utilitiesText);
|
|
3081
3099
|
rules.sort((a, b) => {
|
|
3082
3100
|
const aHasMedia = a.startsWith('@media');
|
|
3083
3101
|
const bHasMedia = b.startsWith('@media');
|
|
3084
|
-
|
|
3085
|
-
// Base utilities come before variants
|
|
3086
3102
|
if (!aHasMedia && bHasMedia) return -1;
|
|
3087
3103
|
if (aHasMedia && !bHasMedia) return 1;
|
|
3088
|
-
|
|
3089
|
-
// Same type, maintain order
|
|
3090
3104
|
return 0;
|
|
3091
3105
|
});
|
|
3092
|
-
|
|
3093
3106
|
return rules.join('\n\n');
|
|
3094
3107
|
};
|
|
3095
3108
|
|
|
3109
|
+
// ---- Incremental apply: new rules go in through CSSOM, never a whole-sheet rewrite ----
|
|
3110
|
+
// Entries mirror the layer's cssRules 1:1, each keyed by where a full regen would emit it
|
|
3111
|
+
// ([media, section, var/entry, pair/array, region, sub]); deltas are inserted at that index.
|
|
3112
|
+
|
|
3113
|
+
const utilitiesCssFrom = (applied) => `@layer utilities {\n${applied.entries.map(e => e.rule).join('\n\n')}\n}`;
|
|
3114
|
+
const isMediaRule = (rule) => rule.trim().startsWith('@media');
|
|
3115
|
+
|
|
3116
|
+
function compareKeys(a, b) {
|
|
3117
|
+
for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return a[i] - b[i];
|
|
3118
|
+
return 0;
|
|
3119
|
+
}
|
|
3120
|
+
|
|
3121
|
+
// Raw rules + order keys for a class set, as a full regen would emit them (base before @media, stable)
|
|
3122
|
+
TailwindCompiler.prototype.generateKeyedUtilities = function (themeCss, classes) {
|
|
3123
|
+
const usedData = { classes, variableSuffixes: [], rulesOut: [], keysOut: [] };
|
|
3124
|
+
this.generateUtilitiesFromVars(themeCss, usedData);
|
|
3125
|
+
this.generateCustomUtilities(usedData);
|
|
3126
|
+
const out = usedData.rulesOut.map((rule, i) => {
|
|
3127
|
+
const media = isMediaRule(rule) ? 1 : 0;
|
|
3128
|
+
return { rule, key: [media].concat(usedData.keysOut[i]) };
|
|
3129
|
+
});
|
|
3130
|
+
return out.filter(e => !e.key[0]).concat(out.filter(e => e.key[0]));
|
|
3131
|
+
};
|
|
3132
|
+
|
|
3133
|
+
TailwindCompiler.prototype.utilitiesLayerRule = function () {
|
|
3134
|
+
const sheet = this.styleElement && this.styleElement.sheet;
|
|
3135
|
+
if (!sheet || typeof CSSLayerBlockRule === 'undefined') return null;
|
|
3136
|
+
try {
|
|
3137
|
+
for (const rule of sheet.cssRules) {
|
|
3138
|
+
if (rule instanceof CSSLayerBlockRule && rule.name === 'utilities') return rule;
|
|
3139
|
+
}
|
|
3140
|
+
} catch (e) { }
|
|
3141
|
+
return null;
|
|
3142
|
+
};
|
|
3143
|
+
|
|
3144
|
+
// Entries must match the parsed sheet rule-for-rule, or deltas could land at the wrong index
|
|
3145
|
+
TailwindCompiler.prototype.entriesMatchSheet = function (entries) {
|
|
3146
|
+
const layer = this.utilitiesLayerRule();
|
|
3147
|
+
if (!layer || layer.cssRules.length !== entries.length) return false;
|
|
3148
|
+
for (let i = 0; i < entries.length; i++) {
|
|
3149
|
+
if ((layer.cssRules[i] instanceof CSSMediaRule) !== !!entries[i].key[0]) return false;
|
|
3150
|
+
}
|
|
3151
|
+
return true;
|
|
3152
|
+
};
|
|
3153
|
+
|
|
3154
|
+
// After a full write: remember its rules so later compiles can insert beside them
|
|
3155
|
+
TailwindCompiler.prototype.recordAppliedUtilities = function (entries, themeCss) {
|
|
3156
|
+
this._applied = null;
|
|
3157
|
+
if (window.__manifestRender === true || !this.entriesMatchSheet(entries)) return;
|
|
3158
|
+
this._applied = {
|
|
3159
|
+
themeKey: this.lastThemeKey,
|
|
3160
|
+
themeCss, // keys index this exact variable order
|
|
3161
|
+
entries,
|
|
3162
|
+
rules: new Set(entries.map(e => e.rule)),
|
|
3163
|
+
classes: new Set(this.dynamicClassCache),
|
|
3164
|
+
dirty: false // inserted rules not yet in textContent
|
|
3165
|
+
};
|
|
3166
|
+
};
|
|
3167
|
+
|
|
3168
|
+
// Moving <style> rebuilds its sheet from textContent: write inserted rules back first
|
|
3169
|
+
TailwindCompiler.prototype.resyncUtilitiesText = function () {
|
|
3170
|
+
const applied = this._applied;
|
|
3171
|
+
if (!applied || !applied.dirty) return;
|
|
3172
|
+
applied.dirty = false;
|
|
3173
|
+
this.styleElement.textContent = utilitiesCssFrom(applied);
|
|
3174
|
+
if (!this.entriesMatchSheet(applied.entries)) this._applied = null;
|
|
3175
|
+
};
|
|
3176
|
+
|
|
3177
|
+
// Prerender snapshots read #manifest-styles text, so render passes always rewrite it
|
|
3178
|
+
TailwindCompiler.prototype.canCompileDelta = function () {
|
|
3179
|
+
return !!this._applied && window.__manifestRender !== true && !!this.utilitiesLayerRule();
|
|
3180
|
+
};
|
|
3181
|
+
|
|
3182
|
+
// Generate only never-compiled classes and insert each rule where a full regen would put it.
|
|
3183
|
+
// Returns false when a rule's place depends on class discovery order: caller does a full compile.
|
|
3184
|
+
TailwindCompiler.prototype.compileDelta = function (themeCss) {
|
|
3185
|
+
const applied = this._applied;
|
|
3186
|
+
const candidates = [];
|
|
3187
|
+
for (const cls of this.dynamicClassCache) if (!applied.classes.has(cls)) candidates.push(cls);
|
|
3188
|
+
const classes = this.filterStaticallyCoveredClasses(candidates);
|
|
3189
|
+
const fresh = classes.length ? this.generateKeyedUtilities(themeCss, classes).filter(e => !applied.rules.has(e.rule)) : [];
|
|
3190
|
+
const entries = applied.entries;
|
|
3191
|
+
|
|
3192
|
+
// Place every rule first; bail before touching the sheet if any is ambiguous
|
|
3193
|
+
const plan = [];
|
|
3194
|
+
for (const e of fresh) {
|
|
3195
|
+
let lo = 0, hi = entries.length;
|
|
3196
|
+
while (lo < hi) { const mid = (lo + hi) >> 1; if (compareKeys(entries[mid].key, e.key) <= 0) lo = mid + 1; else hi = mid; }
|
|
3197
|
+
// Same utility, same group, both variants: full regen orders them by discovery
|
|
3198
|
+
if (e.key[5] === 2) {
|
|
3199
|
+
const sameSlot = (x) => x && compareKeys(x.key.slice(0, 5), e.key.slice(0, 5)) === 0 && x.key[5] === 2;
|
|
3200
|
+
if (sameSlot(entries[lo - 1]) || sameSlot(entries[lo]) || plan.some(q => sameSlot(q.e))) return false;
|
|
3201
|
+
}
|
|
3202
|
+
plan.push({ e, at: lo });
|
|
3203
|
+
}
|
|
3204
|
+
for (const cls of candidates) applied.classes.add(cls);
|
|
3205
|
+
if (!plan.length) return true;
|
|
3206
|
+
|
|
3207
|
+
const layer = this.utilitiesLayerRule();
|
|
3208
|
+
try {
|
|
3209
|
+
// Insert in key order; each insert shifts later indices by one
|
|
3210
|
+
plan.sort((x, y) => compareKeys(x.e.key, y.e.key));
|
|
3211
|
+
plan.forEach((q, n) => {
|
|
3212
|
+
layer.insertRule(q.e.rule, q.at + n);
|
|
3213
|
+
entries.splice(q.at + n, 0, q.e);
|
|
3214
|
+
applied.rules.add(q.e.rule);
|
|
3215
|
+
});
|
|
3216
|
+
} catch (err) {
|
|
3217
|
+
// A rule the parser splits or drops: the planned indices no longer hold
|
|
3218
|
+
this._applied = null;
|
|
3219
|
+
return false;
|
|
3220
|
+
}
|
|
3221
|
+
applied.dirty = true;
|
|
3222
|
+
this.schedulePersistentSave(themeCss);
|
|
3223
|
+
return true;
|
|
3224
|
+
};
|
|
3225
|
+
|
|
3226
|
+
// Coalesce cache writes: localStorage serialisation is too slow to run per compile
|
|
3227
|
+
TailwindCompiler.prototype.schedulePersistentSave = function (themeCss) {
|
|
3228
|
+
clearTimeout(this._persistTimer);
|
|
3229
|
+
this._persistTimer = setTimeout(() => {
|
|
3230
|
+
if (!this._applied) return;
|
|
3231
|
+
const classesHash = Array.from(this.dynamicClassCache).sort().join(',');
|
|
3232
|
+
const themeHash = this.generateThemeHash(themeCss);
|
|
3233
|
+
this.cache.set(`${classesHash}-${themeHash}`, {
|
|
3234
|
+
css: utilitiesCssFrom(this._applied),
|
|
3235
|
+
timestamp: Date.now(),
|
|
3236
|
+
themeHash
|
|
3237
|
+
});
|
|
3238
|
+
this.savePersistentCache();
|
|
3239
|
+
}, 1000);
|
|
3240
|
+
};
|
|
3241
|
+
|
|
3096
3242
|
// Helper function to filter critical utilities that are already in layer utilities
|
|
3097
3243
|
TailwindCompiler.prototype.filterCriticalUtilities = function(criticalText, layerUtilities) {
|
|
3098
3244
|
if (!criticalText || !layerUtilities) return '';
|
|
@@ -3196,6 +3342,7 @@ TailwindCompiler.prototype.compile = async function () {
|
|
|
3196
3342
|
}
|
|
3197
3343
|
this.lastCompileTime = now;
|
|
3198
3344
|
this.isCompiling = true;
|
|
3345
|
+
this.quietClasses.clear(); // this compile covers them
|
|
3199
3346
|
window.__manifestUtilitiesPending = (window.__manifestUtilitiesPending || 0) + 1;
|
|
3200
3347
|
this._compileCounted = true;
|
|
3201
3348
|
|
|
@@ -3286,6 +3433,13 @@ TailwindCompiler.prototype.compile = async function () {
|
|
|
3286
3433
|
return;
|
|
3287
3434
|
}
|
|
3288
3435
|
|
|
3436
|
+
// Theme unchanged since the last full write: insert only the new classes
|
|
3437
|
+
let prefetchedTheme = null;
|
|
3438
|
+
if (this.canCompileDelta()) {
|
|
3439
|
+
prefetchedTheme = await this.fetchThemeContent();
|
|
3440
|
+
if (prefetchedTheme && this.lastThemeKey === this._applied.themeKey && this.compileDelta(this._applied.themeCss)) return;
|
|
3441
|
+
}
|
|
3442
|
+
|
|
3289
3443
|
// For subsequent compilations, check for new dynamic classes
|
|
3290
3444
|
const usedData = this.getUsedClasses();
|
|
3291
3445
|
const dynamicClasses = Array.from(this.dynamicClassCache);
|
|
@@ -3296,7 +3450,7 @@ TailwindCompiler.prototype.compile = async function () {
|
|
|
3296
3450
|
// Check if dynamic classes have actually changed
|
|
3297
3451
|
if (dynamicClassesHash !== this.lastClassesHash || !this.hasInitialized) {
|
|
3298
3452
|
// Fetch CSS content for dynamic compilation
|
|
3299
|
-
const themeCss = await this.fetchThemeContent();
|
|
3453
|
+
const themeCss = prefetchedTheme || await this.fetchThemeContent();
|
|
3300
3454
|
if (!themeCss) {
|
|
3301
3455
|
this.isCompiling = false;
|
|
3302
3456
|
return;
|
|
@@ -3323,8 +3477,10 @@ TailwindCompiler.prototype.compile = async function () {
|
|
|
3323
3477
|
if (hasVariableChanges || dynamicClassesHash !== this.lastClassesHash) {
|
|
3324
3478
|
|
|
3325
3479
|
// Generate both variable-based and custom utilities
|
|
3326
|
-
const
|
|
3327
|
-
const
|
|
3480
|
+
const rulesOut = [], keysOut = [];
|
|
3481
|
+
const keyedData = Object.assign({}, usedData, { rulesOut, keysOut });
|
|
3482
|
+
const varUtilities = this.generateUtilitiesFromVars(themeCss, keyedData);
|
|
3483
|
+
const customUtilitiesGenerated = this.generateCustomUtilities(keyedData);
|
|
3328
3484
|
|
|
3329
3485
|
let allUtilities = [varUtilities, customUtilitiesGenerated].filter(Boolean).join('\n\n');
|
|
3330
3486
|
// Sort utilities so base classes come before variants
|
|
@@ -3350,6 +3506,8 @@ TailwindCompiler.prototype.compile = async function () {
|
|
|
3350
3506
|
});
|
|
3351
3507
|
});
|
|
3352
3508
|
this.lastClassesHash = dynamicClassesHash;
|
|
3509
|
+
const entries = rulesOut.map((rule, i) => ({ rule, key: [isMediaRule(rule) ? 1 : 0].concat(keysOut[i]) }));
|
|
3510
|
+
this.recordAppliedUtilities(entries.filter(e => !e.key[0]).concat(entries.filter(e => e.key[0])), themeCss);
|
|
3353
3511
|
|
|
3354
3512
|
// Save to cache for next page load
|
|
3355
3513
|
const themeHash = this.generateThemeHash(themeCss);
|
|
@@ -3361,6 +3519,8 @@ TailwindCompiler.prototype.compile = async function () {
|
|
|
3361
3519
|
});
|
|
3362
3520
|
this.savePersistentCache();
|
|
3363
3521
|
}
|
|
3522
|
+
} else {
|
|
3523
|
+
this._applied = null; // theme text moved: order keys are stale until the next full write
|
|
3364
3524
|
}
|
|
3365
3525
|
}
|
|
3366
3526
|
|
|
@@ -3826,80 +3986,29 @@ TailwindCompiler.prototype.setupComponentLoadListener = function () {
|
|
|
3826
3986
|
}
|
|
3827
3987
|
});
|
|
3828
3988
|
|
|
3829
|
-
// Single MutationObserver for all DOM changes
|
|
3989
|
+
// Single MutationObserver for all DOM changes: recompile only when a class not yet seen appears
|
|
3830
3990
|
const observer = new MutationObserver((mutations) => {
|
|
3831
|
-
let
|
|
3991
|
+
let fresh = false;
|
|
3832
3992
|
|
|
3833
3993
|
for (const mutation of mutations) {
|
|
3834
|
-
// Skip attribute changes that don't affect utilities
|
|
3835
3994
|
if (mutation.type === 'attributes') {
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
if (this.ignoredAttributes.includes(attributeName)) {
|
|
3840
|
-
continue;
|
|
3841
|
-
}
|
|
3842
|
-
|
|
3843
|
-
// Only care about class attribute changes
|
|
3844
|
-
if (attributeName !== 'class') {
|
|
3845
|
-
continue;
|
|
3846
|
-
}
|
|
3847
|
-
|
|
3848
|
-
// If it's a class change, check if we have new classes that need utilities
|
|
3849
|
-
const element = mutation.target;
|
|
3850
|
-
if (element.nodeType === Node.ELEMENT_NODE) {
|
|
3851
|
-
const currentClasses = Array.from(element.classList || []);
|
|
3852
|
-
const newClasses = currentClasses.filter(cls => {
|
|
3853
|
-
// Skip ignored patterns
|
|
3854
|
-
if (this.ignoredClassPatterns.some(pattern => pattern.test(cls))) {
|
|
3855
|
-
return false;
|
|
3856
|
-
}
|
|
3857
|
-
|
|
3858
|
-
// Check if this class is new (not in our cache)
|
|
3859
|
-
return !this.staticClassCache.has(cls) && !this.dynamicClassCache.has(cls);
|
|
3860
|
-
});
|
|
3861
|
-
|
|
3862
|
-
if (newClasses.length > 0) {
|
|
3863
|
-
// Add new classes to dynamic cache
|
|
3864
|
-
newClasses.forEach(cls => this.dynamicClassCache.add(cls));
|
|
3865
|
-
shouldRecompile = true;
|
|
3866
|
-
break;
|
|
3867
|
-
}
|
|
3868
|
-
}
|
|
3869
|
-
}
|
|
3870
|
-
else if (mutation.type === 'childList') {
|
|
3995
|
+
if (this.ignoredAttributes.includes(mutation.attributeName) || mutation.attributeName !== 'class') continue;
|
|
3996
|
+
if (mutation.target.nodeType === Node.ELEMENT_NODE && this.collectNewClasses(mutation.target)) fresh = true;
|
|
3997
|
+
} else if (mutation.type === 'childList') {
|
|
3871
3998
|
for (const node of mutation.addedNodes) {
|
|
3872
|
-
if (node.nodeType
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
continue;
|
|
3881
|
-
}
|
|
3882
|
-
|
|
3883
|
-
// Only recompile for significant changes using configurable selectors
|
|
3884
|
-
const hasSignificantChange = this.significantChangeSelectors.some(selector => {
|
|
3885
|
-
try {
|
|
3886
|
-
return node.matches?.(selector) || node.querySelector?.(selector);
|
|
3887
|
-
} catch (e) {
|
|
3888
|
-
return false; // Invalid selector
|
|
3889
|
-
}
|
|
3890
|
-
});
|
|
3891
|
-
|
|
3892
|
-
if (hasSignificantChange) {
|
|
3893
|
-
shouldRecompile = true;
|
|
3894
|
-
break;
|
|
3895
|
-
}
|
|
3896
|
-
}
|
|
3999
|
+
if (node.nodeType !== Node.ELEMENT_NODE) continue;
|
|
4000
|
+
const isIgnoredElement = this.ignoredElementSelectors.some(selector =>
|
|
4001
|
+
node.tagName?.toLowerCase() === selector.toLowerCase() ||
|
|
4002
|
+
node.closest(selector)
|
|
4003
|
+
);
|
|
4004
|
+
// Ignored subtrees (code blocks) are collected for the next compile but never trigger one
|
|
4005
|
+
if (this.collectSubtreeClasses(node, isIgnoredElement)) fresh = true;
|
|
4006
|
+
else if (!fresh && !isIgnoredElement && !this.hasInitialized && this.isSignificantNode(node)) fresh = true;
|
|
3897
4007
|
}
|
|
3898
4008
|
}
|
|
3899
|
-
if (shouldRecompile) break;
|
|
3900
4009
|
}
|
|
3901
4010
|
|
|
3902
|
-
if (
|
|
4011
|
+
if (fresh) {
|
|
3903
4012
|
debouncedCompile();
|
|
3904
4013
|
}
|
|
3905
4014
|
});
|
|
@@ -3913,6 +4022,43 @@ TailwindCompiler.prototype.setupComponentLoadListener = function () {
|
|
|
3913
4022
|
});
|
|
3914
4023
|
};
|
|
3915
4024
|
|
|
4025
|
+
// Record classes not seen before; true when any were new. Quiet = collect without triggering
|
|
4026
|
+
TailwindCompiler.prototype.collectNewClasses = function (el, quiet) {
|
|
4027
|
+
const value = el.getAttribute && el.getAttribute('class');
|
|
4028
|
+
if (!value) return false;
|
|
4029
|
+
let fresh = false;
|
|
4030
|
+
for (const cls of value.split(/\s+/)) {
|
|
4031
|
+
if (!cls || this.staticClassCache.has(cls)) continue;
|
|
4032
|
+
if (this.dynamicClassCache.has(cls)) {
|
|
4033
|
+
if (!quiet && this.quietClasses.delete(cls)) fresh = true;
|
|
4034
|
+
continue;
|
|
4035
|
+
}
|
|
4036
|
+
if (this.ignoredClassPatterns.some(pattern => pattern.test(cls))) continue;
|
|
4037
|
+
this.dynamicClassCache.add(cls);
|
|
4038
|
+
if (quiet) this.quietClasses.add(cls); else fresh = true;
|
|
4039
|
+
}
|
|
4040
|
+
return fresh;
|
|
4041
|
+
};
|
|
4042
|
+
|
|
4043
|
+
TailwindCompiler.prototype.collectSubtreeClasses = function (node, quiet) {
|
|
4044
|
+
let fresh = this.collectNewClasses(node, quiet);
|
|
4045
|
+
const descendants = node.getElementsByTagName ? node.getElementsByTagName('*') : [];
|
|
4046
|
+
for (let i = 0; i < descendants.length; i++) {
|
|
4047
|
+
if (this.collectNewClasses(descendants[i], quiet)) fresh = true;
|
|
4048
|
+
}
|
|
4049
|
+
return fresh;
|
|
4050
|
+
};
|
|
4051
|
+
|
|
4052
|
+
TailwindCompiler.prototype.isSignificantNode = function (node) {
|
|
4053
|
+
return this.significantChangeSelectors.some(selector => {
|
|
4054
|
+
try {
|
|
4055
|
+
return node.matches?.(selector) || node.querySelector?.(selector);
|
|
4056
|
+
} catch (e) {
|
|
4057
|
+
return false;
|
|
4058
|
+
}
|
|
4059
|
+
});
|
|
4060
|
+
};
|
|
4061
|
+
|
|
3916
4062
|
// Initial compilation only. DOM observation is owned by
|
|
3917
4063
|
// setupComponentLoadListener (incremental, scales to thousands of elements);
|
|
3918
4064
|
// don't add a per-mutation getUsedClasses() scan here — it froze the main
|