@symbo.ls/utils 3.2.3 → 3.14.0
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/LICENSE +25 -12
- package/README.md +106 -0
- package/array.js +162 -0
- package/cache.js +7 -0
- package/cdn.js +83 -0
- package/component.js +25 -0
- package/cookie.js +71 -0
- package/{src/detectHeight.js → detectHeight.js} +3 -3
- package/dist/cjs/array.js +168 -0
- package/dist/cjs/browser.js +0 -2
- package/dist/cjs/cache.js +26 -0
- package/dist/cjs/cdn.js +85 -0
- package/dist/cjs/component.js +45 -0
- package/dist/cjs/cookie.js +75 -0
- package/dist/cjs/date.js +1 -3
- package/dist/cjs/detectHeight.js +4 -6
- package/dist/cjs/element.js +136 -0
- package/dist/cjs/env.js +43 -0
- package/dist/cjs/events.js +39 -0
- package/dist/cjs/extends.js +434 -0
- package/dist/cjs/fibonacci.js +1 -3
- package/dist/cjs/files.js +1 -3
- package/dist/cjs/function.js +114 -0
- package/dist/cjs/globals.js +30 -0
- package/dist/cjs/if.js +48 -0
- package/dist/cjs/index.js +38 -425
- package/dist/cjs/key.js +38 -0
- package/dist/cjs/keys.js +201 -0
- package/dist/cjs/load.js +5 -7
- package/dist/cjs/log.js +34 -0
- package/dist/cjs/metadata.js +310 -0
- package/dist/cjs/methods.js +599 -0
- package/dist/cjs/node.js +35 -0
- package/dist/cjs/object.js +634 -0
- package/dist/cjs/projectKeys.js +35 -0
- package/dist/cjs/props.js +282 -0
- package/dist/cjs/scaling.js +5 -169
- package/dist/cjs/scope.js +44 -0
- package/dist/cjs/sharedLibraries.js +146 -0
- package/dist/cjs/state.js +173 -0
- package/dist/cjs/string-extra.js +72 -0
- package/dist/cjs/string.js +183 -0
- package/dist/cjs/style.js +1 -3
- package/dist/cjs/tags.js +220 -0
- package/dist/cjs/triggerEvent.js +112 -0
- package/dist/cjs/types.js +74 -0
- package/dist/cjs/update.js +42 -0
- package/dist/esm/array.js +148 -0
- package/dist/esm/browser.js +14 -0
- package/dist/esm/cache.js +6 -0
- package/dist/esm/cdn.js +65 -0
- package/dist/esm/component.js +25 -0
- package/dist/esm/cookie.js +55 -0
- package/dist/esm/date.js +11 -0
- package/dist/esm/detectHeight.js +42 -0
- package/dist/esm/element.js +116 -0
- package/dist/esm/env.js +23 -0
- package/dist/esm/events.js +19 -0
- package/dist/esm/extends.js +414 -0
- package/dist/esm/fibonacci.js +10 -0
- package/dist/esm/files.js +4 -0
- package/dist/esm/function.js +94 -0
- package/dist/esm/globals.js +10 -0
- package/dist/esm/if.js +28 -0
- package/dist/esm/index.js +37 -0
- package/dist/esm/key.js +18 -0
- package/dist/esm/keys.js +181 -0
- package/dist/esm/load.js +151 -0
- package/dist/esm/log.js +14 -0
- package/dist/esm/metadata.js +290 -0
- package/dist/esm/methods.js +569 -0
- package/dist/esm/node.js +15 -0
- package/dist/esm/object.js +621 -0
- package/dist/esm/projectKeys.js +16 -0
- package/dist/esm/props.js +262 -0
- package/dist/esm/scaling.js +15 -0
- package/dist/esm/scope.js +24 -0
- package/dist/esm/sharedLibraries.js +126 -0
- package/dist/esm/state.js +163 -0
- package/dist/esm/string-extra.js +52 -0
- package/dist/esm/string.js +163 -0
- package/dist/esm/style.js +20 -0
- package/dist/esm/tags.js +200 -0
- package/dist/esm/triggerEvent.js +92 -0
- package/dist/esm/types.js +54 -0
- package/dist/esm/update.js +22 -0
- package/dist/iife/index.js +5818 -0
- package/element.js +148 -0
- package/env.js +15 -0
- package/events.js +19 -0
- package/extends.js +519 -0
- package/function.js +145 -0
- package/globals.js +8 -0
- package/if.js +30 -0
- package/index.js +41 -0
- package/key.js +20 -0
- package/keys.js +176 -0
- package/log.js +12 -0
- package/metadata.js +392 -0
- package/methods.js +636 -0
- package/node.js +37 -0
- package/object.js +803 -0
- package/package.json +38 -22
- package/projectKeys.js +30 -0
- package/props.js +327 -0
- package/{src/scaling.js → scaling.js} +1 -1
- package/scope.js +32 -0
- package/sharedLibraries.js +184 -0
- package/state.js +203 -0
- package/{src/index.js → string-extra.js} +11 -17
- package/string.js +206 -0
- package/tags.js +176 -0
- package/triggerEvent.js +102 -0
- package/types.js +55 -0
- package/update.js +27 -0
- package/dist/cjs/package.json +0 -4
- /package/{src/browser.js → browser.js} +0 -0
- /package/{src/date.js → date.js} +0 -0
- /package/{src/fibonacci.js → fibonacci.js} +0 -0
- /package/{src/files.js → files.js} +0 -0
- /package/{src/load.js → load.js} +0 -0
- /package/{src/style.js → style.js} +0 -0
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
import { isObject } from "./types.js";
|
|
2
|
+
const escapeHtml = (text) => {
|
|
3
|
+
if (text === null || text === void 0) return "";
|
|
4
|
+
const map = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" };
|
|
5
|
+
return text.toString().replace(/[&<>"']/g, (m) => map[m]);
|
|
6
|
+
};
|
|
7
|
+
const buildAttrs = (obj) => Object.entries(obj || {}).filter(([_, v]) => v !== void 0).map(([k, v]) => `${k}="${escapeHtml(v)}"`).join(" ");
|
|
8
|
+
const generateMetaTags = (metadata, isProduction) => {
|
|
9
|
+
if (!isProduction) {
|
|
10
|
+
metadata = { ...metadata, robots: "noindex" };
|
|
11
|
+
}
|
|
12
|
+
const tags = Object.entries(metadata).reduce(
|
|
13
|
+
(acc, [key, value]) => {
|
|
14
|
+
if (!value && value !== 0 && value !== false) return acc;
|
|
15
|
+
if (key === "title") {
|
|
16
|
+
acc.push(`<title>${escapeHtml(value)}</title>`);
|
|
17
|
+
return acc;
|
|
18
|
+
}
|
|
19
|
+
if (key === "canonical") {
|
|
20
|
+
acc.push(`<link rel="canonical" href="${escapeHtml(value)}">`);
|
|
21
|
+
return acc;
|
|
22
|
+
}
|
|
23
|
+
if (key === "manifest") {
|
|
24
|
+
acc.push(`<link rel="manifest" href="${escapeHtml(value)}">`);
|
|
25
|
+
return acc;
|
|
26
|
+
}
|
|
27
|
+
if (key === "favicon" || key === "favicons" || key === "icon" || key === "icons") {
|
|
28
|
+
const items = Array.isArray(value) ? value : [value];
|
|
29
|
+
items.forEach((item) => {
|
|
30
|
+
if (typeof item === "string") {
|
|
31
|
+
acc.push(`<link rel="icon" href="${escapeHtml(item)}">`);
|
|
32
|
+
} else if (typeof item === "object") {
|
|
33
|
+
const attrs = buildAttrs(item);
|
|
34
|
+
if (!/rel=/.test(attrs)) {
|
|
35
|
+
acc.push(`<link rel="icon" ${attrs}>`);
|
|
36
|
+
} else {
|
|
37
|
+
acc.push(`<link ${attrs}>`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
return acc;
|
|
42
|
+
}
|
|
43
|
+
if (key === "alternate") {
|
|
44
|
+
const alternates = Array.isArray(value) ? value : [value];
|
|
45
|
+
alternates.forEach((alt) => {
|
|
46
|
+
if (typeof alt === "object") {
|
|
47
|
+
const attrs = Object.entries(alt).filter(([_, attrValue]) => attrValue !== void 0).map(([attrKey, attrValue]) => `${attrKey}="${escapeHtml(attrValue)}"`).join(" ");
|
|
48
|
+
acc.push(`<link rel="alternate" ${attrs}>`);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
return acc;
|
|
52
|
+
}
|
|
53
|
+
const processMetaTag = (tagKey, tagValue, attrType = "name") => {
|
|
54
|
+
if (typeof tagValue === "string" || typeof tagValue === "number" || typeof tagValue === "boolean") {
|
|
55
|
+
acc.push(`<meta ${attrType}="${escapeHtml(tagKey)}" content="${escapeHtml(tagValue)}">`);
|
|
56
|
+
} else if (Array.isArray(tagValue)) {
|
|
57
|
+
tagValue.forEach((item) => {
|
|
58
|
+
if (typeof item === "string" || typeof item === "number" || typeof item === "boolean") {
|
|
59
|
+
acc.push(`<meta ${attrType}="${escapeHtml(tagKey)}" content="${escapeHtml(item)}">`);
|
|
60
|
+
} else if (typeof item === "object") {
|
|
61
|
+
const attrs = Object.entries(item).filter(([_, attrValue]) => attrValue !== void 0).map(([attrKey, attrValue]) => `${attrKey}="${escapeHtml(attrValue)}"`).join(" ");
|
|
62
|
+
acc.push(`<meta ${attrs}>`);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
} else if (typeof tagValue === "object") {
|
|
66
|
+
const attrs = Object.entries(tagValue).filter(([_, attrValue]) => attrValue !== void 0).map(([attrKey, attrValue]) => `${attrKey}="${escapeHtml(attrValue)}"`).join(" ");
|
|
67
|
+
acc.push(`<meta ${attrs}>`);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
if (key.startsWith("http-equiv:")) {
|
|
71
|
+
const httpEquivKey = key.replace("http-equiv:", "");
|
|
72
|
+
processMetaTag(httpEquivKey, value, "http-equiv");
|
|
73
|
+
return acc;
|
|
74
|
+
}
|
|
75
|
+
if (key.startsWith("itemprop:")) {
|
|
76
|
+
const itempropKey = key.replace("itemprop:", "");
|
|
77
|
+
processMetaTag(itempropKey, value, "itemprop");
|
|
78
|
+
return acc;
|
|
79
|
+
}
|
|
80
|
+
const prefixes = [
|
|
81
|
+
"og:",
|
|
82
|
+
"twitter:",
|
|
83
|
+
"fb:",
|
|
84
|
+
"article:",
|
|
85
|
+
"profile:",
|
|
86
|
+
"book:",
|
|
87
|
+
"business:",
|
|
88
|
+
"music:",
|
|
89
|
+
"product:",
|
|
90
|
+
"video:",
|
|
91
|
+
"DC:",
|
|
92
|
+
"DCTERMS:"
|
|
93
|
+
];
|
|
94
|
+
const prefix = prefixes.find((p) => key.startsWith(p));
|
|
95
|
+
if (prefix) {
|
|
96
|
+
const tagKey = key.replace(prefix, "");
|
|
97
|
+
processMetaTag(
|
|
98
|
+
`${prefix.replace(":", "")}:${tagKey}`,
|
|
99
|
+
value,
|
|
100
|
+
prefix === "twitter:" || prefix === "DC:" || prefix === "DCTERMS:" ? "name" : "property"
|
|
101
|
+
);
|
|
102
|
+
return acc;
|
|
103
|
+
}
|
|
104
|
+
if (key.startsWith("apple:") || key.startsWith("msapplication:")) {
|
|
105
|
+
const pfx = key.startsWith("apple:") ? "apple-" : "msapplication-";
|
|
106
|
+
const tagKey = key.replace(/^apple:|^msapplication:/, "");
|
|
107
|
+
processMetaTag(`${pfx}${tagKey}`, value, "name");
|
|
108
|
+
return acc;
|
|
109
|
+
}
|
|
110
|
+
processMetaTag(key, value);
|
|
111
|
+
return acc;
|
|
112
|
+
},
|
|
113
|
+
[
|
|
114
|
+
'<meta charset="UTF-8">',
|
|
115
|
+
'<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no">'
|
|
116
|
+
]
|
|
117
|
+
);
|
|
118
|
+
return tags.join("\n");
|
|
119
|
+
};
|
|
120
|
+
const isBareFilename = (val) => typeof val === "string" && val.length > 0 && !val.startsWith("/") && !val.startsWith("http");
|
|
121
|
+
function resolveFileReferences(metadata, files) {
|
|
122
|
+
if (!files || typeof files !== "object") return metadata;
|
|
123
|
+
const resolve = (val) => {
|
|
124
|
+
if (!isBareFilename(val)) return val;
|
|
125
|
+
const fileEntry = files[val];
|
|
126
|
+
if (fileEntry?.src) return fileEntry.src;
|
|
127
|
+
return val;
|
|
128
|
+
};
|
|
129
|
+
const result = { ...metadata };
|
|
130
|
+
for (const [key, value] of Object.entries(result)) {
|
|
131
|
+
if (typeof value === "string") {
|
|
132
|
+
result[key] = resolve(value);
|
|
133
|
+
} else if (Array.isArray(value)) {
|
|
134
|
+
result[key] = value.map(
|
|
135
|
+
(item) => typeof item === "string" ? resolve(item) : item
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return result;
|
|
140
|
+
}
|
|
141
|
+
function resolveDotPath(obj, path) {
|
|
142
|
+
if (!obj || !path) return void 0;
|
|
143
|
+
const parts = path.split(".");
|
|
144
|
+
let v = obj;
|
|
145
|
+
for (const p of parts) {
|
|
146
|
+
if (v == null || typeof v !== "object") return void 0;
|
|
147
|
+
v = v[p];
|
|
148
|
+
}
|
|
149
|
+
return v;
|
|
150
|
+
}
|
|
151
|
+
function resolveMetadataTemplates(metadata, data, ssrContext) {
|
|
152
|
+
const config = data.config || data.settings || {};
|
|
153
|
+
const polyglot = config.polyglot || data.polyglot;
|
|
154
|
+
const defaultLang = ssrContext?.lang || polyglot?.defaultLang || "en";
|
|
155
|
+
const translations = polyglot?.translations || {};
|
|
156
|
+
const langMap = translations[defaultLang] || {};
|
|
157
|
+
const state = ssrContext?.state || {};
|
|
158
|
+
const result = { ...metadata };
|
|
159
|
+
for (const [key, value] of Object.entries(result)) {
|
|
160
|
+
if (typeof value !== "string" || !value.includes("{{")) continue;
|
|
161
|
+
result[key] = value.replace(/\{\{\s*([^|{}]+?)\s*(?:\|\s*(\w+)\s*)?\}\}/g, (match, k, filter) => {
|
|
162
|
+
const trimmed = k.trim();
|
|
163
|
+
if (filter === "polyglot") {
|
|
164
|
+
return langMap[trimmed] ?? resolveDotPath(langMap, trimmed) ?? match;
|
|
165
|
+
}
|
|
166
|
+
if (filter === "getLocalStateLang") {
|
|
167
|
+
const resolved = resolveDotPath(state, trimmed + defaultLang);
|
|
168
|
+
return resolved ?? match;
|
|
169
|
+
}
|
|
170
|
+
const fromState = resolveDotPath(state, trimmed);
|
|
171
|
+
if (fromState !== void 0) return fromState;
|
|
172
|
+
return langMap[trimmed] ?? resolveDotPath(langMap, trimmed) ?? match;
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
return result;
|
|
176
|
+
}
|
|
177
|
+
function convertFunctionMetaToTemplates(metadata) {
|
|
178
|
+
const result = { ...metadata };
|
|
179
|
+
for (const [key, value] of Object.entries(result)) {
|
|
180
|
+
if (typeof value === "function") {
|
|
181
|
+
const src = value.toString();
|
|
182
|
+
const langMatch = src.match(/el\.call\(\s*["']getLocalStateLang["']\s*,\s*["']([^"']+)["']\s*\)/);
|
|
183
|
+
if (langMatch) {
|
|
184
|
+
result[key] = `{{ ${langMatch[1]} | getLocalStateLang }}`;
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
const polyMatch = src.match(/el\.call\(\s*["']polyglot["']\s*,\s*["']([^"']+)["']\s*\)/);
|
|
188
|
+
if (polyMatch) {
|
|
189
|
+
result[key] = `{{ ${polyMatch[1]} | polyglot }}`;
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
const stateMatch = src.match(/s\.(\w+(?:\.\w+)+)/);
|
|
193
|
+
if (stateMatch) {
|
|
194
|
+
result[key] = `{{ ${stateMatch[1]} }}`;
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
delete result[key];
|
|
198
|
+
} else if (typeof value === "string") {
|
|
199
|
+
const langMatch = value.match(/el\.call\(\s*["']getLocalStateLang["']\s*,\s*["']([^"']+)["']\s*\)/);
|
|
200
|
+
if (langMatch) {
|
|
201
|
+
result[key] = `{{ ${langMatch[1]} | getLocalStateLang }}`;
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
const polyMatch = value.match(/el\.call\(\s*["']polyglot["']\s*,\s*["']([^"']+)["']\s*\)/);
|
|
205
|
+
if (polyMatch) {
|
|
206
|
+
result[key] = `{{ ${polyMatch[1]} | polyglot }}`;
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
if (value.includes("=>") && value.includes("s.")) {
|
|
210
|
+
const stateMatch = value.match(/s\.(\w+(?:\.\w+)+)/);
|
|
211
|
+
if (stateMatch) {
|
|
212
|
+
result[key] = `{{ ${stateMatch[1]} }}`;
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return result;
|
|
219
|
+
}
|
|
220
|
+
function getPageMetadata(data, pathname, ssrContext, { isProduction = false, channel = "production" } = {}) {
|
|
221
|
+
const currentPage = data.pages?.[pathname];
|
|
222
|
+
const stateObject = isObject(currentPage?.state) && currentPage?.state;
|
|
223
|
+
const rawPageMeta = currentPage?.metadata || currentPage?.helmet || stateObject || {};
|
|
224
|
+
const pageMetadata = convertFunctionMetaToTemplates(rawPageMeta);
|
|
225
|
+
const pageExplicitKeys = new Set(Object.keys(pageMetadata));
|
|
226
|
+
const appMetadata = data.app?.metadata || {};
|
|
227
|
+
const cleanAppMeta = convertFunctionMetaToTemplates(appMetadata);
|
|
228
|
+
let merged = {};
|
|
229
|
+
if (data.integrations?.seo) {
|
|
230
|
+
merged = { ...data.integrations.seo, ...cleanAppMeta, ...pageMetadata };
|
|
231
|
+
} else if (Object.keys(cleanAppMeta).length) {
|
|
232
|
+
merged = { ...cleanAppMeta, ...pageMetadata };
|
|
233
|
+
} else {
|
|
234
|
+
merged = { ...pageMetadata };
|
|
235
|
+
}
|
|
236
|
+
if (!merged.title) {
|
|
237
|
+
const label = data.name || data.key;
|
|
238
|
+
if (label) {
|
|
239
|
+
if (isProduction) merged.title = label;
|
|
240
|
+
else if (channel === "development") merged.title = `${label} / dev:symbo.ls`;
|
|
241
|
+
else if (channel === "staging") merged.title = `${label} / staging:symbo.ls`;
|
|
242
|
+
else merged.title = `${label} / symbo.ls`;
|
|
243
|
+
} else {
|
|
244
|
+
merged.title = "Symbols demo";
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
merged = resolveMetadataTemplates(merged, data, ssrContext);
|
|
248
|
+
if (merged.title && (pageExplicitKeys.has("title") || !merged["og:title"])) {
|
|
249
|
+
if (!pageExplicitKeys.has("og:title")) {
|
|
250
|
+
merged["og:title"] = merged.title;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
if (merged.description && (pageExplicitKeys.has("description") || !merged["og:description"])) {
|
|
254
|
+
if (!pageExplicitKeys.has("og:description")) {
|
|
255
|
+
merged["og:description"] = merged.description;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
if (merged.title && !merged["twitter:title"]) {
|
|
259
|
+
merged["twitter:title"] = merged.title;
|
|
260
|
+
}
|
|
261
|
+
if (merged.description && !merged["twitter:description"]) {
|
|
262
|
+
merged["twitter:description"] = merged.description;
|
|
263
|
+
}
|
|
264
|
+
const routeForUrl = ssrContext?.actualPathname || pathname;
|
|
265
|
+
if (merged["og:url"] || merged.url) {
|
|
266
|
+
const baseUrl = (merged["og:url"] || merged.url || "").replace(/\/$/, "");
|
|
267
|
+
if (baseUrl && routeForUrl && routeForUrl !== "/") {
|
|
268
|
+
const cleanRoute = routeForUrl.startsWith("/") ? routeForUrl : "/" + routeForUrl;
|
|
269
|
+
merged["og:url"] = baseUrl + cleanRoute;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
merged = resolveFileReferences(merged, data.files);
|
|
273
|
+
const siteName = data.name || data.app?.metadata?.title || data.app?.name || "";
|
|
274
|
+
for (const [key, value] of Object.entries(merged)) {
|
|
275
|
+
if (typeof value === "string" && value.includes("{{")) {
|
|
276
|
+
const cleaned = value.replace(/\{\{[^}]*\}\}/g, "").trim();
|
|
277
|
+
if (!cleaned) {
|
|
278
|
+
if (key === "title") merged[key] = siteName;
|
|
279
|
+
else delete merged[key];
|
|
280
|
+
} else {
|
|
281
|
+
merged[key] = cleaned;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return merged;
|
|
286
|
+
}
|
|
287
|
+
export {
|
|
288
|
+
generateMetaTags,
|
|
289
|
+
getPageMetadata
|
|
290
|
+
};
|