mnfst 0.5.54 → 0.5.57
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.data.js +41 -10
- package/lib/manifest.js +1 -0
- package/lib/manifest.svg.js +228 -0
- package/package.json +1 -1
package/lib/manifest.data.js
CHANGED
|
@@ -11760,20 +11760,51 @@ async function initializeDataSourcesPlugin() {
|
|
|
11760
11760
|
_currentUrl: existingStore._currentUrl || window.location.pathname
|
|
11761
11761
|
});
|
|
11762
11762
|
|
|
11763
|
-
// Pre-load
|
|
11763
|
+
// Pre-load all local file-backed data sources so $x.* accessors see
|
|
11764
|
+
// real data on the very first render pass.
|
|
11765
|
+
//
|
|
11766
|
+
// Each source is at most ONE fetch regardless of type:
|
|
11767
|
+
// - Simple string paths (e.g. "/data/clients.yaml") → one file.
|
|
11768
|
+
// - Localized objects (e.g. { "en": "...", "fr": "..." }) → only the
|
|
11769
|
+
// current locale file is fetched (+ default locale for fallback
|
|
11770
|
+
// merging if different), NOT all 35 variants.
|
|
11771
|
+
// - Single CSV with embedded locales → one file.
|
|
11772
|
+
//
|
|
11773
|
+
// Skipped (remain on-demand):
|
|
11774
|
+
// - Appwrite collections / buckets — require auth/session context.
|
|
11775
|
+
// - API-URL sources — may have side-effects or auth requirements.
|
|
11776
|
+
// - The special "manifest" key — handled separately below.
|
|
11777
|
+
//
|
|
11778
|
+
// Without pre-loading, sources like $x.clients load asynchronously on
|
|
11779
|
+
// first access, causing a visible flash in the SPA and missing data in
|
|
11780
|
+
// prerender snapshots.
|
|
11764
11781
|
try {
|
|
11765
11782
|
const manifest = await window.ManifestDataConfig.ensureManifest();
|
|
11766
11783
|
const locale = (typeof document !== 'undefined' && document.documentElement?.lang) || (typeof Alpine !== 'undefined' && Alpine.store('locale')?.current) || 'en';
|
|
11784
|
+
const isAppwriteCollection = window.ManifestDataConfig.isAppwriteCollection;
|
|
11767
11785
|
|
|
11768
|
-
|
|
11769
|
-
|
|
11770
|
-
|
|
11771
|
-
|
|
11772
|
-
if (
|
|
11773
|
-
|
|
11774
|
-
|
|
11775
|
-
}
|
|
11776
|
-
|
|
11786
|
+
if (manifest?.data) {
|
|
11787
|
+
const preloadNames = [];
|
|
11788
|
+
for (const [name, source] of Object.entries(manifest.data)) {
|
|
11789
|
+
if (name === 'manifest') continue; // handled separately below
|
|
11790
|
+
if (isAppwriteCollection(source)) continue;
|
|
11791
|
+
if (source && typeof source === 'object' && source.url) continue;
|
|
11792
|
+
preloadNames.push(name);
|
|
11793
|
+
}
|
|
11794
|
+
|
|
11795
|
+
if (preloadNames.length > 0) {
|
|
11796
|
+
await Promise.all(
|
|
11797
|
+
preloadNames.map(async (name) => {
|
|
11798
|
+
try {
|
|
11799
|
+
const data = await loadDataSource(name, locale);
|
|
11800
|
+
if (data != null && window.ManifestDataStore?.updateStore) {
|
|
11801
|
+
window.ManifestDataStore.updateStore(name, data, { loading: false, error: null, ready: true, allowDuringInit: true });
|
|
11802
|
+
}
|
|
11803
|
+
} catch (err) {
|
|
11804
|
+
console.warn(`[Manifest Data] Failed to pre-load ${name}:`, err);
|
|
11805
|
+
}
|
|
11806
|
+
})
|
|
11807
|
+
);
|
|
11777
11808
|
}
|
|
11778
11809
|
}
|
|
11779
11810
|
|
package/lib/manifest.js
CHANGED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/* Manifest SVG */
|
|
2
|
+
|
|
3
|
+
const svgCache = new Map();
|
|
4
|
+
|
|
5
|
+
function resolveFetchPath(pathOrContent) {
|
|
6
|
+
let resolved = pathOrContent;
|
|
7
|
+
if (!pathOrContent.startsWith('/')) {
|
|
8
|
+
const base = (typeof window.getManifestBase === 'function' ? window.getManifestBase() : '') || '';
|
|
9
|
+
const basePath = base.replace(/\/$/, '') || '';
|
|
10
|
+
resolved = (basePath ? basePath + '/' : '/') + pathOrContent;
|
|
11
|
+
}
|
|
12
|
+
return resolved;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function looksLikeInlineSvgMarkup(str) {
|
|
16
|
+
if (typeof str !== 'string') return false;
|
|
17
|
+
const t = str.trim();
|
|
18
|
+
return t.length > 0 && t.startsWith('<') && /<svg[\s>]/i.test(t);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function isLikelySvgFilePath(str) {
|
|
22
|
+
if (typeof str !== 'string') return false;
|
|
23
|
+
const t = str.trim();
|
|
24
|
+
if (!t || looksLikeInlineSvgMarkup(t)) return false;
|
|
25
|
+
return (
|
|
26
|
+
t.includes('.svg') ||
|
|
27
|
+
t.startsWith('/') ||
|
|
28
|
+
(t.includes('/') && !t.includes('<'))
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function parseSvgRoot(svgText) {
|
|
33
|
+
const trimmed = svgText.trim();
|
|
34
|
+
const parser = new DOMParser();
|
|
35
|
+
const doc = parser.parseFromString(trimmed, 'image/svg+xml');
|
|
36
|
+
const err = doc.querySelector('parsererror');
|
|
37
|
+
if (err) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
const root = doc.documentElement;
|
|
41
|
+
if (!root || root.tagName.toLowerCase() !== 'svg') {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
return root;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function resolveSvgString(pathOrContent) {
|
|
48
|
+
if (pathOrContent === undefined || pathOrContent === null) {
|
|
49
|
+
return { ok: false, error: 'empty', text: '' };
|
|
50
|
+
}
|
|
51
|
+
const str = typeof pathOrContent === 'string' ? pathOrContent : String(pathOrContent);
|
|
52
|
+
if (!str.trim()) {
|
|
53
|
+
return { ok: false, error: 'empty', text: '' };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (looksLikeInlineSvgMarkup(str)) {
|
|
57
|
+
return { ok: true, text: str };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!isLikelySvgFilePath(str)) {
|
|
61
|
+
if (parseSvgRoot(str)) {
|
|
62
|
+
return { ok: true, text: str };
|
|
63
|
+
}
|
|
64
|
+
return { ok: false, error: 'not-path', text: str };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
let resolvedPath;
|
|
68
|
+
try {
|
|
69
|
+
resolvedPath = resolveFetchPath(str);
|
|
70
|
+
|
|
71
|
+
if (svgCache.has(resolvedPath)) {
|
|
72
|
+
return { ok: true, text: svgCache.get(resolvedPath) };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const response = await fetch(resolvedPath);
|
|
76
|
+
if (!response.ok) {
|
|
77
|
+
console.warn(`[Manifest SVG] Failed to fetch: ${resolvedPath}`);
|
|
78
|
+
const errText = `<!-- SVG load error: ${resolvedPath} -->`;
|
|
79
|
+
svgCache.set(resolvedPath, errText);
|
|
80
|
+
return { ok: false, error: 'fetch', text: errText };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const text = await response.text();
|
|
84
|
+
svgCache.set(resolvedPath, text);
|
|
85
|
+
return { ok: true, text };
|
|
86
|
+
} catch (error) {
|
|
87
|
+
console.error(`[Manifest SVG] Error fetching: ${str}`, error);
|
|
88
|
+
const errText = `<!-- SVG fetch error: ${error.message} -->`;
|
|
89
|
+
if (resolvedPath) {
|
|
90
|
+
svgCache.set(resolvedPath, errText);
|
|
91
|
+
}
|
|
92
|
+
return { ok: false, error: 'fetch', text: errText };
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function injectSvgChildren(hostEl, svgText) {
|
|
97
|
+
const root = parseSvgRoot(svgText);
|
|
98
|
+
if (!root) {
|
|
99
|
+
console.warn('[Manifest SVG] Invalid SVG markup');
|
|
100
|
+
hostEl.replaceChildren();
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const clone = document.importNode(root, true);
|
|
104
|
+
hostEl.replaceChildren(clone);
|
|
105
|
+
|
|
106
|
+
if (window.Alpine && typeof window.Alpine.initTree === 'function') {
|
|
107
|
+
if (window.Alpine.nextTick) {
|
|
108
|
+
window.Alpine.nextTick(() => {
|
|
109
|
+
window.Alpine.initTree(hostEl);
|
|
110
|
+
});
|
|
111
|
+
} else {
|
|
112
|
+
setTimeout(() => {
|
|
113
|
+
window.Alpine.initTree(hostEl);
|
|
114
|
+
}, 0);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async function initializeSvgPlugin() {
|
|
120
|
+
try {
|
|
121
|
+
const isPrerenderedPage = !!(
|
|
122
|
+
document.querySelector('meta[name="manifest:prerendered"]') &&
|
|
123
|
+
document.querySelector('meta[name="manifest:prerendered"]').getAttribute('content') !== '0'
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
Alpine.directive('svg', (el, { expression }, { effect, evaluateLater }) => {
|
|
127
|
+
if (!expression) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const hasBakedContent =
|
|
132
|
+
isPrerenderedPage &&
|
|
133
|
+
el.querySelector('svg') &&
|
|
134
|
+
el.querySelector('svg').parentElement === el;
|
|
135
|
+
|
|
136
|
+
if (hasBakedContent) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Only wrap bare tokens that look like file paths — not Alpine identifiers (e.g. `star`
|
|
141
|
+
// from x-data). Same intent as x-markdown path literals vs expressions.
|
|
142
|
+
let processedExpression = expression;
|
|
143
|
+
const looksLikeUnquotedPath =
|
|
144
|
+
!expression.includes('+') &&
|
|
145
|
+
!expression.includes('`') &&
|
|
146
|
+
!expression.includes('${') &&
|
|
147
|
+
!expression.startsWith('$') &&
|
|
148
|
+
!expression.startsWith("'") &&
|
|
149
|
+
!expression.startsWith('"') &&
|
|
150
|
+
(expression.includes('/') || expression.includes('.svg'));
|
|
151
|
+
if (looksLikeUnquotedPath) {
|
|
152
|
+
processedExpression = `'${expression.replace(/'/g, "\\'")}'`;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const getSvgSource = evaluateLater(processedExpression);
|
|
156
|
+
let lastText = null;
|
|
157
|
+
|
|
158
|
+
effect(() => {
|
|
159
|
+
getSvgSource(async (pathOrContent) => {
|
|
160
|
+
if (pathOrContent === undefined || pathOrContent === '' || pathOrContent === null) {
|
|
161
|
+
el.replaceChildren();
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const resolved = await resolveSvgString(
|
|
166
|
+
pathOrContent === undefined ? expression : pathOrContent
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
if (!resolved.ok && resolved.error === 'not-path') {
|
|
170
|
+
console.warn('[Manifest SVG] Expected a file path or SVG markup');
|
|
171
|
+
el.replaceChildren();
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const text = resolved.text || '';
|
|
176
|
+
if (text === lastText) {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
lastText = text;
|
|
180
|
+
|
|
181
|
+
if (!text.trim()) {
|
|
182
|
+
el.replaceChildren();
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
injectSvgChildren(el, text);
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
} catch (error) {
|
|
191
|
+
console.error('[Manifest] Failed to initialize SVG plugin:', error);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
let svgPluginInitialized = false;
|
|
196
|
+
|
|
197
|
+
async function ensureSvgPluginInitialized() {
|
|
198
|
+
if (svgPluginInitialized) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
if (!window.Alpine || typeof window.Alpine.directive !== 'function') {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
svgPluginInitialized = true;
|
|
206
|
+
await initializeSvgPlugin();
|
|
207
|
+
|
|
208
|
+
if (window.Alpine && typeof window.Alpine.initTree === 'function') {
|
|
209
|
+
const existing = document.querySelectorAll('[x-svg]');
|
|
210
|
+
existing.forEach((el) => {
|
|
211
|
+
if (!el.__x) {
|
|
212
|
+
window.Alpine.initTree(el);
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
window.ensureSvgPluginInitialized = ensureSvgPluginInitialized;
|
|
219
|
+
|
|
220
|
+
if (document.readyState === 'loading') {
|
|
221
|
+
document.addEventListener('DOMContentLoaded', ensureSvgPluginInitialized);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
document.addEventListener('alpine:init', ensureSvgPluginInitialized);
|
|
225
|
+
|
|
226
|
+
if (window.Alpine && typeof window.Alpine.directive === 'function') {
|
|
227
|
+
ensureSvgPluginInitialized();
|
|
228
|
+
}
|