mnfst 0.5.21 → 0.5.23
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/dist/manifest.appwrite.auth.js +4 -1
- package/dist/manifest.code.js +17 -17
- package/dist/manifest.components.js +66 -39
- package/dist/manifest.data.js +38 -1
- package/dist/manifest.js +61 -9
- package/dist/manifest.localization.js +8 -4
- package/dist/manifest.utilities.js +42 -0
- package/package.json +1 -1
|
@@ -8,11 +8,14 @@
|
|
|
8
8
|
|
|
9
9
|
/* Auth config */
|
|
10
10
|
|
|
11
|
-
// Load manifest if not already loaded
|
|
11
|
+
// Load manifest if not already loaded (loader may set __manifestLoaded / registry.manifest)
|
|
12
12
|
async function ensureManifest() {
|
|
13
13
|
if (window.ManifestComponentsRegistry?.manifest) {
|
|
14
14
|
return window.ManifestComponentsRegistry.manifest;
|
|
15
15
|
}
|
|
16
|
+
if (window.__manifestLoaded) {
|
|
17
|
+
return window.__manifestLoaded;
|
|
18
|
+
}
|
|
16
19
|
|
|
17
20
|
try {
|
|
18
21
|
const response = await fetch('/manifest.json');
|
package/dist/manifest.code.js
CHANGED
|
@@ -47,11 +47,6 @@ async function loadHighlightJS() {
|
|
|
47
47
|
return hljsPromise;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
// Preload highlight.js as soon as script loads
|
|
51
|
-
loadHighlightJS().catch(() => {
|
|
52
|
-
// Silently ignore errors during preload
|
|
53
|
-
});
|
|
54
|
-
|
|
55
50
|
// Optional optimization: Configure utilities plugin if present
|
|
56
51
|
if (window.ManifestUtilities) {
|
|
57
52
|
// Tell utilities plugin to ignore code-related DOM changes and classes
|
|
@@ -798,19 +793,24 @@ function initializeCodePlugin() {
|
|
|
798
793
|
customElements.define('x-code-group', XCodeGroupElement);
|
|
799
794
|
}
|
|
800
795
|
|
|
801
|
-
//
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
});
|
|
808
|
-
|
|
809
|
-
// Also listen for the event on the document body for better coverage
|
|
810
|
-
document.body.addEventListener('manifest:code-blocks-converted', async () => {
|
|
811
|
-
await processExistingCodeBlocks();
|
|
812
|
-
});
|
|
796
|
+
// Listen for markdown plugin conversions (always process when new blocks appear)
|
|
797
|
+
const runProcess = () => processExistingCodeBlocks();
|
|
798
|
+
document.addEventListener('manifest:code-blocks-converted', runProcess);
|
|
799
|
+
if (document.body) {
|
|
800
|
+
document.body.addEventListener('manifest:code-blocks-converted', runProcess);
|
|
801
|
+
}
|
|
813
802
|
|
|
803
|
+
// Defer loading highlight.js until first code block is in view (or process immediately if none to observe)
|
|
804
|
+
const codeTargets = document.querySelectorAll('pre > code:not(.hljs):not([data-highlighted="yes"]), x-code:not([data-highlighted="yes"])');
|
|
805
|
+
if (codeTargets.length === 0) {
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
808
|
+
const io = new IntersectionObserver((entries) => {
|
|
809
|
+
if (!entries.some(e => e.isIntersecting)) return;
|
|
810
|
+
io.disconnect();
|
|
811
|
+
runProcess();
|
|
812
|
+
}, { rootMargin: '100px', threshold: 0 });
|
|
813
|
+
codeTargets.forEach(el => io.observe(el));
|
|
814
814
|
} catch (error) {
|
|
815
815
|
console.error('[Manifest] Failed to initialize code plugin:', error);
|
|
816
816
|
}
|
|
@@ -6,40 +6,50 @@ window.ManifestComponentsRegistry = {
|
|
|
6
6
|
registered: new Set(),
|
|
7
7
|
preloaded: [],
|
|
8
8
|
initialize() {
|
|
9
|
-
//
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
});
|
|
28
|
-
this.preloaded = (this.manifest?.preloadedComponents || []).map(path => path.split('/').pop().replace('.html', ''));
|
|
29
|
-
} else {
|
|
30
|
-
console.warn('[Manifest] Failed to load manifest.json (HTTP', req.status + ')');
|
|
9
|
+
// Use loader-provided manifest if set; otherwise load synchronously (standalone)
|
|
10
|
+
let manifest = window.__manifestLoaded || this.manifest;
|
|
11
|
+
if (!manifest) {
|
|
12
|
+
try {
|
|
13
|
+
const manifestUrl = (document.querySelector('link[rel="manifest"]')?.getAttribute('href')) || '/manifest.json';
|
|
14
|
+
const req = new XMLHttpRequest();
|
|
15
|
+
req.open('GET', manifestUrl + (manifestUrl.includes('?') ? '&' : '?') + 't=' + Date.now(), false);
|
|
16
|
+
req.setRequestHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
|
|
17
|
+
req.setRequestHeader('Pragma', 'no-cache');
|
|
18
|
+
req.setRequestHeader('Expires', '0');
|
|
19
|
+
req.send(null);
|
|
20
|
+
if (req.status === 200) {
|
|
21
|
+
manifest = JSON.parse(req.responseText);
|
|
22
|
+
} else {
|
|
23
|
+
console.warn('[Manifest] Failed to load manifest.json (HTTP', req.status + ')');
|
|
24
|
+
}
|
|
25
|
+
} catch (e) {
|
|
26
|
+
console.warn('[Manifest] Failed to load manifest.json:', e.message);
|
|
31
27
|
}
|
|
32
|
-
}
|
|
33
|
-
|
|
28
|
+
}
|
|
29
|
+
if (manifest) {
|
|
30
|
+
this.manifest = manifest;
|
|
31
|
+
const allComponents = [
|
|
32
|
+
...(this.manifest?.preloadedComponents || []),
|
|
33
|
+
...(this.manifest?.components || [])
|
|
34
|
+
];
|
|
35
|
+
allComponents.forEach(path => {
|
|
36
|
+
const name = path.split('/').pop().replace('.html', '');
|
|
37
|
+
this.registered.add(name);
|
|
38
|
+
});
|
|
39
|
+
this.preloaded = (this.manifest?.preloadedComponents || []).map(path => path.split('/').pop().replace('.html', ''));
|
|
34
40
|
}
|
|
35
41
|
}
|
|
36
42
|
};
|
|
37
43
|
|
|
38
44
|
// Components loader
|
|
45
|
+
// Uses cache for resolved content and _loading for in-flight promises so duplicate
|
|
46
|
+
// loadComponent(name) calls share one network request.
|
|
39
47
|
window.ManifestComponentsLoader = {
|
|
40
48
|
cache: {},
|
|
49
|
+
_loading: {},
|
|
41
50
|
initialize() {
|
|
42
51
|
this.cache = {};
|
|
52
|
+
this._loading = {};
|
|
43
53
|
// Preload components listed in registry.preloaded
|
|
44
54
|
const registry = window.ManifestComponentsRegistry;
|
|
45
55
|
if (registry && Array.isArray(registry.preloaded)) {
|
|
@@ -54,6 +64,9 @@ window.ManifestComponentsLoader = {
|
|
|
54
64
|
if (this.cache[name]) {
|
|
55
65
|
return this.cache[name];
|
|
56
66
|
}
|
|
67
|
+
if (this._loading[name]) {
|
|
68
|
+
return this._loading[name];
|
|
69
|
+
}
|
|
57
70
|
const registry = window.ManifestComponentsRegistry;
|
|
58
71
|
if (!registry || !registry.manifest) {
|
|
59
72
|
console.warn('[Manifest] Manifest not loaded, cannot load component:', name);
|
|
@@ -65,19 +78,25 @@ window.ManifestComponentsLoader = {
|
|
|
65
78
|
console.warn('[Manifest] Component', name, 'not found in manifest.');
|
|
66
79
|
return null;
|
|
67
80
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
81
|
+
const promise = (async () => {
|
|
82
|
+
try {
|
|
83
|
+
const response = await fetch('/' + path);
|
|
84
|
+
if (!response.ok) {
|
|
85
|
+
console.warn('[Manifest] HTML file not found for component', name, 'at path:', path, '(HTTP', response.status + ')');
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
const content = await response.text();
|
|
89
|
+
this.cache[name] = content;
|
|
90
|
+
return content;
|
|
91
|
+
} catch (error) {
|
|
92
|
+
console.warn('[Manifest] Failed to load component', name, 'from', path + ':', error.message);
|
|
72
93
|
return null;
|
|
94
|
+
} finally {
|
|
95
|
+
delete this._loading[name];
|
|
73
96
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
} catch (error) {
|
|
78
|
-
console.warn('[Manifest] Failed to load component', name, 'from', path + ':', error.message);
|
|
79
|
-
return null;
|
|
80
|
-
}
|
|
97
|
+
})();
|
|
98
|
+
this._loading[name] = promise;
|
|
99
|
+
return promise;
|
|
81
100
|
}
|
|
82
101
|
};
|
|
83
102
|
|
|
@@ -716,19 +735,27 @@ function initializeComponents() {
|
|
|
716
735
|
window.dispatchEvent(new CustomEvent('manifest:components-ready'));
|
|
717
736
|
}
|
|
718
737
|
|
|
719
|
-
//
|
|
720
|
-
//
|
|
738
|
+
// When data plugin is loaded: wait for manifest:data-ready so $x.content is ready before components render.
|
|
739
|
+
// When data plugin is absent: init immediately (no artificial delay).
|
|
721
740
|
function waitForDataThenInitialize() {
|
|
741
|
+
const hasDataPlugin = typeof window.ManifestDataConfig !== 'undefined';
|
|
742
|
+
|
|
743
|
+
if (!hasDataPlugin) {
|
|
744
|
+
initializeComponents();
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
747
|
+
|
|
722
748
|
window.addEventListener('manifest:data-ready', () => {
|
|
723
749
|
initializeComponents();
|
|
724
750
|
}, { once: true });
|
|
725
751
|
|
|
726
|
-
// Fallback: if data plugin
|
|
752
|
+
// Fallback: if data plugin never fires (e.g. slow network, error), initialize anyway
|
|
753
|
+
const fallbackMs = 5000;
|
|
727
754
|
setTimeout(() => {
|
|
728
755
|
if (!window.__manifestComponentsInitialized) {
|
|
729
756
|
initializeComponents();
|
|
730
757
|
}
|
|
731
|
-
},
|
|
758
|
+
}, fallbackMs);
|
|
732
759
|
}
|
|
733
760
|
|
|
734
761
|
if (document.readyState === 'loading') {
|
package/dist/manifest.data.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
/* Manifest Data Sources - Configuration */
|
|
2
2
|
|
|
3
|
-
// Load manifest if not already loaded
|
|
3
|
+
// Load manifest if not already loaded (loader may set __manifestLoaded / registry.manifest)
|
|
4
4
|
async function ensureManifest() {
|
|
5
5
|
if (window.ManifestComponentsRegistry?.manifest) {
|
|
6
6
|
return window.ManifestComponentsRegistry.manifest;
|
|
7
7
|
}
|
|
8
|
+
if (window.__manifestLoaded) {
|
|
9
|
+
return window.__manifestLoaded;
|
|
10
|
+
}
|
|
8
11
|
|
|
9
12
|
try {
|
|
10
13
|
const response = await fetch('/manifest.json');
|
|
@@ -1148,10 +1151,38 @@ let yamlLoadingPromise = null;
|
|
|
1148
1151
|
let papaparse = null;
|
|
1149
1152
|
let csvLoadingPromise = null;
|
|
1150
1153
|
|
|
1154
|
+
// Collect all path-like strings from manifest.data (recursive; includes nested locale objects)
|
|
1155
|
+
function collectDataPaths(manifest) {
|
|
1156
|
+
const paths = [];
|
|
1157
|
+
if (!manifest?.data || typeof manifest.data !== 'object') return paths;
|
|
1158
|
+
function visit(val) {
|
|
1159
|
+
if (typeof val === 'string' && (val.startsWith('/') || /\.(yaml|yml|csv|json)$/i.test(val))) {
|
|
1160
|
+
paths.push(val);
|
|
1161
|
+
} else if (Array.isArray(val)) {
|
|
1162
|
+
val.forEach(visit);
|
|
1163
|
+
} else if (val && typeof val === 'object' && !Array.isArray(val)) {
|
|
1164
|
+
Object.values(val).forEach(visit);
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
Object.values(manifest.data).forEach(visit);
|
|
1168
|
+
return paths;
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
function manifestDataPathsInclude(manifest, extensions) {
|
|
1172
|
+
const paths = collectDataPaths(manifest);
|
|
1173
|
+
return paths.some(p => extensions.some(ext => p.toLowerCase().includes(ext)));
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1151
1176
|
async function loadYamlLibrary() {
|
|
1152
1177
|
if (jsyaml) return jsyaml;
|
|
1153
1178
|
if (yamlLoadingPromise) return yamlLoadingPromise;
|
|
1154
1179
|
|
|
1180
|
+
const manifest = await window.ManifestDataConfig?.ensureManifest?.();
|
|
1181
|
+
if (manifest && !manifestDataPathsInclude(manifest, ['.yaml', '.yml'])) {
|
|
1182
|
+
yamlLoadingPromise = Promise.reject(new Error('[Manifest Data] No YAML paths in manifest - skipping loader'));
|
|
1183
|
+
return yamlLoadingPromise;
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1155
1186
|
yamlLoadingPromise = new Promise((resolve, reject) => {
|
|
1156
1187
|
const script = document.createElement('script');
|
|
1157
1188
|
script.src = 'https://cdn.jsdelivr.net/npm/js-yaml/dist/js-yaml.min.js';
|
|
@@ -1181,6 +1212,12 @@ async function loadCSVParser() {
|
|
|
1181
1212
|
if (papaparse) return papaparse;
|
|
1182
1213
|
if (csvLoadingPromise) return csvLoadingPromise;
|
|
1183
1214
|
|
|
1215
|
+
const manifest = await window.ManifestDataConfig?.ensureManifest?.();
|
|
1216
|
+
if (manifest && !manifestDataPathsInclude(manifest, ['.csv'])) {
|
|
1217
|
+
csvLoadingPromise = Promise.reject(new Error('[Manifest Data] No CSV paths in manifest - skipping loader'));
|
|
1218
|
+
return csvLoadingPromise;
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1184
1221
|
csvLoadingPromise = new Promise((resolve, reject) => {
|
|
1185
1222
|
const script = document.createElement('script');
|
|
1186
1223
|
script.src = 'https://cdn.jsdelivr.net/npm/papaparse@latest/papaparse.min.js';
|
package/dist/manifest.js
CHANGED
|
@@ -52,6 +52,33 @@
|
|
|
52
52
|
'appwrite-presence': ['data']
|
|
53
53
|
};
|
|
54
54
|
|
|
55
|
+
// Derive default plugin list from manifest (only load data/localization/components when manifest needs them)
|
|
56
|
+
function getDefaultPluginsFromManifest(manifest) {
|
|
57
|
+
if (!manifest || typeof manifest !== 'object') {
|
|
58
|
+
return AVAILABLE_PLUGINS.slice();
|
|
59
|
+
}
|
|
60
|
+
const hasData = manifest.data && typeof manifest.data === 'object' && Object.keys(manifest.data).length > 0;
|
|
61
|
+
const hasComponents = (manifest.components?.length > 0) || (manifest.preloadedComponents?.length > 0);
|
|
62
|
+
const hasLocalization = (() => {
|
|
63
|
+
if (!manifest.data || typeof manifest.data !== 'object') return false;
|
|
64
|
+
for (const collection of Object.values(manifest.data)) {
|
|
65
|
+
if (!collection || typeof collection !== 'object') continue;
|
|
66
|
+
if (typeof collection.locales === 'string') return true;
|
|
67
|
+
for (const key of Object.keys(collection)) {
|
|
68
|
+
if (['url', 'headers', 'params', 'transform', 'defaultValue', 'locales'].includes(key)) continue;
|
|
69
|
+
if (/^[a-zA-Z]{2}(-[a-zA-Z]{2})?$/.test(key)) return true;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return false;
|
|
73
|
+
})();
|
|
74
|
+
return AVAILABLE_PLUGINS.filter(p => {
|
|
75
|
+
if (p === 'data') return hasData;
|
|
76
|
+
if (p === 'localization') return hasLocalization;
|
|
77
|
+
if (p === 'components') return hasComponents;
|
|
78
|
+
return true;
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
55
82
|
// Get plugin URL from CDN
|
|
56
83
|
function getPluginUrl(pluginName, version = DEFAULT_VERSION) {
|
|
57
84
|
const base = getBaseUrl(version);
|
|
@@ -193,12 +220,13 @@
|
|
|
193
220
|
const version = script.getAttribute('data-version') || DEFAULT_VERSION;
|
|
194
221
|
|
|
195
222
|
let pluginList = [];
|
|
223
|
+
const deriveFromManifest = !plugins;
|
|
196
224
|
|
|
197
225
|
if (plugins) {
|
|
198
226
|
// Explicit declaration - load only specified plugins (core + Appwrite)
|
|
199
227
|
pluginList = plugins.split(',').map(p => p.trim()).filter(p => p);
|
|
200
228
|
} else {
|
|
201
|
-
// Default:
|
|
229
|
+
// Default: start with all core plugins; loader will trim by manifest when manifest is available
|
|
202
230
|
pluginList = AVAILABLE_PLUGINS.slice();
|
|
203
231
|
}
|
|
204
232
|
|
|
@@ -213,6 +241,7 @@
|
|
|
213
241
|
|
|
214
242
|
return {
|
|
215
243
|
plugins: pluginList,
|
|
244
|
+
deriveFromManifest,
|
|
216
245
|
tailwind,
|
|
217
246
|
version
|
|
218
247
|
};
|
|
@@ -268,22 +297,45 @@
|
|
|
268
297
|
detectAppwriteFromManifest();
|
|
269
298
|
|
|
270
299
|
if (config && config.plugins.length > 0) {
|
|
271
|
-
|
|
300
|
+
const MANIFEST_DEPENDENT_PLUGINS = [
|
|
301
|
+
'data', 'localization', 'components',
|
|
302
|
+
'appwrite-auth', 'appwrite-data', 'appwrite-presence'
|
|
303
|
+
];
|
|
304
|
+
const manifestUrl = (document.querySelector('link[rel="manifest"]')?.getAttribute('href')) || '/manifest.json';
|
|
305
|
+
|
|
272
306
|
const loadPlugins = async () => {
|
|
273
|
-
|
|
307
|
+
let manifest = null;
|
|
308
|
+
let pluginsToLoad = config.plugins;
|
|
309
|
+
let manifestPromise = null;
|
|
310
|
+
|
|
311
|
+
if (config.deriveFromManifest) {
|
|
312
|
+
manifest = await fetch(manifestUrl).then(r => r.ok ? r.json() : null).catch(() => null);
|
|
313
|
+
pluginsToLoad = resolveDependencies(getDefaultPluginsFromManifest(manifest));
|
|
314
|
+
} else {
|
|
315
|
+
const needsManifest = config.plugins.some(p => MANIFEST_DEPENDENT_PLUGINS.includes(p));
|
|
316
|
+
if (needsManifest) {
|
|
317
|
+
manifestPromise = fetch(manifestUrl).then(r => r.ok ? r.json() : null).catch(() => null);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const pluginPromises = pluginsToLoad.map(pluginName => {
|
|
274
322
|
return addScript(pluginName, config.version).catch(error => {
|
|
275
323
|
console.warn(`[Manifest Loader] Failed to load plugin ${pluginName}:`, error);
|
|
276
324
|
});
|
|
277
325
|
});
|
|
278
|
-
|
|
279
|
-
// Load Tailwind in parallel if requested
|
|
280
326
|
if (config.tailwind) {
|
|
281
|
-
pluginPromises.push(loadTailwind(config.version).catch(
|
|
282
|
-
console.warn(`[Manifest Loader] Failed to load Tailwind:`, error);
|
|
283
|
-
}));
|
|
327
|
+
pluginPromises.push(loadTailwind(config.version).catch(() => {}));
|
|
284
328
|
}
|
|
285
|
-
|
|
286
329
|
await Promise.all(pluginPromises);
|
|
330
|
+
if (manifestPromise) {
|
|
331
|
+
manifest = await manifestPromise;
|
|
332
|
+
}
|
|
333
|
+
if (manifest && typeof window !== 'undefined') {
|
|
334
|
+
window.__manifestLoaded = manifest;
|
|
335
|
+
if (window.ManifestComponentsRegistry) {
|
|
336
|
+
window.ManifestComponentsRegistry.manifest = manifest;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
287
339
|
loadAlpine();
|
|
288
340
|
};
|
|
289
341
|
|
|
@@ -137,11 +137,15 @@ function initializeLocalizationPlugin() {
|
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
try {
|
|
140
|
-
|
|
141
|
-
if (!
|
|
142
|
-
|
|
140
|
+
let manifest = window.__manifestLoaded || window.ManifestComponentsRegistry?.manifest;
|
|
141
|
+
if (!manifest) {
|
|
142
|
+
const manifestUrl = (document.querySelector('link[rel="manifest"]')?.getAttribute('href')) || '/manifest.json';
|
|
143
|
+
const response = await fetch(manifestUrl);
|
|
144
|
+
if (!response.ok) {
|
|
145
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
146
|
+
}
|
|
147
|
+
manifest = await response.json();
|
|
143
148
|
}
|
|
144
|
-
const manifest = await response.json();
|
|
145
149
|
|
|
146
150
|
// Validate manifest structure
|
|
147
151
|
if (!manifest || typeof manifest !== 'object') {
|
|
@@ -489,6 +489,7 @@ class TailwindCompiler {
|
|
|
489
489
|
this.styleElement = document.createElement('style');
|
|
490
490
|
this.styleElement.id = 'utility-styles';
|
|
491
491
|
document.head.appendChild(this.styleElement);
|
|
492
|
+
this.setupUtilityStylesOrderObserver();
|
|
492
493
|
|
|
493
494
|
// Initialize properties
|
|
494
495
|
this.tailwindLink = null;
|
|
@@ -1028,6 +1029,7 @@ TailwindCompiler.prototype.generateSynchronousUtilities = function () {
|
|
|
1028
1029
|
} else {
|
|
1029
1030
|
this.styleElement.textContent = finalCss;
|
|
1030
1031
|
}
|
|
1032
|
+
this.ensureUtilityStylesLast();
|
|
1031
1033
|
|
|
1032
1034
|
// Clear critical styles once we have generated utilities
|
|
1033
1035
|
if (this.criticalStyleElement && generated.trim()) {
|
|
@@ -1116,6 +1118,8 @@ TailwindCompiler.prototype.loadAndApplyCache = function () {
|
|
|
1116
1118
|
if (cacheToUse && cacheToUse.css) {
|
|
1117
1119
|
const applyCacheStart = performance.now();
|
|
1118
1120
|
this.styleElement.textContent = cacheToUse.css;
|
|
1121
|
+
this.ensureUtilityStylesLast();
|
|
1122
|
+
this.scheduleEnsureUtilityStylesLast();
|
|
1119
1123
|
this.lastThemeHash = cacheToUse.themeHash;
|
|
1120
1124
|
|
|
1121
1125
|
// Also apply cache to critical style element
|
|
@@ -1192,6 +1196,38 @@ TailwindCompiler.prototype.cleanupCache = function () {
|
|
|
1192
1196
|
// Helper methods
|
|
1193
1197
|
// Utility functions for extracting, parsing, and processing CSS and classes
|
|
1194
1198
|
|
|
1199
|
+
// Ensure #utility-styles is last in head so our responsive/variant rules win over Tailwind and any later-injected styles
|
|
1200
|
+
TailwindCompiler.prototype.ensureUtilityStylesLast = function () {
|
|
1201
|
+
if (this.styleElement && this.styleElement.parentNode && document.head.lastElementChild !== this.styleElement) {
|
|
1202
|
+
document.head.appendChild(this.styleElement);
|
|
1203
|
+
}
|
|
1204
|
+
};
|
|
1205
|
+
|
|
1206
|
+
// When any element is added to head after ours, move #utility-styles to end. Handles CDN load order (e.g. Tailwind injecting after we run).
|
|
1207
|
+
TailwindCompiler.prototype.setupUtilityStylesOrderObserver = function () {
|
|
1208
|
+
if (!document.head || !this.styleElement) return;
|
|
1209
|
+
const self = this;
|
|
1210
|
+
const observer = new MutationObserver((mutations) => {
|
|
1211
|
+
for (const mutation of mutations) {
|
|
1212
|
+
if (mutation.type !== 'childList' || !mutation.addedNodes.length) continue;
|
|
1213
|
+
for (const node of mutation.addedNodes) {
|
|
1214
|
+
if (node.nodeType !== Node.ELEMENT_NODE || node === self.styleElement) continue;
|
|
1215
|
+
self.ensureUtilityStylesLast();
|
|
1216
|
+
break;
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
});
|
|
1220
|
+
observer.observe(document.head, { childList: true, subtree: false });
|
|
1221
|
+
};
|
|
1222
|
+
|
|
1223
|
+
// Schedule ensureUtilityStylesLast at 0ms, 100ms, 500ms so we win when Tailwind (or other scripts) inject styles later (e.g. CDN).
|
|
1224
|
+
TailwindCompiler.prototype.scheduleEnsureUtilityStylesLast = function () {
|
|
1225
|
+
const self = this;
|
|
1226
|
+
[0, 100, 500].forEach((ms) => {
|
|
1227
|
+
setTimeout(() => self.ensureUtilityStylesLast(), ms);
|
|
1228
|
+
});
|
|
1229
|
+
};
|
|
1230
|
+
|
|
1195
1231
|
// Discover CSS files from stylesheets and imports
|
|
1196
1232
|
TailwindCompiler.prototype.discoverCssFiles = function () {
|
|
1197
1233
|
try {
|
|
@@ -2981,6 +3017,8 @@ TailwindCompiler.prototype.compile = async function () {
|
|
|
2981
3017
|
const finalCss = `@layer utilities {\n${allUtilities}\n}`;
|
|
2982
3018
|
|
|
2983
3019
|
this.styleElement.textContent = finalCss;
|
|
3020
|
+
this.ensureUtilityStylesLast();
|
|
3021
|
+
this.scheduleEnsureUtilityStylesLast();
|
|
2984
3022
|
|
|
2985
3023
|
// Remove critical style element entirely after compilation
|
|
2986
3024
|
// Use requestAnimationFrame to ensure styles are painted before removing
|
|
@@ -2991,6 +3029,7 @@ TailwindCompiler.prototype.compile = async function () {
|
|
|
2991
3029
|
this.criticalStyleElement.parentNode.removeChild(this.criticalStyleElement);
|
|
2992
3030
|
this.criticalStyleElement = null;
|
|
2993
3031
|
}
|
|
3032
|
+
this.ensureUtilityStylesLast();
|
|
2994
3033
|
});
|
|
2995
3034
|
});
|
|
2996
3035
|
this.lastClassesHash = staticUsedData.classes.sort().join(',');
|
|
@@ -3060,6 +3099,8 @@ TailwindCompiler.prototype.compile = async function () {
|
|
|
3060
3099
|
const finalCss = `@layer utilities {\n${allUtilities}\n}`;
|
|
3061
3100
|
|
|
3062
3101
|
this.styleElement.textContent = finalCss;
|
|
3102
|
+
this.ensureUtilityStylesLast();
|
|
3103
|
+
this.scheduleEnsureUtilityStylesLast();
|
|
3063
3104
|
|
|
3064
3105
|
// Remove critical style element entirely after compilation
|
|
3065
3106
|
// Use requestAnimationFrame to ensure styles are painted before removing
|
|
@@ -3070,6 +3111,7 @@ TailwindCompiler.prototype.compile = async function () {
|
|
|
3070
3111
|
this.criticalStyleElement.parentNode.removeChild(this.criticalStyleElement);
|
|
3071
3112
|
this.criticalStyleElement = null;
|
|
3072
3113
|
}
|
|
3114
|
+
this.ensureUtilityStylesLast();
|
|
3073
3115
|
});
|
|
3074
3116
|
});
|
|
3075
3117
|
this.lastClassesHash = dynamicClassesHash;
|