@symbo.ls/smbls-utils 3.5.0 → 3.6.1
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/cjs/cdn.js +85 -0
- package/dist/cjs/index.js +2 -0
- package/dist/cjs/metadata.js +196 -0
- package/dist/esm/cdn.js +65 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/metadata.js +176 -0
- package/dist/iife/index.js +240 -0
- package/package.json +7 -7
- package/src/cdn.js +83 -0
- package/src/index.js +2 -0
- package/src/metadata.js +225 -0
package/dist/cjs/cdn.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var cdn_exports = {};
|
|
20
|
+
__export(cdn_exports, {
|
|
21
|
+
CDN_PROVIDERS: () => CDN_PROVIDERS,
|
|
22
|
+
PACKAGE_MANAGER_TO_CDN: () => PACKAGE_MANAGER_TO_CDN,
|
|
23
|
+
getCDNUrl: () => getCDNUrl,
|
|
24
|
+
getCdnProviderFromConfig: () => getCdnProviderFromConfig,
|
|
25
|
+
getImportMapScript: () => getImportMapScript
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(cdn_exports);
|
|
28
|
+
function onlyDotsAndNumbers(str) {
|
|
29
|
+
return /^[0-9.]+$/.test(str) && str !== "";
|
|
30
|
+
}
|
|
31
|
+
const CDN_PROVIDERS = {
|
|
32
|
+
skypack: {
|
|
33
|
+
url: "https://cdn.skypack.dev",
|
|
34
|
+
formatUrl: (pkg, version) => `${CDN_PROVIDERS.skypack.url}/${pkg}${version !== "latest" ? `@${version}` : ""}`
|
|
35
|
+
},
|
|
36
|
+
esmsh: {
|
|
37
|
+
url: "https://esm.sh",
|
|
38
|
+
formatUrl: (pkg, version) => `${CDN_PROVIDERS.esmsh.url}/${pkg}${version !== "latest" ? `@${version}` : ""}`
|
|
39
|
+
},
|
|
40
|
+
unpkg: {
|
|
41
|
+
url: "https://unpkg.com",
|
|
42
|
+
formatUrl: (pkg, version) => `${CDN_PROVIDERS.unpkg.url}/${pkg}${version !== "latest" ? `@${version}` : ""}?module`
|
|
43
|
+
},
|
|
44
|
+
jsdelivr: {
|
|
45
|
+
url: "https://cdn.jsdelivr.net/npm",
|
|
46
|
+
formatUrl: (pkg, version) => `${CDN_PROVIDERS.jsdelivr.url}/${pkg}${version !== "latest" ? `@${version}` : ""}/+esm`
|
|
47
|
+
},
|
|
48
|
+
symbols: {
|
|
49
|
+
url: "https://pkg.symbo.ls",
|
|
50
|
+
formatUrl: (pkg, version) => {
|
|
51
|
+
if (pkg.split("/").length > 2 || !onlyDotsAndNumbers(version)) {
|
|
52
|
+
return `${CDN_PROVIDERS.symbols.url}/${pkg}`;
|
|
53
|
+
}
|
|
54
|
+
return `${CDN_PROVIDERS.symbols.url}/${pkg}/${version}.js`;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
const PACKAGE_MANAGER_TO_CDN = {
|
|
59
|
+
"esm.sh": "esmsh",
|
|
60
|
+
"unpkg": "unpkg",
|
|
61
|
+
"skypack": "skypack",
|
|
62
|
+
"jsdelivr": "jsdelivr",
|
|
63
|
+
"pkg.symbo.ls": "symbols"
|
|
64
|
+
};
|
|
65
|
+
const getCdnProviderFromConfig = (symbolsConfig = {}) => {
|
|
66
|
+
const { packageManager } = symbolsConfig;
|
|
67
|
+
return PACKAGE_MANAGER_TO_CDN[packageManager] || null;
|
|
68
|
+
};
|
|
69
|
+
const getCDNUrl = (packageName, version = "latest", provider = "esmsh") => {
|
|
70
|
+
const cdnConfig = CDN_PROVIDERS[provider] || CDN_PROVIDERS.esmsh;
|
|
71
|
+
return cdnConfig.formatUrl(packageName, version);
|
|
72
|
+
};
|
|
73
|
+
const getImportMapScript = (data, defaultProvider = "skypack") => {
|
|
74
|
+
const dependencies = data.dependencies || {};
|
|
75
|
+
const keys = Object.keys(dependencies);
|
|
76
|
+
if (!keys.length) return "";
|
|
77
|
+
const imports = {};
|
|
78
|
+
for (const pkgName of keys) {
|
|
79
|
+
const version = dependencies[pkgName] || "latest";
|
|
80
|
+
imports[pkgName] = getCDNUrl(pkgName, version, defaultProvider);
|
|
81
|
+
}
|
|
82
|
+
return `<script type="importmap">{
|
|
83
|
+
"imports": ${JSON.stringify(imports, null, 2)}
|
|
84
|
+
}<\/script>`;
|
|
85
|
+
};
|
package/dist/cjs/index.js
CHANGED
|
@@ -36,6 +36,8 @@ __reExport(index_exports, require("./date.js"), module.exports);
|
|
|
36
36
|
__reExport(index_exports, require("./fibonacci.js"), module.exports);
|
|
37
37
|
__reExport(index_exports, require("./load.js"), module.exports);
|
|
38
38
|
__reExport(index_exports, require("./files.js"), module.exports);
|
|
39
|
+
__reExport(index_exports, require("./cdn.js"), module.exports);
|
|
40
|
+
__reExport(index_exports, require("./metadata.js"), module.exports);
|
|
39
41
|
const copyStringToClipboard = async (str) => {
|
|
40
42
|
try {
|
|
41
43
|
await navigator.clipboard.writeText(str);
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var metadata_exports = {};
|
|
20
|
+
__export(metadata_exports, {
|
|
21
|
+
generateMetaTags: () => generateMetaTags,
|
|
22
|
+
getPageMetadata: () => getPageMetadata
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(metadata_exports);
|
|
25
|
+
var import_utils = require("@domql/utils");
|
|
26
|
+
const escapeHtml = (text) => {
|
|
27
|
+
if (text === null || text === void 0) return "";
|
|
28
|
+
const map = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" };
|
|
29
|
+
return text.toString().replace(/[&<>"']/g, (m) => map[m]);
|
|
30
|
+
};
|
|
31
|
+
const buildAttrs = (obj) => Object.entries(obj || {}).filter(([_, v]) => v !== void 0).map(([k, v]) => `${k}="${escapeHtml(v)}"`).join(" ");
|
|
32
|
+
const generateMetaTags = (metadata, isProduction) => {
|
|
33
|
+
if (!isProduction) {
|
|
34
|
+
const faviconTag = (() => {
|
|
35
|
+
const fv = metadata?.favicon || metadata?.favicons;
|
|
36
|
+
if (!fv) return '<link rel="icon" href="/favicon.ico">';
|
|
37
|
+
if (typeof fv === "string") return `<link rel="icon" href="${escapeHtml(fv)}">`;
|
|
38
|
+
if (Array.isArray(fv)) {
|
|
39
|
+
return fv.map(
|
|
40
|
+
(item) => typeof item === "string" ? `<link rel="icon" href="${escapeHtml(item)}">` : `<link ${buildAttrs({ rel: "icon", ...item })}>`
|
|
41
|
+
).join("\n");
|
|
42
|
+
}
|
|
43
|
+
return `<link ${buildAttrs({ rel: "icon", ...fv })}>`;
|
|
44
|
+
})();
|
|
45
|
+
return [
|
|
46
|
+
'<meta charset="UTF-8">',
|
|
47
|
+
`<title>${escapeHtml(metadata.title || "Test")}</title>`,
|
|
48
|
+
'<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no">',
|
|
49
|
+
'<meta name="robots" content="noindex">',
|
|
50
|
+
'<meta name="apple-mobile-web-app-capable" content="yes">',
|
|
51
|
+
faviconTag
|
|
52
|
+
].join("\n");
|
|
53
|
+
}
|
|
54
|
+
const tags = Object.entries(metadata).reduce(
|
|
55
|
+
(acc, [key, value]) => {
|
|
56
|
+
if (!value && value !== 0 && value !== false) return acc;
|
|
57
|
+
if (key === "title") {
|
|
58
|
+
acc.push(`<title>${escapeHtml(value)}</title>`);
|
|
59
|
+
return acc;
|
|
60
|
+
}
|
|
61
|
+
if (key === "canonical") {
|
|
62
|
+
acc.push(`<link rel="canonical" href="${escapeHtml(value)}">`);
|
|
63
|
+
return acc;
|
|
64
|
+
}
|
|
65
|
+
if (key === "manifest") {
|
|
66
|
+
acc.push(`<link rel="manifest" href="${escapeHtml(value)}">`);
|
|
67
|
+
return acc;
|
|
68
|
+
}
|
|
69
|
+
if (key === "favicon" || key === "favicons" || key === "icon" || key === "icons") {
|
|
70
|
+
const items = Array.isArray(value) ? value : [value];
|
|
71
|
+
items.forEach((item) => {
|
|
72
|
+
if (typeof item === "string") {
|
|
73
|
+
acc.push(`<link rel="icon" href="${escapeHtml(item)}">`);
|
|
74
|
+
} else if (typeof item === "object") {
|
|
75
|
+
const attrs = buildAttrs(item);
|
|
76
|
+
if (!/rel=/.test(attrs)) {
|
|
77
|
+
acc.push(`<link rel="icon" ${attrs}>`);
|
|
78
|
+
} else {
|
|
79
|
+
acc.push(`<link ${attrs}>`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
return acc;
|
|
84
|
+
}
|
|
85
|
+
if (key === "alternate") {
|
|
86
|
+
const alternates = Array.isArray(value) ? value : [value];
|
|
87
|
+
alternates.forEach((alt) => {
|
|
88
|
+
if (typeof alt === "object") {
|
|
89
|
+
const attrs = Object.entries(alt).filter(([_, attrValue]) => attrValue !== void 0).map(([attrKey, attrValue]) => `${attrKey}="${escapeHtml(attrValue)}"`).join(" ");
|
|
90
|
+
acc.push(`<link rel="alternate" ${attrs}>`);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
return acc;
|
|
94
|
+
}
|
|
95
|
+
const processMetaTag = (tagKey, tagValue, attrType = "name") => {
|
|
96
|
+
if (typeof tagValue === "string" || typeof tagValue === "number" || typeof tagValue === "boolean") {
|
|
97
|
+
acc.push(`<meta ${attrType}="${escapeHtml(tagKey)}" content="${escapeHtml(tagValue)}">`);
|
|
98
|
+
} else if (Array.isArray(tagValue)) {
|
|
99
|
+
tagValue.forEach((item) => {
|
|
100
|
+
if (typeof item === "string" || typeof item === "number" || typeof item === "boolean") {
|
|
101
|
+
acc.push(`<meta ${attrType}="${escapeHtml(tagKey)}" content="${escapeHtml(item)}">`);
|
|
102
|
+
} else if (typeof item === "object") {
|
|
103
|
+
const attrs = Object.entries(item).filter(([_, attrValue]) => attrValue !== void 0).map(([attrKey, attrValue]) => `${attrKey}="${escapeHtml(attrValue)}"`).join(" ");
|
|
104
|
+
acc.push(`<meta ${attrs}>`);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
} else if (typeof tagValue === "object") {
|
|
108
|
+
const attrs = Object.entries(tagValue).filter(([_, attrValue]) => attrValue !== void 0).map(([attrKey, attrValue]) => `${attrKey}="${escapeHtml(attrValue)}"`).join(" ");
|
|
109
|
+
acc.push(`<meta ${attrs}>`);
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
if (key.startsWith("http-equiv:")) {
|
|
113
|
+
const httpEquivKey = key.replace("http-equiv:", "");
|
|
114
|
+
processMetaTag(httpEquivKey, value, "http-equiv");
|
|
115
|
+
return acc;
|
|
116
|
+
}
|
|
117
|
+
if (key.startsWith("itemprop:")) {
|
|
118
|
+
const itempropKey = key.replace("itemprop:", "");
|
|
119
|
+
processMetaTag(itempropKey, value, "itemprop");
|
|
120
|
+
return acc;
|
|
121
|
+
}
|
|
122
|
+
const prefixes = [
|
|
123
|
+
"og:",
|
|
124
|
+
"twitter:",
|
|
125
|
+
"fb:",
|
|
126
|
+
"article:",
|
|
127
|
+
"profile:",
|
|
128
|
+
"book:",
|
|
129
|
+
"business:",
|
|
130
|
+
"music:",
|
|
131
|
+
"product:",
|
|
132
|
+
"video:",
|
|
133
|
+
"DC:",
|
|
134
|
+
"DCTERMS:"
|
|
135
|
+
];
|
|
136
|
+
const prefix = prefixes.find((p) => key.startsWith(p));
|
|
137
|
+
if (prefix) {
|
|
138
|
+
const tagKey = key.replace(prefix, "");
|
|
139
|
+
processMetaTag(
|
|
140
|
+
`${prefix.replace(":", "")}:${tagKey}`,
|
|
141
|
+
value,
|
|
142
|
+
prefix === "twitter:" || prefix === "DC:" || prefix === "DCTERMS:" ? "name" : "property"
|
|
143
|
+
);
|
|
144
|
+
return acc;
|
|
145
|
+
}
|
|
146
|
+
if (key.startsWith("apple:") || key.startsWith("msapplication:")) {
|
|
147
|
+
const pfx = key.startsWith("apple:") ? "apple-" : "msapplication-";
|
|
148
|
+
const tagKey = key.replace(/^apple:|^msapplication:/, "");
|
|
149
|
+
processMetaTag(`${pfx}${tagKey}`, value, "name");
|
|
150
|
+
return acc;
|
|
151
|
+
}
|
|
152
|
+
processMetaTag(key, value);
|
|
153
|
+
return acc;
|
|
154
|
+
},
|
|
155
|
+
[
|
|
156
|
+
'<meta charset="UTF-8">',
|
|
157
|
+
'<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no">'
|
|
158
|
+
]
|
|
159
|
+
);
|
|
160
|
+
return tags.join("\n");
|
|
161
|
+
};
|
|
162
|
+
const isBareFilename = (val) => typeof val === "string" && val.length > 0 && !val.startsWith("/") && !val.startsWith("http");
|
|
163
|
+
function resolveFileReferences(metadata, files) {
|
|
164
|
+
if (!files || typeof files !== "object") return metadata;
|
|
165
|
+
const resolve = (val) => {
|
|
166
|
+
if (!isBareFilename(val)) return val;
|
|
167
|
+
const fileEntry = files[val];
|
|
168
|
+
if (fileEntry?.src) return fileEntry.src;
|
|
169
|
+
return val;
|
|
170
|
+
};
|
|
171
|
+
const result = { ...metadata };
|
|
172
|
+
for (const [key, value] of Object.entries(result)) {
|
|
173
|
+
if (typeof value === "string") {
|
|
174
|
+
result[key] = resolve(value);
|
|
175
|
+
} else if (Array.isArray(value)) {
|
|
176
|
+
result[key] = value.map(
|
|
177
|
+
(item) => typeof item === "string" ? resolve(item) : item
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return result;
|
|
182
|
+
}
|
|
183
|
+
function getPageMetadata(data, pathname) {
|
|
184
|
+
const currentPage = data.pages?.[pathname];
|
|
185
|
+
const stateObject = (0, import_utils.isObject)(currentPage?.state) && currentPage?.state;
|
|
186
|
+
let pageMetadata = currentPage?.metadata || currentPage?.helmet || stateObject || {};
|
|
187
|
+
const appMetadata = data.app?.metadata || {};
|
|
188
|
+
if (data.integrations?.seo) {
|
|
189
|
+
pageMetadata = { ...data.integrations.seo, ...appMetadata, ...pageMetadata };
|
|
190
|
+
} else if (Object.keys(appMetadata).length) {
|
|
191
|
+
pageMetadata = { ...appMetadata, ...pageMetadata };
|
|
192
|
+
}
|
|
193
|
+
if (!pageMetadata.title) pageMetadata.title = data.name + " / symbo.ls" || "Symbols demo";
|
|
194
|
+
pageMetadata = resolveFileReferences(pageMetadata, data.files);
|
|
195
|
+
return pageMetadata;
|
|
196
|
+
}
|
package/dist/esm/cdn.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
function onlyDotsAndNumbers(str) {
|
|
2
|
+
return /^[0-9.]+$/.test(str) && str !== "";
|
|
3
|
+
}
|
|
4
|
+
const CDN_PROVIDERS = {
|
|
5
|
+
skypack: {
|
|
6
|
+
url: "https://cdn.skypack.dev",
|
|
7
|
+
formatUrl: (pkg, version) => `${CDN_PROVIDERS.skypack.url}/${pkg}${version !== "latest" ? `@${version}` : ""}`
|
|
8
|
+
},
|
|
9
|
+
esmsh: {
|
|
10
|
+
url: "https://esm.sh",
|
|
11
|
+
formatUrl: (pkg, version) => `${CDN_PROVIDERS.esmsh.url}/${pkg}${version !== "latest" ? `@${version}` : ""}`
|
|
12
|
+
},
|
|
13
|
+
unpkg: {
|
|
14
|
+
url: "https://unpkg.com",
|
|
15
|
+
formatUrl: (pkg, version) => `${CDN_PROVIDERS.unpkg.url}/${pkg}${version !== "latest" ? `@${version}` : ""}?module`
|
|
16
|
+
},
|
|
17
|
+
jsdelivr: {
|
|
18
|
+
url: "https://cdn.jsdelivr.net/npm",
|
|
19
|
+
formatUrl: (pkg, version) => `${CDN_PROVIDERS.jsdelivr.url}/${pkg}${version !== "latest" ? `@${version}` : ""}/+esm`
|
|
20
|
+
},
|
|
21
|
+
symbols: {
|
|
22
|
+
url: "https://pkg.symbo.ls",
|
|
23
|
+
formatUrl: (pkg, version) => {
|
|
24
|
+
if (pkg.split("/").length > 2 || !onlyDotsAndNumbers(version)) {
|
|
25
|
+
return `${CDN_PROVIDERS.symbols.url}/${pkg}`;
|
|
26
|
+
}
|
|
27
|
+
return `${CDN_PROVIDERS.symbols.url}/${pkg}/${version}.js`;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
const PACKAGE_MANAGER_TO_CDN = {
|
|
32
|
+
"esm.sh": "esmsh",
|
|
33
|
+
"unpkg": "unpkg",
|
|
34
|
+
"skypack": "skypack",
|
|
35
|
+
"jsdelivr": "jsdelivr",
|
|
36
|
+
"pkg.symbo.ls": "symbols"
|
|
37
|
+
};
|
|
38
|
+
const getCdnProviderFromConfig = (symbolsConfig = {}) => {
|
|
39
|
+
const { packageManager } = symbolsConfig;
|
|
40
|
+
return PACKAGE_MANAGER_TO_CDN[packageManager] || null;
|
|
41
|
+
};
|
|
42
|
+
const getCDNUrl = (packageName, version = "latest", provider = "esmsh") => {
|
|
43
|
+
const cdnConfig = CDN_PROVIDERS[provider] || CDN_PROVIDERS.esmsh;
|
|
44
|
+
return cdnConfig.formatUrl(packageName, version);
|
|
45
|
+
};
|
|
46
|
+
const getImportMapScript = (data, defaultProvider = "skypack") => {
|
|
47
|
+
const dependencies = data.dependencies || {};
|
|
48
|
+
const keys = Object.keys(dependencies);
|
|
49
|
+
if (!keys.length) return "";
|
|
50
|
+
const imports = {};
|
|
51
|
+
for (const pkgName of keys) {
|
|
52
|
+
const version = dependencies[pkgName] || "latest";
|
|
53
|
+
imports[pkgName] = getCDNUrl(pkgName, version, defaultProvider);
|
|
54
|
+
}
|
|
55
|
+
return `<script type="importmap">{
|
|
56
|
+
"imports": ${JSON.stringify(imports, null, 2)}
|
|
57
|
+
}<\/script>`;
|
|
58
|
+
};
|
|
59
|
+
export {
|
|
60
|
+
CDN_PROVIDERS,
|
|
61
|
+
PACKAGE_MANAGER_TO_CDN,
|
|
62
|
+
getCDNUrl,
|
|
63
|
+
getCdnProviderFromConfig,
|
|
64
|
+
getImportMapScript
|
|
65
|
+
};
|
package/dist/esm/index.js
CHANGED
|
@@ -5,6 +5,8 @@ export * from "./date.js";
|
|
|
5
5
|
export * from "./fibonacci.js";
|
|
6
6
|
export * from "./load.js";
|
|
7
7
|
export * from "./files.js";
|
|
8
|
+
export * from "./cdn.js";
|
|
9
|
+
export * from "./metadata.js";
|
|
8
10
|
const copyStringToClipboard = async (str) => {
|
|
9
11
|
try {
|
|
10
12
|
await navigator.clipboard.writeText(str);
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { isObject } from "@domql/utils";
|
|
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
|
+
const faviconTag = (() => {
|
|
11
|
+
const fv = metadata?.favicon || metadata?.favicons;
|
|
12
|
+
if (!fv) return '<link rel="icon" href="/favicon.ico">';
|
|
13
|
+
if (typeof fv === "string") return `<link rel="icon" href="${escapeHtml(fv)}">`;
|
|
14
|
+
if (Array.isArray(fv)) {
|
|
15
|
+
return fv.map(
|
|
16
|
+
(item) => typeof item === "string" ? `<link rel="icon" href="${escapeHtml(item)}">` : `<link ${buildAttrs({ rel: "icon", ...item })}>`
|
|
17
|
+
).join("\n");
|
|
18
|
+
}
|
|
19
|
+
return `<link ${buildAttrs({ rel: "icon", ...fv })}>`;
|
|
20
|
+
})();
|
|
21
|
+
return [
|
|
22
|
+
'<meta charset="UTF-8">',
|
|
23
|
+
`<title>${escapeHtml(metadata.title || "Test")}</title>`,
|
|
24
|
+
'<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no">',
|
|
25
|
+
'<meta name="robots" content="noindex">',
|
|
26
|
+
'<meta name="apple-mobile-web-app-capable" content="yes">',
|
|
27
|
+
faviconTag
|
|
28
|
+
].join("\n");
|
|
29
|
+
}
|
|
30
|
+
const tags = Object.entries(metadata).reduce(
|
|
31
|
+
(acc, [key, value]) => {
|
|
32
|
+
if (!value && value !== 0 && value !== false) return acc;
|
|
33
|
+
if (key === "title") {
|
|
34
|
+
acc.push(`<title>${escapeHtml(value)}</title>`);
|
|
35
|
+
return acc;
|
|
36
|
+
}
|
|
37
|
+
if (key === "canonical") {
|
|
38
|
+
acc.push(`<link rel="canonical" href="${escapeHtml(value)}">`);
|
|
39
|
+
return acc;
|
|
40
|
+
}
|
|
41
|
+
if (key === "manifest") {
|
|
42
|
+
acc.push(`<link rel="manifest" href="${escapeHtml(value)}">`);
|
|
43
|
+
return acc;
|
|
44
|
+
}
|
|
45
|
+
if (key === "favicon" || key === "favicons" || key === "icon" || key === "icons") {
|
|
46
|
+
const items = Array.isArray(value) ? value : [value];
|
|
47
|
+
items.forEach((item) => {
|
|
48
|
+
if (typeof item === "string") {
|
|
49
|
+
acc.push(`<link rel="icon" href="${escapeHtml(item)}">`);
|
|
50
|
+
} else if (typeof item === "object") {
|
|
51
|
+
const attrs = buildAttrs(item);
|
|
52
|
+
if (!/rel=/.test(attrs)) {
|
|
53
|
+
acc.push(`<link rel="icon" ${attrs}>`);
|
|
54
|
+
} else {
|
|
55
|
+
acc.push(`<link ${attrs}>`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
return acc;
|
|
60
|
+
}
|
|
61
|
+
if (key === "alternate") {
|
|
62
|
+
const alternates = Array.isArray(value) ? value : [value];
|
|
63
|
+
alternates.forEach((alt) => {
|
|
64
|
+
if (typeof alt === "object") {
|
|
65
|
+
const attrs = Object.entries(alt).filter(([_, attrValue]) => attrValue !== void 0).map(([attrKey, attrValue]) => `${attrKey}="${escapeHtml(attrValue)}"`).join(" ");
|
|
66
|
+
acc.push(`<link rel="alternate" ${attrs}>`);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
return acc;
|
|
70
|
+
}
|
|
71
|
+
const processMetaTag = (tagKey, tagValue, attrType = "name") => {
|
|
72
|
+
if (typeof tagValue === "string" || typeof tagValue === "number" || typeof tagValue === "boolean") {
|
|
73
|
+
acc.push(`<meta ${attrType}="${escapeHtml(tagKey)}" content="${escapeHtml(tagValue)}">`);
|
|
74
|
+
} else if (Array.isArray(tagValue)) {
|
|
75
|
+
tagValue.forEach((item) => {
|
|
76
|
+
if (typeof item === "string" || typeof item === "number" || typeof item === "boolean") {
|
|
77
|
+
acc.push(`<meta ${attrType}="${escapeHtml(tagKey)}" content="${escapeHtml(item)}">`);
|
|
78
|
+
} else if (typeof item === "object") {
|
|
79
|
+
const attrs = Object.entries(item).filter(([_, attrValue]) => attrValue !== void 0).map(([attrKey, attrValue]) => `${attrKey}="${escapeHtml(attrValue)}"`).join(" ");
|
|
80
|
+
acc.push(`<meta ${attrs}>`);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
} else if (typeof tagValue === "object") {
|
|
84
|
+
const attrs = Object.entries(tagValue).filter(([_, attrValue]) => attrValue !== void 0).map(([attrKey, attrValue]) => `${attrKey}="${escapeHtml(attrValue)}"`).join(" ");
|
|
85
|
+
acc.push(`<meta ${attrs}>`);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
if (key.startsWith("http-equiv:")) {
|
|
89
|
+
const httpEquivKey = key.replace("http-equiv:", "");
|
|
90
|
+
processMetaTag(httpEquivKey, value, "http-equiv");
|
|
91
|
+
return acc;
|
|
92
|
+
}
|
|
93
|
+
if (key.startsWith("itemprop:")) {
|
|
94
|
+
const itempropKey = key.replace("itemprop:", "");
|
|
95
|
+
processMetaTag(itempropKey, value, "itemprop");
|
|
96
|
+
return acc;
|
|
97
|
+
}
|
|
98
|
+
const prefixes = [
|
|
99
|
+
"og:",
|
|
100
|
+
"twitter:",
|
|
101
|
+
"fb:",
|
|
102
|
+
"article:",
|
|
103
|
+
"profile:",
|
|
104
|
+
"book:",
|
|
105
|
+
"business:",
|
|
106
|
+
"music:",
|
|
107
|
+
"product:",
|
|
108
|
+
"video:",
|
|
109
|
+
"DC:",
|
|
110
|
+
"DCTERMS:"
|
|
111
|
+
];
|
|
112
|
+
const prefix = prefixes.find((p) => key.startsWith(p));
|
|
113
|
+
if (prefix) {
|
|
114
|
+
const tagKey = key.replace(prefix, "");
|
|
115
|
+
processMetaTag(
|
|
116
|
+
`${prefix.replace(":", "")}:${tagKey}`,
|
|
117
|
+
value,
|
|
118
|
+
prefix === "twitter:" || prefix === "DC:" || prefix === "DCTERMS:" ? "name" : "property"
|
|
119
|
+
);
|
|
120
|
+
return acc;
|
|
121
|
+
}
|
|
122
|
+
if (key.startsWith("apple:") || key.startsWith("msapplication:")) {
|
|
123
|
+
const pfx = key.startsWith("apple:") ? "apple-" : "msapplication-";
|
|
124
|
+
const tagKey = key.replace(/^apple:|^msapplication:/, "");
|
|
125
|
+
processMetaTag(`${pfx}${tagKey}`, value, "name");
|
|
126
|
+
return acc;
|
|
127
|
+
}
|
|
128
|
+
processMetaTag(key, value);
|
|
129
|
+
return acc;
|
|
130
|
+
},
|
|
131
|
+
[
|
|
132
|
+
'<meta charset="UTF-8">',
|
|
133
|
+
'<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no">'
|
|
134
|
+
]
|
|
135
|
+
);
|
|
136
|
+
return tags.join("\n");
|
|
137
|
+
};
|
|
138
|
+
const isBareFilename = (val) => typeof val === "string" && val.length > 0 && !val.startsWith("/") && !val.startsWith("http");
|
|
139
|
+
function resolveFileReferences(metadata, files) {
|
|
140
|
+
if (!files || typeof files !== "object") return metadata;
|
|
141
|
+
const resolve = (val) => {
|
|
142
|
+
if (!isBareFilename(val)) return val;
|
|
143
|
+
const fileEntry = files[val];
|
|
144
|
+
if (fileEntry?.src) return fileEntry.src;
|
|
145
|
+
return val;
|
|
146
|
+
};
|
|
147
|
+
const result = { ...metadata };
|
|
148
|
+
for (const [key, value] of Object.entries(result)) {
|
|
149
|
+
if (typeof value === "string") {
|
|
150
|
+
result[key] = resolve(value);
|
|
151
|
+
} else if (Array.isArray(value)) {
|
|
152
|
+
result[key] = value.map(
|
|
153
|
+
(item) => typeof item === "string" ? resolve(item) : item
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return result;
|
|
158
|
+
}
|
|
159
|
+
function getPageMetadata(data, pathname) {
|
|
160
|
+
const currentPage = data.pages?.[pathname];
|
|
161
|
+
const stateObject = isObject(currentPage?.state) && currentPage?.state;
|
|
162
|
+
let pageMetadata = currentPage?.metadata || currentPage?.helmet || stateObject || {};
|
|
163
|
+
const appMetadata = data.app?.metadata || {};
|
|
164
|
+
if (data.integrations?.seo) {
|
|
165
|
+
pageMetadata = { ...data.integrations.seo, ...appMetadata, ...pageMetadata };
|
|
166
|
+
} else if (Object.keys(appMetadata).length) {
|
|
167
|
+
pageMetadata = { ...appMetadata, ...pageMetadata };
|
|
168
|
+
}
|
|
169
|
+
if (!pageMetadata.title) pageMetadata.title = data.name + " / symbo.ls" || "Symbols demo";
|
|
170
|
+
pageMetadata = resolveFileReferences(pageMetadata, data.files);
|
|
171
|
+
return pageMetadata;
|
|
172
|
+
}
|
|
173
|
+
export {
|
|
174
|
+
generateMetaTags,
|
|
175
|
+
getPageMetadata
|
|
176
|
+
};
|
package/dist/iife/index.js
CHANGED
|
@@ -21,6 +21,8 @@ var SmblsSmblsUtils = (() => {
|
|
|
21
21
|
// src/index.js
|
|
22
22
|
var index_exports = {};
|
|
23
23
|
__export(index_exports, {
|
|
24
|
+
CDN_PROVIDERS: () => CDN_PROVIDERS,
|
|
25
|
+
PACKAGE_MANAGER_TO_CDN: () => PACKAGE_MANAGER_TO_CDN,
|
|
24
26
|
arrayzeValue: () => arrayzeValue,
|
|
25
27
|
copyJavaScriptToClipboard: () => copyJavaScriptToClipboard,
|
|
26
28
|
copyStringToClipboard: () => copyStringToClipboard,
|
|
@@ -28,6 +30,11 @@ var SmblsSmblsUtils = (() => {
|
|
|
28
30
|
findClosestNumber: () => findClosestNumber,
|
|
29
31
|
findClosestNumberInFactory: () => findClosestNumberInFactory,
|
|
30
32
|
formatDate: () => formatDate,
|
|
33
|
+
generateMetaTags: () => generateMetaTags,
|
|
34
|
+
getCDNUrl: () => getCDNUrl,
|
|
35
|
+
getCdnProviderFromConfig: () => getCdnProviderFromConfig,
|
|
36
|
+
getImportMapScript: () => getImportMapScript,
|
|
37
|
+
getPageMetadata: () => getPageMetadata,
|
|
31
38
|
isPhoto: () => isPhoto,
|
|
32
39
|
loadCssFile: () => loadCssFile,
|
|
33
40
|
loadJavascript: () => loadJavascript,
|
|
@@ -244,6 +251,239 @@ var SmblsSmblsUtils = (() => {
|
|
|
244
251
|
// src/files.js
|
|
245
252
|
var isPhoto = (format) => ["jpeg", "gif", "jpg", "png", "tiff", "woff"].includes(format);
|
|
246
253
|
|
|
254
|
+
// src/cdn.js
|
|
255
|
+
function onlyDotsAndNumbers(str) {
|
|
256
|
+
return /^[0-9.]+$/.test(str) && str !== "";
|
|
257
|
+
}
|
|
258
|
+
var CDN_PROVIDERS = {
|
|
259
|
+
skypack: {
|
|
260
|
+
url: "https://cdn.skypack.dev",
|
|
261
|
+
formatUrl: (pkg, version) => `${CDN_PROVIDERS.skypack.url}/${pkg}${version !== "latest" ? `@${version}` : ""}`
|
|
262
|
+
},
|
|
263
|
+
esmsh: {
|
|
264
|
+
url: "https://esm.sh",
|
|
265
|
+
formatUrl: (pkg, version) => `${CDN_PROVIDERS.esmsh.url}/${pkg}${version !== "latest" ? `@${version}` : ""}`
|
|
266
|
+
},
|
|
267
|
+
unpkg: {
|
|
268
|
+
url: "https://unpkg.com",
|
|
269
|
+
formatUrl: (pkg, version) => `${CDN_PROVIDERS.unpkg.url}/${pkg}${version !== "latest" ? `@${version}` : ""}?module`
|
|
270
|
+
},
|
|
271
|
+
jsdelivr: {
|
|
272
|
+
url: "https://cdn.jsdelivr.net/npm",
|
|
273
|
+
formatUrl: (pkg, version) => `${CDN_PROVIDERS.jsdelivr.url}/${pkg}${version !== "latest" ? `@${version}` : ""}/+esm`
|
|
274
|
+
},
|
|
275
|
+
symbols: {
|
|
276
|
+
url: "https://pkg.symbo.ls",
|
|
277
|
+
formatUrl: (pkg, version) => {
|
|
278
|
+
if (pkg.split("/").length > 2 || !onlyDotsAndNumbers(version)) {
|
|
279
|
+
return `${CDN_PROVIDERS.symbols.url}/${pkg}`;
|
|
280
|
+
}
|
|
281
|
+
return `${CDN_PROVIDERS.symbols.url}/${pkg}/${version}.js`;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
var PACKAGE_MANAGER_TO_CDN = {
|
|
286
|
+
"esm.sh": "esmsh",
|
|
287
|
+
"unpkg": "unpkg",
|
|
288
|
+
"skypack": "skypack",
|
|
289
|
+
"jsdelivr": "jsdelivr",
|
|
290
|
+
"pkg.symbo.ls": "symbols"
|
|
291
|
+
};
|
|
292
|
+
var getCdnProviderFromConfig = (symbolsConfig = {}) => {
|
|
293
|
+
const { packageManager } = symbolsConfig;
|
|
294
|
+
return PACKAGE_MANAGER_TO_CDN[packageManager] || null;
|
|
295
|
+
};
|
|
296
|
+
var getCDNUrl = (packageName, version = "latest", provider = "esmsh") => {
|
|
297
|
+
const cdnConfig = CDN_PROVIDERS[provider] || CDN_PROVIDERS.esmsh;
|
|
298
|
+
return cdnConfig.formatUrl(packageName, version);
|
|
299
|
+
};
|
|
300
|
+
var getImportMapScript = (data, defaultProvider = "skypack") => {
|
|
301
|
+
const dependencies = data.dependencies || {};
|
|
302
|
+
const keys = Object.keys(dependencies);
|
|
303
|
+
if (!keys.length) return "";
|
|
304
|
+
const imports = {};
|
|
305
|
+
for (const pkgName of keys) {
|
|
306
|
+
const version = dependencies[pkgName] || "latest";
|
|
307
|
+
imports[pkgName] = getCDNUrl(pkgName, version, defaultProvider);
|
|
308
|
+
}
|
|
309
|
+
return `<script type="importmap">{
|
|
310
|
+
"imports": ${JSON.stringify(imports, null, 2)}
|
|
311
|
+
}<\/script>`;
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
// src/metadata.js
|
|
315
|
+
var escapeHtml = (text) => {
|
|
316
|
+
if (text === null || text === void 0) return "";
|
|
317
|
+
const map = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" };
|
|
318
|
+
return text.toString().replace(/[&<>"']/g, (m) => map[m]);
|
|
319
|
+
};
|
|
320
|
+
var buildAttrs = (obj) => Object.entries(obj || {}).filter(([_, v]) => v !== void 0).map(([k, v]) => `${k}="${escapeHtml(v)}"`).join(" ");
|
|
321
|
+
var generateMetaTags = (metadata, isProduction) => {
|
|
322
|
+
if (!isProduction) {
|
|
323
|
+
const faviconTag = (() => {
|
|
324
|
+
const fv = metadata?.favicon || metadata?.favicons;
|
|
325
|
+
if (!fv) return '<link rel="icon" href="/favicon.ico">';
|
|
326
|
+
if (typeof fv === "string") return `<link rel="icon" href="${escapeHtml(fv)}">`;
|
|
327
|
+
if (Array.isArray(fv)) {
|
|
328
|
+
return fv.map(
|
|
329
|
+
(item) => typeof item === "string" ? `<link rel="icon" href="${escapeHtml(item)}">` : `<link ${buildAttrs({ rel: "icon", ...item })}>`
|
|
330
|
+
).join("\n");
|
|
331
|
+
}
|
|
332
|
+
return `<link ${buildAttrs({ rel: "icon", ...fv })}>`;
|
|
333
|
+
})();
|
|
334
|
+
return [
|
|
335
|
+
'<meta charset="UTF-8">',
|
|
336
|
+
`<title>${escapeHtml(metadata.title || "Test")}</title>`,
|
|
337
|
+
'<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no">',
|
|
338
|
+
'<meta name="robots" content="noindex">',
|
|
339
|
+
'<meta name="apple-mobile-web-app-capable" content="yes">',
|
|
340
|
+
faviconTag
|
|
341
|
+
].join("\n");
|
|
342
|
+
}
|
|
343
|
+
const tags = Object.entries(metadata).reduce(
|
|
344
|
+
(acc, [key, value]) => {
|
|
345
|
+
if (!value && value !== 0 && value !== false) return acc;
|
|
346
|
+
if (key === "title") {
|
|
347
|
+
acc.push(`<title>${escapeHtml(value)}</title>`);
|
|
348
|
+
return acc;
|
|
349
|
+
}
|
|
350
|
+
if (key === "canonical") {
|
|
351
|
+
acc.push(`<link rel="canonical" href="${escapeHtml(value)}">`);
|
|
352
|
+
return acc;
|
|
353
|
+
}
|
|
354
|
+
if (key === "manifest") {
|
|
355
|
+
acc.push(`<link rel="manifest" href="${escapeHtml(value)}">`);
|
|
356
|
+
return acc;
|
|
357
|
+
}
|
|
358
|
+
if (key === "favicon" || key === "favicons" || key === "icon" || key === "icons") {
|
|
359
|
+
const items = Array.isArray(value) ? value : [value];
|
|
360
|
+
items.forEach((item) => {
|
|
361
|
+
if (typeof item === "string") {
|
|
362
|
+
acc.push(`<link rel="icon" href="${escapeHtml(item)}">`);
|
|
363
|
+
} else if (typeof item === "object") {
|
|
364
|
+
const attrs = buildAttrs(item);
|
|
365
|
+
if (!/rel=/.test(attrs)) {
|
|
366
|
+
acc.push(`<link rel="icon" ${attrs}>`);
|
|
367
|
+
} else {
|
|
368
|
+
acc.push(`<link ${attrs}>`);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
return acc;
|
|
373
|
+
}
|
|
374
|
+
if (key === "alternate") {
|
|
375
|
+
const alternates = Array.isArray(value) ? value : [value];
|
|
376
|
+
alternates.forEach((alt) => {
|
|
377
|
+
if (typeof alt === "object") {
|
|
378
|
+
const attrs = Object.entries(alt).filter(([_, attrValue]) => attrValue !== void 0).map(([attrKey, attrValue]) => `${attrKey}="${escapeHtml(attrValue)}"`).join(" ");
|
|
379
|
+
acc.push(`<link rel="alternate" ${attrs}>`);
|
|
380
|
+
}
|
|
381
|
+
});
|
|
382
|
+
return acc;
|
|
383
|
+
}
|
|
384
|
+
const processMetaTag = (tagKey, tagValue, attrType = "name") => {
|
|
385
|
+
if (typeof tagValue === "string" || typeof tagValue === "number" || typeof tagValue === "boolean") {
|
|
386
|
+
acc.push(`<meta ${attrType}="${escapeHtml(tagKey)}" content="${escapeHtml(tagValue)}">`);
|
|
387
|
+
} else if (Array.isArray(tagValue)) {
|
|
388
|
+
tagValue.forEach((item) => {
|
|
389
|
+
if (typeof item === "string" || typeof item === "number" || typeof item === "boolean") {
|
|
390
|
+
acc.push(`<meta ${attrType}="${escapeHtml(tagKey)}" content="${escapeHtml(item)}">`);
|
|
391
|
+
} else if (typeof item === "object") {
|
|
392
|
+
const attrs = Object.entries(item).filter(([_, attrValue]) => attrValue !== void 0).map(([attrKey, attrValue]) => `${attrKey}="${escapeHtml(attrValue)}"`).join(" ");
|
|
393
|
+
acc.push(`<meta ${attrs}>`);
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
} else if (typeof tagValue === "object") {
|
|
397
|
+
const attrs = Object.entries(tagValue).filter(([_, attrValue]) => attrValue !== void 0).map(([attrKey, attrValue]) => `${attrKey}="${escapeHtml(attrValue)}"`).join(" ");
|
|
398
|
+
acc.push(`<meta ${attrs}>`);
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
if (key.startsWith("http-equiv:")) {
|
|
402
|
+
const httpEquivKey = key.replace("http-equiv:", "");
|
|
403
|
+
processMetaTag(httpEquivKey, value, "http-equiv");
|
|
404
|
+
return acc;
|
|
405
|
+
}
|
|
406
|
+
if (key.startsWith("itemprop:")) {
|
|
407
|
+
const itempropKey = key.replace("itemprop:", "");
|
|
408
|
+
processMetaTag(itempropKey, value, "itemprop");
|
|
409
|
+
return acc;
|
|
410
|
+
}
|
|
411
|
+
const prefixes = [
|
|
412
|
+
"og:",
|
|
413
|
+
"twitter:",
|
|
414
|
+
"fb:",
|
|
415
|
+
"article:",
|
|
416
|
+
"profile:",
|
|
417
|
+
"book:",
|
|
418
|
+
"business:",
|
|
419
|
+
"music:",
|
|
420
|
+
"product:",
|
|
421
|
+
"video:",
|
|
422
|
+
"DC:",
|
|
423
|
+
"DCTERMS:"
|
|
424
|
+
];
|
|
425
|
+
const prefix = prefixes.find((p) => key.startsWith(p));
|
|
426
|
+
if (prefix) {
|
|
427
|
+
const tagKey = key.replace(prefix, "");
|
|
428
|
+
processMetaTag(
|
|
429
|
+
`${prefix.replace(":", "")}:${tagKey}`,
|
|
430
|
+
value,
|
|
431
|
+
prefix === "twitter:" || prefix === "DC:" || prefix === "DCTERMS:" ? "name" : "property"
|
|
432
|
+
);
|
|
433
|
+
return acc;
|
|
434
|
+
}
|
|
435
|
+
if (key.startsWith("apple:") || key.startsWith("msapplication:")) {
|
|
436
|
+
const pfx = key.startsWith("apple:") ? "apple-" : "msapplication-";
|
|
437
|
+
const tagKey = key.replace(/^apple:|^msapplication:/, "");
|
|
438
|
+
processMetaTag(`${pfx}${tagKey}`, value, "name");
|
|
439
|
+
return acc;
|
|
440
|
+
}
|
|
441
|
+
processMetaTag(key, value);
|
|
442
|
+
return acc;
|
|
443
|
+
},
|
|
444
|
+
[
|
|
445
|
+
'<meta charset="UTF-8">',
|
|
446
|
+
'<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no">'
|
|
447
|
+
]
|
|
448
|
+
);
|
|
449
|
+
return tags.join("\n");
|
|
450
|
+
};
|
|
451
|
+
var isBareFilename = (val) => typeof val === "string" && val.length > 0 && !val.startsWith("/") && !val.startsWith("http");
|
|
452
|
+
function resolveFileReferences(metadata, files) {
|
|
453
|
+
if (!files || typeof files !== "object") return metadata;
|
|
454
|
+
const resolve = (val) => {
|
|
455
|
+
if (!isBareFilename(val)) return val;
|
|
456
|
+
const fileEntry = files[val];
|
|
457
|
+
if (fileEntry?.src) return fileEntry.src;
|
|
458
|
+
return val;
|
|
459
|
+
};
|
|
460
|
+
const result = { ...metadata };
|
|
461
|
+
for (const [key, value] of Object.entries(result)) {
|
|
462
|
+
if (typeof value === "string") {
|
|
463
|
+
result[key] = resolve(value);
|
|
464
|
+
} else if (Array.isArray(value)) {
|
|
465
|
+
result[key] = value.map(
|
|
466
|
+
(item) => typeof item === "string" ? resolve(item) : item
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
return result;
|
|
471
|
+
}
|
|
472
|
+
function getPageMetadata(data, pathname) {
|
|
473
|
+
const currentPage = data.pages?.[pathname];
|
|
474
|
+
const stateObject = isObject(currentPage?.state) && currentPage?.state;
|
|
475
|
+
let pageMetadata = currentPage?.metadata || currentPage?.helmet || stateObject || {};
|
|
476
|
+
const appMetadata = data.app?.metadata || {};
|
|
477
|
+
if (data.integrations?.seo) {
|
|
478
|
+
pageMetadata = { ...data.integrations.seo, ...appMetadata, ...pageMetadata };
|
|
479
|
+
} else if (Object.keys(appMetadata).length) {
|
|
480
|
+
pageMetadata = { ...appMetadata, ...pageMetadata };
|
|
481
|
+
}
|
|
482
|
+
if (!pageMetadata.title) pageMetadata.title = data.name + " / symbo.ls" || "Symbols demo";
|
|
483
|
+
pageMetadata = resolveFileReferences(pageMetadata, data.files);
|
|
484
|
+
return pageMetadata;
|
|
485
|
+
}
|
|
486
|
+
|
|
247
487
|
// src/index.js
|
|
248
488
|
var copyStringToClipboard = async (str) => {
|
|
249
489
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@symbo.ls/smbls-utils",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.6.1",
|
|
4
4
|
"author": "symbo.ls",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
@@ -15,13 +15,13 @@
|
|
|
15
15
|
"exports": {
|
|
16
16
|
".": {
|
|
17
17
|
"import": "./dist/esm/index.js",
|
|
18
|
-
"require": "./dist/cjs/index.js"
|
|
19
|
-
"browser": "./dist/esm/index.js",
|
|
20
|
-
"default": "./dist/esm/index.js"
|
|
18
|
+
"require": "./dist/cjs/index.js"
|
|
21
19
|
}
|
|
22
20
|
},
|
|
23
21
|
"source": "src/index.js",
|
|
24
|
-
"publishConfig": {
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public"
|
|
24
|
+
},
|
|
25
25
|
"scripts": {
|
|
26
26
|
"copy:package:cjs": "cp ../../build/package-cjs.json dist/cjs/package.json",
|
|
27
27
|
"build:esm": "cross-env NODE_ENV=$NODE_ENV esbuild src/*.js --target=es2020 --format=esm --outdir=dist/esm --define:process.env.NODE_ENV=process.env.NODE_ENV",
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
},
|
|
33
33
|
"license": "ISC",
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@domql/element": "^3.
|
|
36
|
-
"@domql/utils": "^3.
|
|
35
|
+
"@domql/element": "^3.6.1",
|
|
36
|
+
"@domql/utils": "^3.6.1"
|
|
37
37
|
},
|
|
38
38
|
"gitHead": "9fc1b79b41cdc725ca6b24aec64920a599634681",
|
|
39
39
|
"browser": "./dist/esm/index.js",
|
package/src/cdn.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
function onlyDotsAndNumbers (str) {
|
|
4
|
+
return /^[0-9.]+$/.test(str) && str !== ''
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export const CDN_PROVIDERS = {
|
|
8
|
+
skypack: {
|
|
9
|
+
url: 'https://cdn.skypack.dev',
|
|
10
|
+
formatUrl: (pkg, version) =>
|
|
11
|
+
`${CDN_PROVIDERS.skypack.url}/${pkg}${version !== 'latest' ? `@${version}` : ''}`
|
|
12
|
+
},
|
|
13
|
+
esmsh: {
|
|
14
|
+
url: 'https://esm.sh',
|
|
15
|
+
formatUrl: (pkg, version) =>
|
|
16
|
+
`${CDN_PROVIDERS.esmsh.url}/${pkg}${version !== 'latest' ? `@${version}` : ''}`
|
|
17
|
+
},
|
|
18
|
+
unpkg: {
|
|
19
|
+
url: 'https://unpkg.com',
|
|
20
|
+
formatUrl: (pkg, version) =>
|
|
21
|
+
`${CDN_PROVIDERS.unpkg.url}/${pkg}${version !== 'latest' ? `@${version}` : ''}?module`
|
|
22
|
+
},
|
|
23
|
+
jsdelivr: {
|
|
24
|
+
url: 'https://cdn.jsdelivr.net/npm',
|
|
25
|
+
formatUrl: (pkg, version) =>
|
|
26
|
+
`${CDN_PROVIDERS.jsdelivr.url}/${pkg}${version !== 'latest' ? `@${version}` : ''}/+esm`
|
|
27
|
+
},
|
|
28
|
+
symbols: {
|
|
29
|
+
url: 'https://pkg.symbo.ls',
|
|
30
|
+
formatUrl: (pkg, version) => {
|
|
31
|
+
if (pkg.split('/').length > 2 || !onlyDotsAndNumbers(version)) {
|
|
32
|
+
return `${CDN_PROVIDERS.symbols.url}/${pkg}`
|
|
33
|
+
}
|
|
34
|
+
return `${CDN_PROVIDERS.symbols.url}/${pkg}/${version}.js`
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Maps symbols.json packageManager values to CDN_PROVIDERS keys
|
|
40
|
+
export const PACKAGE_MANAGER_TO_CDN = {
|
|
41
|
+
'esm.sh': 'esmsh',
|
|
42
|
+
'unpkg': 'unpkg',
|
|
43
|
+
'skypack': 'skypack',
|
|
44
|
+
'jsdelivr': 'jsdelivr',
|
|
45
|
+
'pkg.symbo.ls': 'symbols'
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Derive the CDN provider key from a symbols config object.
|
|
50
|
+
* Returns null when packageManager is a local tool (npm/yarn/pnpm/bun).
|
|
51
|
+
*/
|
|
52
|
+
export const getCdnProviderFromConfig = (symbolsConfig = {}) => {
|
|
53
|
+
const { packageManager } = symbolsConfig
|
|
54
|
+
return PACKAGE_MANAGER_TO_CDN[packageManager] || null
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const getCDNUrl = (
|
|
58
|
+
packageName,
|
|
59
|
+
version = 'latest',
|
|
60
|
+
provider = 'esmsh'
|
|
61
|
+
) => {
|
|
62
|
+
const cdnConfig = CDN_PROVIDERS[provider] || CDN_PROVIDERS.esmsh
|
|
63
|
+
return cdnConfig.formatUrl(packageName, version)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Generate an HTML <script type="importmap"> tag from project dependencies.
|
|
68
|
+
*/
|
|
69
|
+
export const getImportMapScript = (data, defaultProvider = 'skypack') => {
|
|
70
|
+
const dependencies = data.dependencies || {}
|
|
71
|
+
const keys = Object.keys(dependencies)
|
|
72
|
+
if (!keys.length) return ''
|
|
73
|
+
|
|
74
|
+
const imports = {}
|
|
75
|
+
for (const pkgName of keys) {
|
|
76
|
+
const version = dependencies[pkgName] || 'latest'
|
|
77
|
+
imports[pkgName] = getCDNUrl(pkgName, version, defaultProvider)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return `<script type="importmap">{
|
|
81
|
+
"imports": ${JSON.stringify(imports, null, 2)}
|
|
82
|
+
}</script>`
|
|
83
|
+
}
|
package/src/index.js
CHANGED
package/src/metadata.js
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
import { isObject } from '@domql/utils'
|
|
4
|
+
|
|
5
|
+
const escapeHtml = (text) => {
|
|
6
|
+
if (text === null || text === undefined) return ''
|
|
7
|
+
const map = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }
|
|
8
|
+
return text.toString().replace(/[&<>"']/g, (m) => map[m])
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const buildAttrs = (obj) =>
|
|
12
|
+
Object.entries(obj || {})
|
|
13
|
+
.filter(([_, v]) => v !== undefined)
|
|
14
|
+
.map(([k, v]) => `${k}="${escapeHtml(v)}"`)
|
|
15
|
+
.join(' ')
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Generates HTML meta tags from a flat metadata object.
|
|
19
|
+
* Works on both server (SSR head injection) and client (dynamic meta updates).
|
|
20
|
+
*/
|
|
21
|
+
export const generateMetaTags = (metadata, isProduction) => {
|
|
22
|
+
if (!isProduction) {
|
|
23
|
+
const faviconTag = (() => {
|
|
24
|
+
const fv = metadata?.favicon || metadata?.favicons
|
|
25
|
+
if (!fv) return '<link rel="icon" href="/favicon.ico">'
|
|
26
|
+
if (typeof fv === 'string') return `<link rel="icon" href="${escapeHtml(fv)}">`
|
|
27
|
+
if (Array.isArray(fv)) {
|
|
28
|
+
return fv
|
|
29
|
+
.map((item) =>
|
|
30
|
+
typeof item === 'string'
|
|
31
|
+
? `<link rel="icon" href="${escapeHtml(item)}">`
|
|
32
|
+
: `<link ${buildAttrs({ rel: 'icon', ...item })}>`
|
|
33
|
+
)
|
|
34
|
+
.join('\n')
|
|
35
|
+
}
|
|
36
|
+
return `<link ${buildAttrs({ rel: 'icon', ...fv })}>`
|
|
37
|
+
})()
|
|
38
|
+
|
|
39
|
+
return [
|
|
40
|
+
'<meta charset="UTF-8">',
|
|
41
|
+
`<title>${escapeHtml(metadata.title || 'Test')}</title>`,
|
|
42
|
+
'<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no">',
|
|
43
|
+
'<meta name="robots" content="noindex">',
|
|
44
|
+
'<meta name="apple-mobile-web-app-capable" content="yes">',
|
|
45
|
+
faviconTag
|
|
46
|
+
].join('\n')
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const tags = Object.entries(metadata).reduce(
|
|
50
|
+
(acc, [key, value]) => {
|
|
51
|
+
if (!value && value !== 0 && value !== false) return acc
|
|
52
|
+
|
|
53
|
+
if (key === 'title') {
|
|
54
|
+
acc.push(`<title>${escapeHtml(value)}</title>`)
|
|
55
|
+
return acc
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (key === 'canonical') {
|
|
59
|
+
acc.push(`<link rel="canonical" href="${escapeHtml(value)}">`)
|
|
60
|
+
return acc
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (key === 'manifest') {
|
|
64
|
+
acc.push(`<link rel="manifest" href="${escapeHtml(value)}">`)
|
|
65
|
+
return acc
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (key === 'favicon' || key === 'favicons' || key === 'icon' || key === 'icons') {
|
|
69
|
+
const items = Array.isArray(value) ? value : [value]
|
|
70
|
+
items.forEach((item) => {
|
|
71
|
+
if (typeof item === 'string') {
|
|
72
|
+
acc.push(`<link rel="icon" href="${escapeHtml(item)}">`)
|
|
73
|
+
} else if (typeof item === 'object') {
|
|
74
|
+
const attrs = buildAttrs(item)
|
|
75
|
+
if (!/rel=/.test(attrs)) {
|
|
76
|
+
acc.push(`<link rel="icon" ${attrs}>`)
|
|
77
|
+
} else {
|
|
78
|
+
acc.push(`<link ${attrs}>`)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
})
|
|
82
|
+
return acc
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (key === 'alternate') {
|
|
86
|
+
const alternates = Array.isArray(value) ? value : [value]
|
|
87
|
+
alternates.forEach((alt) => {
|
|
88
|
+
if (typeof alt === 'object') {
|
|
89
|
+
const attrs = Object.entries(alt)
|
|
90
|
+
.filter(([_, attrValue]) => attrValue !== undefined)
|
|
91
|
+
.map(([attrKey, attrValue]) => `${attrKey}="${escapeHtml(attrValue)}"`)
|
|
92
|
+
.join(' ')
|
|
93
|
+
acc.push(`<link rel="alternate" ${attrs}>`)
|
|
94
|
+
}
|
|
95
|
+
})
|
|
96
|
+
return acc
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const processMetaTag = (tagKey, tagValue, attrType = 'name') => {
|
|
100
|
+
if (
|
|
101
|
+
typeof tagValue === 'string' ||
|
|
102
|
+
typeof tagValue === 'number' ||
|
|
103
|
+
typeof tagValue === 'boolean'
|
|
104
|
+
) {
|
|
105
|
+
acc.push(`<meta ${attrType}="${escapeHtml(tagKey)}" content="${escapeHtml(tagValue)}">`)
|
|
106
|
+
} else if (Array.isArray(tagValue)) {
|
|
107
|
+
tagValue.forEach((item) => {
|
|
108
|
+
if (typeof item === 'string' || typeof item === 'number' || typeof item === 'boolean') {
|
|
109
|
+
acc.push(`<meta ${attrType}="${escapeHtml(tagKey)}" content="${escapeHtml(item)}">`)
|
|
110
|
+
} else if (typeof item === 'object') {
|
|
111
|
+
const attrs = Object.entries(item)
|
|
112
|
+
.filter(([_, attrValue]) => attrValue !== undefined)
|
|
113
|
+
.map(([attrKey, attrValue]) => `${attrKey}="${escapeHtml(attrValue)}"`)
|
|
114
|
+
.join(' ')
|
|
115
|
+
acc.push(`<meta ${attrs}>`)
|
|
116
|
+
}
|
|
117
|
+
})
|
|
118
|
+
} else if (typeof tagValue === 'object') {
|
|
119
|
+
const attrs = Object.entries(tagValue)
|
|
120
|
+
.filter(([_, attrValue]) => attrValue !== undefined)
|
|
121
|
+
.map(([attrKey, attrValue]) => `${attrKey}="${escapeHtml(attrValue)}"`)
|
|
122
|
+
.join(' ')
|
|
123
|
+
acc.push(`<meta ${attrs}>`)
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (key.startsWith('http-equiv:')) {
|
|
128
|
+
const httpEquivKey = key.replace('http-equiv:', '')
|
|
129
|
+
processMetaTag(httpEquivKey, value, 'http-equiv')
|
|
130
|
+
return acc
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (key.startsWith('itemprop:')) {
|
|
134
|
+
const itempropKey = key.replace('itemprop:', '')
|
|
135
|
+
processMetaTag(itempropKey, value, 'itemprop')
|
|
136
|
+
return acc
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const prefixes = [
|
|
140
|
+
'og:', 'twitter:', 'fb:', 'article:', 'profile:', 'book:',
|
|
141
|
+
'business:', 'music:', 'product:', 'video:', 'DC:', 'DCTERMS:'
|
|
142
|
+
]
|
|
143
|
+
const prefix = prefixes.find((p) => key.startsWith(p))
|
|
144
|
+
if (prefix) {
|
|
145
|
+
const tagKey = key.replace(prefix, '')
|
|
146
|
+
processMetaTag(
|
|
147
|
+
`${prefix.replace(':', '')}:${tagKey}`,
|
|
148
|
+
value,
|
|
149
|
+
prefix === 'twitter:' || prefix === 'DC:' || prefix === 'DCTERMS:' ? 'name' : 'property'
|
|
150
|
+
)
|
|
151
|
+
return acc
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (key.startsWith('apple:') || key.startsWith('msapplication:')) {
|
|
155
|
+
const pfx = key.startsWith('apple:') ? 'apple-' : 'msapplication-'
|
|
156
|
+
const tagKey = key.replace(/^apple:|^msapplication:/, '')
|
|
157
|
+
processMetaTag(`${pfx}${tagKey}`, value, 'name')
|
|
158
|
+
return acc
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
processMetaTag(key, value)
|
|
162
|
+
return acc
|
|
163
|
+
},
|
|
164
|
+
[
|
|
165
|
+
'<meta charset="UTF-8">',
|
|
166
|
+
'<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no">'
|
|
167
|
+
]
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
return tags.join('\n')
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Extract page-level metadata from project data for a given pathname.
|
|
175
|
+
* Merges global SEO (data.integrations.seo) with page-level metadata/helmet/state.
|
|
176
|
+
*/
|
|
177
|
+
/**
|
|
178
|
+
* Check if a string looks like a bare filename (not an absolute path or URL).
|
|
179
|
+
* These are references to project files from `data.files`.
|
|
180
|
+
*/
|
|
181
|
+
const isBareFilename = (val) =>
|
|
182
|
+
typeof val === 'string' && val.length > 0 && !val.startsWith('/') && !val.startsWith('http')
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Resolve bare filename references in metadata values against `data.files`.
|
|
186
|
+
* If a metadata value is a plain filename (e.g. "logo.png") and a matching
|
|
187
|
+
* entry exists in `data.files`, replace it with the file's `src` URL.
|
|
188
|
+
*/
|
|
189
|
+
function resolveFileReferences (metadata, files) {
|
|
190
|
+
if (!files || typeof files !== 'object') return metadata
|
|
191
|
+
|
|
192
|
+
const resolve = (val) => {
|
|
193
|
+
if (!isBareFilename(val)) return val
|
|
194
|
+
const fileEntry = files[val]
|
|
195
|
+
if (fileEntry?.src) return fileEntry.src
|
|
196
|
+
return val
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const result = { ...metadata }
|
|
200
|
+
for (const [key, value] of Object.entries(result)) {
|
|
201
|
+
if (typeof value === 'string') {
|
|
202
|
+
result[key] = resolve(value)
|
|
203
|
+
} else if (Array.isArray(value)) {
|
|
204
|
+
result[key] = value.map((item) =>
|
|
205
|
+
typeof item === 'string' ? resolve(item) : item
|
|
206
|
+
)
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return result
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export function getPageMetadata (data, pathname) {
|
|
213
|
+
const currentPage = data.pages?.[pathname]
|
|
214
|
+
const stateObject = isObject(currentPage?.state) && currentPage?.state
|
|
215
|
+
let pageMetadata = currentPage?.metadata || currentPage?.helmet || stateObject || {}
|
|
216
|
+
const appMetadata = data.app?.metadata || {}
|
|
217
|
+
if (data.integrations?.seo) {
|
|
218
|
+
pageMetadata = { ...data.integrations.seo, ...appMetadata, ...pageMetadata }
|
|
219
|
+
} else if (Object.keys(appMetadata).length) {
|
|
220
|
+
pageMetadata = { ...appMetadata, ...pageMetadata }
|
|
221
|
+
}
|
|
222
|
+
if (!pageMetadata.title) pageMetadata.title = data.name + ' / symbo.ls' || 'Symbols demo'
|
|
223
|
+
pageMetadata = resolveFileReferences(pageMetadata, data.files)
|
|
224
|
+
return pageMetadata
|
|
225
|
+
}
|