nuxt-og-image 6.7.8 → 6.9.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/dist/chunks/tw4.mjs +2 -2
- package/dist/chunks/uno.mjs +4 -4
- package/dist/cli.mjs +1 -1
- package/dist/devtools/nuxt.config.ts +4 -1
- package/dist/module.d.mts +10 -0
- package/dist/module.d.ts +10 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +3 -3
- package/dist/runtime/app/client-utils.js +4 -3
- package/dist/runtime/app/utils.js +3 -2
- package/dist/runtime/server/nitro.d.ts +9 -0
- package/dist/runtime/server/nitro.js +10 -0
- package/dist/runtime/server/og-image/bindings/fetch/node.d.ts +2 -0
- package/dist/runtime/server/og-image/bindings/fetch/node.js +42 -0
- package/dist/runtime/server/og-image/bindings/fetch/web.d.ts +7 -0
- package/dist/runtime/server/og-image/bindings/fetch/web.js +3 -0
- package/dist/runtime/server/og-image/bindings/font-assets/external-url.d.ts +0 -11
- package/dist/runtime/server/og-image/bindings/font-assets/external-url.js +2 -19
- package/dist/runtime/server/og-image/bindings/satori/harfbuzz.d.ts +4 -0
- package/dist/runtime/server/og-image/bindings/satori/harfbuzz.js +33 -0
- package/dist/runtime/server/og-image/browser/screenshot.js +4 -1
- package/dist/runtime/server/og-image/context.js +9 -1
- package/dist/runtime/server/og-image/font-subsets.d.ts +3 -11
- package/dist/runtime/server/og-image/font-subsets.js +50 -27
- package/dist/runtime/server/og-image/fonts.d.ts +1 -1
- package/dist/runtime/server/routes/resolve.js +2 -2
- package/dist/runtime/server/util/cloudflare.js +2 -2
- package/dist/runtime/server/util/eventHandlers.js +3 -3
- package/dist/runtime/server/util/ssrf.d.ts +31 -0
- package/dist/runtime/server/util/ssrf.js +64 -5
- package/dist/runtime/server/utils.d.ts +1 -1
- package/dist/runtime/server/utils.js +3 -3
- package/dist/runtime/shared.d.ts +10 -1
- package/dist/runtime/shared.js +2 -2
- package/dist/runtime/types.d.ts +5 -1
- package/dist/shared/{nuxt-og-image.BMHzg1Vz.mjs → nuxt-og-image.CTD0ZPNd.mjs} +634 -468
- package/package.json +70 -66
- package/types/virtual.d.ts +26 -0
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import { createHash, randomBytes } from 'node:crypto';
|
|
2
2
|
import * as fs from 'node:fs';
|
|
3
3
|
import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, rmSync } from 'node:fs';
|
|
4
|
-
import {
|
|
4
|
+
import { readFile, mkdir, writeFile, readdir } from 'node:fs/promises';
|
|
5
5
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
6
|
-
import { resolvePath, useNuxt, addTemplate, updateTemplates, directoryToURL, tryResolveModule, addTypeTemplate, defineNuxtModule, createResolver, addImports, hasNuxtModule, hasNuxtModuleCompatibility, getNuxtModuleVersion, addVitePlugin, addBuildPlugin, addServerHandler, addPlugin, addComponentsDir,
|
|
6
|
+
import { resolvePath, useNuxt, getNuxtVersion, addTemplate, updateTemplates, directoryToURL, tryResolveModule, addTypeTemplate, defineNuxtModule, createResolver, addImports, hasNuxtModule, hasNuxtModuleCompatibility, getNuxtModuleVersion, addVitePlugin, addBuildPlugin, addServerHandler, addServerImports, addPlugin, addComponentsDir, addServerPlugin } from '@nuxt/kit';
|
|
7
7
|
import { defu } from 'defu';
|
|
8
8
|
import { fnv1a64Base36 } from 'fnv1a-64';
|
|
9
9
|
import { installNuxtSiteConfig } from 'nuxt-site-config/kit';
|
|
10
10
|
import { resolveNitroPreset, renderNitroTypeAugmentations, setupNitroRuntimeCompatibility, getNuxtModuleOptions, isNuxtGenerate } from 'nuxtseo-shared/kit';
|
|
11
|
-
import {
|
|
11
|
+
import { join, basename, dirname, relative, isAbsolute, resolve } from 'pathe';
|
|
12
12
|
import { readPackageJSON } from 'pkg-types';
|
|
13
13
|
import { isCI, isAgent, hasTTY } from 'std-env';
|
|
14
|
+
import MagicString from 'magic-string';
|
|
14
15
|
import { parseAndWalk } from 'oxc-walker';
|
|
16
|
+
import { resolveModulePath } from 'exsolve';
|
|
15
17
|
import { logger } from '../../dist/runtime/logger.js';
|
|
16
18
|
import { Launcher } from 'chrome-launcher';
|
|
17
19
|
import { detectPackageManager, addDependency } from 'nypm';
|
|
@@ -19,7 +21,6 @@ import { setupDevToolsUI as setupDevToolsUI$1, setupDevToolsRpc } from 'nuxtseo-
|
|
|
19
21
|
import { createStorage } from 'unstorage';
|
|
20
22
|
import fsDriver from 'unstorage/drivers/fs-lite';
|
|
21
23
|
import { resolveColorMix, GRADIENT_COLOR_SPACE_RE, splitCssDeclarations } from '../../dist/runtime/server/og-image/utils/css.js';
|
|
22
|
-
import MagicString from 'magic-string';
|
|
23
24
|
import { stripLiteral } from 'strip-literal';
|
|
24
25
|
import { parseURL, parseQuery } from 'ufo';
|
|
25
26
|
import { createUnplugin } from 'unplugin';
|
|
@@ -27,9 +28,81 @@ import { parse } from '@vue/compiler-sfc';
|
|
|
27
28
|
import { ofetch } from 'ofetch';
|
|
28
29
|
import { parse as parse$1, walkSync, ELEMENT_NODE as ELEMENT_NODE$2, renderSync } from 'ultrahtml';
|
|
29
30
|
import { RE_MATCH_EMOJIS, getEmojiCodePoint, getEmojiIconNames } from '../../dist/runtime/server/og-image/core/transforms/emojis/emoji-utils.js';
|
|
30
|
-
import { resolveModulePath } from 'exsolve';
|
|
31
31
|
import { p as promptFontsMigration } from './nuxt-og-image.CJa2KCie.mjs';
|
|
32
32
|
|
|
33
|
+
const valueTypes = { i: 127, p: 127, j: 126, f: 125, d: 124 };
|
|
34
|
+
function encodeLength(value) {
|
|
35
|
+
const bytes = [];
|
|
36
|
+
do {
|
|
37
|
+
const byte = value & 127;
|
|
38
|
+
value >>>= 7;
|
|
39
|
+
bytes.push(value ? byte | 128 : byte);
|
|
40
|
+
} while (value);
|
|
41
|
+
return bytes;
|
|
42
|
+
}
|
|
43
|
+
function createHarfBuzzCallbackModule(signature) {
|
|
44
|
+
const type = (character) => {
|
|
45
|
+
const value = valueTypes[character];
|
|
46
|
+
if (value === void 0)
|
|
47
|
+
throw new Error(`Unsupported HarfBuzz callback type: ${character}`);
|
|
48
|
+
return value;
|
|
49
|
+
};
|
|
50
|
+
const params = [...signature.slice(1)].map(type);
|
|
51
|
+
const results = signature[0] === "v" ? [] : [type(signature[0])];
|
|
52
|
+
const functionType = [1, 96, ...encodeLength(params.length), ...params, results.length, ...results];
|
|
53
|
+
return Uint8Array.from([
|
|
54
|
+
0,
|
|
55
|
+
97,
|
|
56
|
+
115,
|
|
57
|
+
109,
|
|
58
|
+
1,
|
|
59
|
+
0,
|
|
60
|
+
0,
|
|
61
|
+
0,
|
|
62
|
+
1,
|
|
63
|
+
...encodeLength(functionType.length),
|
|
64
|
+
...functionType,
|
|
65
|
+
2,
|
|
66
|
+
7,
|
|
67
|
+
1,
|
|
68
|
+
1,
|
|
69
|
+
101,
|
|
70
|
+
1,
|
|
71
|
+
102,
|
|
72
|
+
0,
|
|
73
|
+
0,
|
|
74
|
+
7,
|
|
75
|
+
5,
|
|
76
|
+
1,
|
|
77
|
+
1,
|
|
78
|
+
102,
|
|
79
|
+
0,
|
|
80
|
+
0
|
|
81
|
+
]);
|
|
82
|
+
}
|
|
83
|
+
async function prepareHarfBuzzCallbacks(adapterPath, outputDir) {
|
|
84
|
+
const signatures = /* @__PURE__ */ new Set();
|
|
85
|
+
parseAndWalk(await readFile(adapterPath, "utf8"), adapterPath, {
|
|
86
|
+
enter(node) {
|
|
87
|
+
if (node.type !== "CallExpression" || node.callee.type !== "Identifier" || node.callee.name !== "addFunction")
|
|
88
|
+
return;
|
|
89
|
+
const signature = node.arguments[1];
|
|
90
|
+
if (signature?.type === "Literal" && typeof signature.value === "string")
|
|
91
|
+
signatures.add(signature.value);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
await mkdir(outputDir, { recursive: true });
|
|
95
|
+
const entries = await Promise.all([...signatures].sort().map(async (signature) => {
|
|
96
|
+
const path = join(outputDir, `callback-${signature}.wasm`);
|
|
97
|
+
await writeFile(path, createHarfBuzzCallbackModule(signature));
|
|
98
|
+
return { signature, path };
|
|
99
|
+
}));
|
|
100
|
+
return [
|
|
101
|
+
...entries.map(({ signature, path }) => `import ${signature} from ${JSON.stringify(`${path}?module`)}`),
|
|
102
|
+
`export default { ${entries.map(({ signature }) => signature).join(", ")} }`
|
|
103
|
+
].join("\n");
|
|
104
|
+
}
|
|
105
|
+
|
|
33
106
|
const isUndefinedOrTruthy = (v) => typeof v === "undefined" || v !== false;
|
|
34
107
|
function checkLocalChrome() {
|
|
35
108
|
if (isCI)
|
|
@@ -46,6 +119,16 @@ async function hasResolvableDependency(dep) {
|
|
|
46
119
|
return null;
|
|
47
120
|
}).then((r) => r && r !== dep);
|
|
48
121
|
}
|
|
122
|
+
const MIN_SATORI_VERSION = "0.33.5";
|
|
123
|
+
function isSupportedSatoriVersion(version) {
|
|
124
|
+
const current = (version || "").split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
125
|
+
const minimum = MIN_SATORI_VERSION.split(".").map(Number);
|
|
126
|
+
for (let i = 0; i < minimum.length; i++) {
|
|
127
|
+
if ((current[i] ?? 0) !== minimum[i])
|
|
128
|
+
return (current[i] ?? 0) > minimum[i];
|
|
129
|
+
}
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
49
132
|
const VALID_RENDERER_SUFFIXES = ["satori", "browser", "takumi"];
|
|
50
133
|
const RE_RENDERER_SUFFIX_CI = /\.?(satori|browser|takumi)$/i;
|
|
51
134
|
const RE_RENDERER_SUFFIX = /(Satori|Browser|Takumi)$/;
|
|
@@ -119,6 +202,8 @@ const cloudflare = {
|
|
|
119
202
|
sharp: false,
|
|
120
203
|
emoji: "fetch",
|
|
121
204
|
// edge size limits - use API instead of bundling 24MB icons
|
|
205
|
+
fetch: "web",
|
|
206
|
+
// no DNS API
|
|
122
207
|
wasm: {
|
|
123
208
|
esmImport: true
|
|
124
209
|
// Do NOT set lazy here — Nitro's cloudflare preset uses lazy: false because
|
|
@@ -141,8 +226,9 @@ const WebContainer = {
|
|
|
141
226
|
satori: "wasm-fs",
|
|
142
227
|
takumi: "wasm",
|
|
143
228
|
sharp: false,
|
|
144
|
-
emoji: "fetch"
|
|
229
|
+
emoji: "fetch",
|
|
145
230
|
// webcontainer has size constraints
|
|
231
|
+
fetch: "web"
|
|
146
232
|
};
|
|
147
233
|
const RuntimeCompatibility = {
|
|
148
234
|
"nitro-dev": NodeDevRuntime,
|
|
@@ -183,6 +269,7 @@ const RuntimeCompatibility = {
|
|
|
183
269
|
sharp: false,
|
|
184
270
|
emoji: "fetch",
|
|
185
271
|
// edge size limits
|
|
272
|
+
fetch: "web",
|
|
186
273
|
wasm: {
|
|
187
274
|
rollup: {
|
|
188
275
|
targetEnv: "auto-inline",
|
|
@@ -203,6 +290,7 @@ const RuntimeCompatibility = {
|
|
|
203
290
|
sharp: false,
|
|
204
291
|
emoji: "fetch",
|
|
205
292
|
// edge size limits - bundling 24MB icons not viable
|
|
293
|
+
fetch: "web",
|
|
206
294
|
wasm: {
|
|
207
295
|
// lowers workers kb size
|
|
208
296
|
esmImport: true,
|
|
@@ -276,6 +364,36 @@ async function applyNitroPresetCompatibility(nitroConfig, options) {
|
|
|
276
364
|
await applyBinding("sharp"),
|
|
277
365
|
nitroConfig.alias || {}
|
|
278
366
|
);
|
|
367
|
+
if (resolvedCompatibility.satori) {
|
|
368
|
+
const satoriPackage = await readPackageJSON(`${satoriPkgDir}/package.json`);
|
|
369
|
+
if (!isSupportedSatoriVersion(satoriPackage.version))
|
|
370
|
+
throw new Error(`[Nuxt OG Image] Satori ${satoriPackage.version} is not supported. Satori ${MIN_SATORI_VERSION} or later is required (GHSA-wx4j-mvgx-mqwp). Run: npm install satori@latest`);
|
|
371
|
+
const harfbuzzWasmPath = resolveModulePath("harfbuzzjs/hb.wasm", { from: `${satoriPkgDir}/package.json` });
|
|
372
|
+
if (resolvedCompatibility.satori === "wasm" || target.replace(RE_LEGACY_SUFFIX, "") === "netlify-edge") {
|
|
373
|
+
const harfbuzzDir = harfbuzzWasmPath.slice(0, -"hb.wasm".length);
|
|
374
|
+
nitroConfig.virtual["#og-image/harfbuzz-callbacks"] = resolvedCompatibility.satori === "wasm" ? await prepareHarfBuzzCallbacks(`${harfbuzzDir}hbjs.js`, join(useNuxt().options.buildDir, "cache/og-image/harfbuzz")) : "export default undefined";
|
|
375
|
+
Object.assign(nitroConfig.alias, {
|
|
376
|
+
"harfbuzzjs": await resolve.resolvePath("./runtime/server/og-image/bindings/satori/harfbuzz"),
|
|
377
|
+
"#og-image/harfbuzz-factory": `${harfbuzzDir}hb.js`,
|
|
378
|
+
"#og-image/harfbuzz-adapter": `${harfbuzzDir}hbjs.js`
|
|
379
|
+
});
|
|
380
|
+
if (resolvedCompatibility.satori === "wasm") {
|
|
381
|
+
nitroConfig.alias["#og-image/harfbuzz-wasm"] = `${harfbuzzWasmPath}?module`;
|
|
382
|
+
} else {
|
|
383
|
+
const base64 = (await readFile(harfbuzzWasmPath)).toString("base64");
|
|
384
|
+
nitroConfig.virtual["#og-image/harfbuzz-wasm"] = `export default imports => WebAssembly.instantiate(Uint8Array.from(atob(${JSON.stringify(base64)}), c => c.charCodeAt(0)), imports)`;
|
|
385
|
+
}
|
|
386
|
+
} else if (Number.parseInt(getNuxtVersion(useNuxt()), 10) >= 5) {
|
|
387
|
+
const config = nitroConfig;
|
|
388
|
+
config.traceDeps = [.../* @__PURE__ */ new Set([...config.traceDeps || [], "harfbuzzjs*"])];
|
|
389
|
+
} else {
|
|
390
|
+
nitroConfig.externals = nitroConfig.externals || {};
|
|
391
|
+
nitroConfig.externals.traceInclude = [.../* @__PURE__ */ new Set([
|
|
392
|
+
...nitroConfig.externals.traceInclude || [],
|
|
393
|
+
harfbuzzWasmPath
|
|
394
|
+
])];
|
|
395
|
+
}
|
|
396
|
+
}
|
|
279
397
|
if (Object.values(resolvedCompatibility).includes("wasm")) {
|
|
280
398
|
nitroConfig.experimental = nitroConfig.experimental || {};
|
|
281
399
|
nitroConfig.experimental.wasm = true;
|
|
@@ -334,6 +452,27 @@ async function setupBuildHandler(config, resolve, getDetectedRenderers, getCompa
|
|
|
334
452
|
detectedRenderers: getDetectedRenderers(),
|
|
335
453
|
metadata: getCompatibilityMeta()
|
|
336
454
|
});
|
|
455
|
+
const rollupConfig = nitro.options.rollupConfig ||= { output: {} };
|
|
456
|
+
rollupConfig.plugins = [rollupConfig.plugins, {
|
|
457
|
+
name: "og-image-harfbuzz-worker",
|
|
458
|
+
transform(code, id) {
|
|
459
|
+
if (!nitro.options.alias["#og-image/harfbuzz-factory"] || !id.endsWith("/harfbuzzjs/hb.js"))
|
|
460
|
+
return;
|
|
461
|
+
const transformed = new MagicString(code);
|
|
462
|
+
parseAndWalk(code, id, {
|
|
463
|
+
enter(node) {
|
|
464
|
+
if (node.type === "VariableDeclarator" && node.id.type === "Identifier" && node.id.name === "convertJsFunctionToWasm" && node.init) {
|
|
465
|
+
transformed.prependLeft(node.init.start, 'Module["convertJsFunctionToWasm"] || (');
|
|
466
|
+
transformed.appendRight(node.init.end, ")");
|
|
467
|
+
}
|
|
468
|
+
if (node.type === "VariableDeclarator" && node.id.type === "Identifier" && ["ENVIRONMENT_IS_NODE", "ENVIRONMENT_IS_WEB", "ENVIRONMENT_IS_WORKER"].includes(node.id.name) && node.init) {
|
|
469
|
+
transformed.overwrite(node.init.start, node.init.end, "false");
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
});
|
|
473
|
+
return { code: transformed.toString(), map: transformed.generateMap({ hires: true }).toString() };
|
|
474
|
+
}
|
|
475
|
+
}];
|
|
337
476
|
const target = resolveOgImagePreset(nitro.options);
|
|
338
477
|
const normalizedTarget = target.replace(RE_LEGACY_SUFFIX, "");
|
|
339
478
|
const isEdgePreset = ["cloudflare", "cloudflare-pages", "cloudflare-pages-static", "cloudflare-module", "cloudflare-durable", "vercel-edge", "netlify-edge"].includes(normalizedTarget);
|
|
@@ -2086,8 +2225,7 @@ async function initFontless(options) {
|
|
|
2086
2225
|
{
|
|
2087
2226
|
dev: false,
|
|
2088
2227
|
renderedFontURLs,
|
|
2089
|
-
assetsBaseURL: FONTS_URL_PREFIX
|
|
2090
|
-
callback: (filename, url) => renderedFontURLs.set(filename, url)
|
|
2228
|
+
assetsBaseURL: FONTS_URL_PREFIX
|
|
2091
2229
|
},
|
|
2092
2230
|
faces
|
|
2093
2231
|
),
|
|
@@ -2111,7 +2249,7 @@ async function initFontless(options) {
|
|
|
2111
2249
|
providers
|
|
2112
2250
|
});
|
|
2113
2251
|
options.logger?.debug(`fontless initialized with formats: ['woff', 'ttf'], subsets: ${JSON.stringify(options.fontSubsets || ["latin"])}, priority: ${JSON.stringify(nuxtFontsConfig?.priority || ["google", "bunny", "fontsource"])}`);
|
|
2114
|
-
options.nuxt._ogImageFontless = { resolver,
|
|
2252
|
+
options.nuxt._ogImageFontless = { resolver, providerNames: priority };
|
|
2115
2253
|
}
|
|
2116
2254
|
function persistFontUrlMapping(options) {
|
|
2117
2255
|
if (!options.fontContext?.renderedFontURLs.size)
|
|
@@ -2757,11 +2895,10 @@ function isVue(id, opts = {}) {
|
|
|
2757
2895
|
}
|
|
2758
2896
|
return true;
|
|
2759
2897
|
}
|
|
2760
|
-
const
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
}
|
|
2898
|
+
const VUE_RE = /\.vue(?:\?|$)/;
|
|
2899
|
+
const JS_RE = /\.[cm]?[jt]sx?(?:\?|$)/;
|
|
2900
|
+
const ISLANDS_RE = /components\.islands\.mjs(?:\?|$)/;
|
|
2901
|
+
const COMPOSABLE_CODE_MARKER = "defineOgImage";
|
|
2765
2902
|
const TreeShakeComposablesPlugin = createUnplugin(() => {
|
|
2766
2903
|
const composableNames = [
|
|
2767
2904
|
"defineOgImage",
|
|
@@ -2774,21 +2911,26 @@ const TreeShakeComposablesPlugin = createUnplugin(() => {
|
|
|
2774
2911
|
return {
|
|
2775
2912
|
name: "nuxt-og-image:zero-runtime:transform",
|
|
2776
2913
|
enforce: "pre",
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2914
|
+
transform: {
|
|
2915
|
+
filter: {
|
|
2916
|
+
// @todo re-implement composable tree-shaking for island files
|
|
2917
|
+
id: { include: [VUE_RE, JS_RE], exclude: [ISLANDS_RE] },
|
|
2918
|
+
code: COMPOSABLE_CODE_MARKER
|
|
2919
|
+
},
|
|
2920
|
+
handler(code, id) {
|
|
2921
|
+
if (VUE_RE.test(id) && !isVue(id, { type: ["script"] })) {
|
|
2922
|
+
return;
|
|
2923
|
+
}
|
|
2784
2924
|
if (!COMPOSABLE_RE.test(code)) {
|
|
2785
2925
|
return;
|
|
2786
2926
|
}
|
|
2787
|
-
|
|
2927
|
+
const s = new MagicString(code);
|
|
2928
|
+
for (const match of stripLiteral(code).matchAll(COMPOSABLE_RE_GLOBAL)) {
|
|
2788
2929
|
s.overwrite(match.index, match.index + match[0].length, `${match[1]} import.meta.prerender && ${match[2]}`);
|
|
2789
2930
|
}
|
|
2790
|
-
|
|
2791
|
-
|
|
2931
|
+
if (!s.hasChanged()) {
|
|
2932
|
+
return;
|
|
2933
|
+
}
|
|
2792
2934
|
return {
|
|
2793
2935
|
code: s.toString(),
|
|
2794
2936
|
map: s.generateMap({ hires: true })
|
|
@@ -3304,6 +3446,7 @@ const RE_SVG_ID_ATTR = /\bid="([^"]+)"/g;
|
|
|
3304
3446
|
const RE_SVG_VIEWBOX = /viewBox="([^"]*)"/;
|
|
3305
3447
|
const RE_SVG_BODY = /<svg[^>]*>([\s\S]*)<\/svg>/;
|
|
3306
3448
|
const RE_OG_IMAGE_QUERY$1 = /\?og-image(?:-depth=\d+)?$/;
|
|
3449
|
+
const RE_VUE_ID$1 = /\.vue(?:\?|$)/;
|
|
3307
3450
|
const RE_TEXT_BETWEEN_TAGS = />([^<]*)</g;
|
|
3308
3451
|
const RE_SPECIAL_REGEX_CHARS = /[.*+?^${}()|[\]\\]/g;
|
|
3309
3452
|
const RE_WRAPPER_DIV_START = /^<div>/;
|
|
@@ -3542,419 +3685,424 @@ const AssetTransformPlugin = createUnplugin((options) => {
|
|
|
3542
3685
|
return {
|
|
3543
3686
|
name: "nuxt-og-image:asset-transform",
|
|
3544
3687
|
enforce: "pre",
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
const bounds = getOuterTemplateBounds(code);
|
|
3557
|
-
if (!bounds)
|
|
3558
|
-
return;
|
|
3559
|
-
const s = new MagicString(code);
|
|
3560
|
-
const { templateStart, templateEnd } = bounds;
|
|
3561
|
-
let template = code.slice(templateStart, templateEnd);
|
|
3562
|
-
let hasChanges = false;
|
|
3563
|
-
if (options.emojiSet && RE_MATCH_EMOJIS.test(template)) {
|
|
3564
|
-
if (!emojiIcons) {
|
|
3565
|
-
emojiIcons = options.emojiIcons ?? await import(`@iconify-json/${options.emojiSet}/icons.json`, { with: { type: 'json' } }).then((m) => m.default).catch(() => {
|
|
3566
|
-
return null;
|
|
3567
|
-
});
|
|
3568
|
-
}
|
|
3569
|
-
RE_MATCH_EMOJIS.lastIndex = 0;
|
|
3570
|
-
const allEmojis = /* @__PURE__ */ new Set();
|
|
3571
|
-
for (const match of template.matchAll(RE_MATCH_EMOJIS)) {
|
|
3572
|
-
allEmojis.add(match[0]);
|
|
3688
|
+
transform: {
|
|
3689
|
+
filter: {
|
|
3690
|
+
id: RE_VUE_ID$1
|
|
3691
|
+
},
|
|
3692
|
+
async handler(code, rawId) {
|
|
3693
|
+
const isNested = RE_OG_IMAGE_QUERY$1.test(rawId);
|
|
3694
|
+
if (!isNested) {
|
|
3695
|
+
if (!rawId.endsWith(".vue"))
|
|
3696
|
+
return;
|
|
3697
|
+
if (!options.ogComponentPaths.some((dir) => rawId.startsWith(`${dir}/`) || rawId.startsWith(`${dir}\\`)))
|
|
3698
|
+
return;
|
|
3573
3699
|
}
|
|
3574
|
-
const
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3700
|
+
const id = rawId.replace(RE_OG_IMAGE_QUERY$1, "");
|
|
3701
|
+
if (isNested)
|
|
3702
|
+
options.onNestedTransform?.(id);
|
|
3703
|
+
const bounds = getOuterTemplateBounds(code);
|
|
3704
|
+
if (!bounds)
|
|
3705
|
+
return;
|
|
3706
|
+
const s = new MagicString(code);
|
|
3707
|
+
const { templateStart, templateEnd } = bounds;
|
|
3708
|
+
let template = code.slice(templateStart, templateEnd);
|
|
3709
|
+
let hasChanges = false;
|
|
3710
|
+
if (options.emojiSet && RE_MATCH_EMOJIS.test(template)) {
|
|
3711
|
+
if (!emojiIcons) {
|
|
3712
|
+
emojiIcons = options.emojiIcons ?? await import(`@iconify-json/${options.emojiSet}/icons.json`, { with: { type: 'json' } }).then((m) => m.default).catch(() => {
|
|
3713
|
+
return null;
|
|
3714
|
+
});
|
|
3579
3715
|
}
|
|
3580
|
-
|
|
3581
|
-
|
|
3716
|
+
RE_MATCH_EMOJIS.lastIndex = 0;
|
|
3717
|
+
const allEmojis = /* @__PURE__ */ new Set();
|
|
3718
|
+
for (const match of template.matchAll(RE_MATCH_EMOJIS)) {
|
|
3719
|
+
allEmojis.add(match[0]);
|
|
3582
3720
|
}
|
|
3583
|
-
|
|
3584
|
-
|
|
3721
|
+
const emojiSvgMap = /* @__PURE__ */ new Map();
|
|
3722
|
+
await Promise.all(Array.from(allEmojis, async (emoji) => {
|
|
3723
|
+
let svg = null;
|
|
3724
|
+
if (emojiIcons) {
|
|
3725
|
+
svg = buildEmojiSvg(emoji, emojiIcons, options.emojiSet);
|
|
3726
|
+
}
|
|
3727
|
+
if (!svg) {
|
|
3728
|
+
svg = await fetchEmojiSvg(emoji, options.emojiSet);
|
|
3729
|
+
}
|
|
3730
|
+
if (svg) {
|
|
3731
|
+
emojiSvgMap.set(emoji, svg);
|
|
3732
|
+
}
|
|
3733
|
+
}));
|
|
3734
|
+
if (emojiSvgMap.size > 0) {
|
|
3735
|
+
RE_TEXT_BETWEEN_TAGS.lastIndex = 0;
|
|
3736
|
+
template = template.replace(RE_TEXT_BETWEEN_TAGS, (fullMatch, textContent) => {
|
|
3737
|
+
if (!textContent)
|
|
3738
|
+
return fullMatch;
|
|
3739
|
+
RE_MATCH_EMOJIS.lastIndex = 0;
|
|
3740
|
+
const emojiMatches = [...textContent.matchAll(RE_MATCH_EMOJIS)];
|
|
3741
|
+
if (!emojiMatches.length)
|
|
3742
|
+
return fullMatch;
|
|
3743
|
+
let newTextContent = textContent;
|
|
3744
|
+
for (const match of emojiMatches) {
|
|
3745
|
+
const emoji = match[0];
|
|
3746
|
+
const svg = emojiSvgMap.get(emoji);
|
|
3747
|
+
if (svg) {
|
|
3748
|
+
hasChanges = true;
|
|
3749
|
+
const escaped = emoji.replace(RE_SPECIAL_REGEX_CHARS, "\\$&");
|
|
3750
|
+
newTextContent = newTextContent.replace(new RegExp(escaped, "g"), svg);
|
|
3751
|
+
}
|
|
3752
|
+
}
|
|
3753
|
+
return `>${newTextContent}<`;
|
|
3754
|
+
});
|
|
3585
3755
|
}
|
|
3586
|
-
}
|
|
3587
|
-
if (
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
newTextContent = newTextContent.replace(new RegExp(escaped, "g"), svg);
|
|
3756
|
+
}
|
|
3757
|
+
if (template.includes("<Icon") || template.includes("<UIcon")) {
|
|
3758
|
+
const prefixes = /* @__PURE__ */ new Set();
|
|
3759
|
+
const iconElements = [];
|
|
3760
|
+
const wrappedTemplate = `<div>${template}</div>`;
|
|
3761
|
+
const doc = parse$1(wrappedTemplate);
|
|
3762
|
+
walkSync(doc, (node, parent, index) => {
|
|
3763
|
+
if (node.type === ELEMENT_NODE$2) {
|
|
3764
|
+
const el = node;
|
|
3765
|
+
if (el.name === "Icon" || el.name === "UIcon") {
|
|
3766
|
+
const iconName = el.attributes.name;
|
|
3767
|
+
if (iconName && !iconName.includes("{")) {
|
|
3768
|
+
const [prefix] = iconName.split(":");
|
|
3769
|
+
if (prefix)
|
|
3770
|
+
prefixes.add(prefix);
|
|
3771
|
+
iconElements.push({ node: el, parent, index });
|
|
3772
|
+
}
|
|
3604
3773
|
}
|
|
3605
3774
|
}
|
|
3606
|
-
return `>${newTextContent}<`;
|
|
3607
3775
|
});
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
const el = node;
|
|
3618
|
-
if (el.name === "Icon" || el.name === "UIcon") {
|
|
3776
|
+
const iconSets = /* @__PURE__ */ new Map();
|
|
3777
|
+
for (const prefix of prefixes) {
|
|
3778
|
+
const icons = await loadIconSet(prefix);
|
|
3779
|
+
if (icons)
|
|
3780
|
+
iconSets.set(prefix, icons);
|
|
3781
|
+
}
|
|
3782
|
+
if (iconElements.length > 0) {
|
|
3783
|
+
let anyReplaced = false;
|
|
3784
|
+
for (const { node: el, parent, index } of iconElements) {
|
|
3619
3785
|
const iconName = el.attributes.name;
|
|
3620
|
-
if (
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3786
|
+
if (!iconName)
|
|
3787
|
+
continue;
|
|
3788
|
+
const [prefix, name] = iconName.split(":");
|
|
3789
|
+
if (!prefix || !name)
|
|
3790
|
+
continue;
|
|
3791
|
+
const icons = iconSets.get(prefix);
|
|
3792
|
+
const iconData = icons?.icons?.[name];
|
|
3793
|
+
let svgHtml;
|
|
3794
|
+
if (iconData) {
|
|
3795
|
+
svgHtml = buildIconSvg(iconData, icons.width || 24, icons.height || 24, el.attributes);
|
|
3796
|
+
} else if (options.cssProvider?.resolveIcon) {
|
|
3797
|
+
const resolved = await options.cssProvider.resolveIcon(prefix, name);
|
|
3798
|
+
if (resolved)
|
|
3799
|
+
svgHtml = buildIconSvg(resolved, resolved.width, resolved.height, el.attributes);
|
|
3800
|
+
}
|
|
3801
|
+
if (!svgHtml)
|
|
3802
|
+
continue;
|
|
3803
|
+
anyReplaced = true;
|
|
3804
|
+
hasChanges = true;
|
|
3805
|
+
const svgDoc = parse$1(svgHtml);
|
|
3806
|
+
if ("children" in parent && Array.isArray(parent.children)) {
|
|
3807
|
+
parent.children[index] = svgDoc.children[0];
|
|
3625
3808
|
}
|
|
3626
3809
|
}
|
|
3810
|
+
if (anyReplaced) {
|
|
3811
|
+
const rendered = renderSync(doc);
|
|
3812
|
+
template = rendered.replace(RE_WRAPPER_DIV_START, "").replace(RE_WRAPPER_DIV_END, "");
|
|
3813
|
+
}
|
|
3627
3814
|
}
|
|
3628
|
-
});
|
|
3629
|
-
const iconSets = /* @__PURE__ */ new Map();
|
|
3630
|
-
for (const prefix of prefixes) {
|
|
3631
|
-
const icons = await loadIconSet(prefix);
|
|
3632
|
-
if (icons)
|
|
3633
|
-
iconSets.set(prefix, icons);
|
|
3634
3815
|
}
|
|
3635
|
-
if (
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
if (!prefix || !name)
|
|
3816
|
+
if (options.cssProvider?.resolveIcon) {
|
|
3817
|
+
const replacements = [];
|
|
3818
|
+
RE_ICON_ELEMENT.lastIndex = 0;
|
|
3819
|
+
let elMatch;
|
|
3820
|
+
while ((elMatch = RE_ICON_ELEMENT.exec(template)) !== null) {
|
|
3821
|
+
const [fullMatch, , attrsStr, classValue] = elMatch;
|
|
3822
|
+
if (!classValue)
|
|
3643
3823
|
continue;
|
|
3644
|
-
const
|
|
3645
|
-
|
|
3646
|
-
let
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3824
|
+
const classes = classValue.split(RE_WHITESPACE).filter(Boolean);
|
|
3825
|
+
let iconPrefix;
|
|
3826
|
+
let iconName;
|
|
3827
|
+
for (const cls of classes) {
|
|
3828
|
+
const im = cls.match(ICON_CLASS_RE);
|
|
3829
|
+
if (im?.[1] && im[2]) {
|
|
3830
|
+
iconPrefix = im[1];
|
|
3831
|
+
iconName = im[2];
|
|
3832
|
+
break;
|
|
3833
|
+
}
|
|
3653
3834
|
}
|
|
3654
|
-
if (!
|
|
3835
|
+
if (!iconPrefix || !iconName)
|
|
3655
3836
|
continue;
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3837
|
+
const resolved = await options.cssProvider.resolveIcon(iconPrefix, iconName);
|
|
3838
|
+
if (!resolved)
|
|
3839
|
+
continue;
|
|
3840
|
+
const attrs = {};
|
|
3841
|
+
RE_HTML_ATTR.lastIndex = 0;
|
|
3842
|
+
let am;
|
|
3843
|
+
while ((am = RE_HTML_ATTR.exec(attrsStr)) !== null) {
|
|
3844
|
+
if (am[1] && am[1] !== "class" && am[1] !== ":style" && am[1] !== "v-bind:style")
|
|
3845
|
+
attrs[am[1]] = am[2] ?? "";
|
|
3661
3846
|
}
|
|
3847
|
+
const otherClasses = classes.filter((c) => !isIconClass(c));
|
|
3848
|
+
if (otherClasses.length > 0)
|
|
3849
|
+
attrs.class = otherClasses.join(" ");
|
|
3850
|
+
const svgHtml = buildIconSvg(resolved, resolved.width, resolved.height, attrs);
|
|
3851
|
+
replacements.push({ from: fullMatch, to: svgHtml });
|
|
3662
3852
|
}
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3853
|
+
for (const { from, to } of replacements) {
|
|
3854
|
+
template = template.replace(from, to);
|
|
3855
|
+
hasChanges = true;
|
|
3666
3856
|
}
|
|
3667
3857
|
}
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
if (!classValue)
|
|
3676
|
-
continue;
|
|
3677
|
-
const classes = classValue.split(RE_WHITESPACE).filter(Boolean);
|
|
3678
|
-
let iconPrefix;
|
|
3679
|
-
let iconName;
|
|
3680
|
-
for (const cls of classes) {
|
|
3681
|
-
const im = cls.match(ICON_CLASS_RE);
|
|
3682
|
-
if (im?.[1] && im[2]) {
|
|
3683
|
-
iconPrefix = im[1];
|
|
3684
|
-
iconName = im[2];
|
|
3685
|
-
break;
|
|
3686
|
-
}
|
|
3687
|
-
}
|
|
3688
|
-
if (!iconPrefix || !iconName)
|
|
3689
|
-
continue;
|
|
3690
|
-
const resolved = await options.cssProvider.resolveIcon(iconPrefix, iconName);
|
|
3691
|
-
if (!resolved)
|
|
3692
|
-
continue;
|
|
3693
|
-
const attrs = {};
|
|
3694
|
-
RE_HTML_ATTR.lastIndex = 0;
|
|
3695
|
-
let am;
|
|
3696
|
-
while ((am = RE_HTML_ATTR.exec(attrsStr)) !== null) {
|
|
3697
|
-
if (am[1] && am[1] !== "class" && am[1] !== ":style" && am[1] !== "v-bind:style")
|
|
3698
|
-
attrs[am[1]] = am[2] ?? "";
|
|
3858
|
+
const rootAttrs = {};
|
|
3859
|
+
const rootAttrMatch = template.match(RE_ROOT_ELEMENT);
|
|
3860
|
+
if (rootAttrMatch?.[2]) {
|
|
3861
|
+
RE_DATA_ATTR.lastIndex = 0;
|
|
3862
|
+
for (const m of rootAttrMatch[2].matchAll(RE_DATA_ATTR)) {
|
|
3863
|
+
if (m[1] && m[2])
|
|
3864
|
+
rootAttrs[`data-${m[1]}`] = m[2];
|
|
3699
3865
|
}
|
|
3700
|
-
const otherClasses = classes.filter((c) => !isIconClass(c));
|
|
3701
|
-
if (otherClasses.length > 0)
|
|
3702
|
-
attrs.class = otherClasses.join(" ");
|
|
3703
|
-
const svgHtml = buildIconSvg(resolved, resolved.width, resolved.height, attrs);
|
|
3704
|
-
replacements.push({ from: fullMatch, to: svgHtml });
|
|
3705
|
-
}
|
|
3706
|
-
for (const { from, to } of replacements) {
|
|
3707
|
-
template = template.replace(from, to);
|
|
3708
|
-
hasChanges = true;
|
|
3709
|
-
}
|
|
3710
|
-
}
|
|
3711
|
-
const rootAttrs = {};
|
|
3712
|
-
const rootAttrMatch = template.match(RE_ROOT_ELEMENT);
|
|
3713
|
-
if (rootAttrMatch?.[2]) {
|
|
3714
|
-
RE_DATA_ATTR.lastIndex = 0;
|
|
3715
|
-
for (const m of rootAttrMatch[2].matchAll(RE_DATA_ATTR)) {
|
|
3716
|
-
if (m[1] && m[2])
|
|
3717
|
-
rootAttrs[`data-${m[1]}`] = m[2];
|
|
3718
3866
|
}
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3867
|
+
if (!rootAttrs["data-theme"] && options.colorPreference)
|
|
3868
|
+
rootAttrs["data-theme"] = options.colorPreference;
|
|
3869
|
+
const hasDynamicDataTheme = !!(rootAttrMatch?.[2] && RE_DYNAMIC_DATA_THEME.test(` ${rootAttrMatch[2]}`));
|
|
3870
|
+
if (options.cssProvider) {
|
|
3871
|
+
options.beforeCssResolve?.();
|
|
3872
|
+
try {
|
|
3873
|
+
const fullCode = code.slice(0, templateStart) + template + code.slice(templateEnd);
|
|
3874
|
+
const result = await transformVueTemplate(fullCode, {
|
|
3875
|
+
resolveStyles: async (classes) => {
|
|
3876
|
+
const supported = classes.filter((cls) => !isUnsupportedClass(cls) && !isIconClass(cls));
|
|
3877
|
+
const unsupported = classes.filter((cls) => isUnsupportedClass(cls));
|
|
3878
|
+
if (unsupported.length > 0) {
|
|
3879
|
+
const componentName2 = id.split("/").pop();
|
|
3880
|
+
logger.warn(`[nuxt-og-image] ${componentName2}: Filtered unsupported classes: ${unsupported.join(", ")}`);
|
|
3881
|
+
}
|
|
3882
|
+
const componentName = id.split("/").pop()?.replace(RE_FILE_EXTENSION, "");
|
|
3883
|
+
if (hasDynamicDataTheme) {
|
|
3884
|
+
const lightAttrs = { ...rootAttrs, "data-theme": "light" };
|
|
3885
|
+
const darkAttrs = { ...rootAttrs, "data-theme": "dark" };
|
|
3886
|
+
const [lightResolved, darkResolved] = await Promise.all([
|
|
3887
|
+
options.cssProvider.resolveClassesToStyles(supported, componentName, lightAttrs),
|
|
3888
|
+
options.cssProvider.resolveClassesToStyles(supported, componentName, darkAttrs)
|
|
3889
|
+
]);
|
|
3890
|
+
return mergeThemePairs(supported, lightResolved, darkResolved);
|
|
3891
|
+
}
|
|
3892
|
+
return options.cssProvider.resolveClassesToStyles(supported, componentName, rootAttrs);
|
|
3734
3893
|
}
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
options.cssProvider.resolveClassesToStyles(supported, componentName, darkAttrs)
|
|
3742
|
-
]);
|
|
3743
|
-
return mergeThemePairs(supported, lightResolved, darkResolved);
|
|
3894
|
+
});
|
|
3895
|
+
if (result) {
|
|
3896
|
+
const newBounds = getOuterTemplateBounds(result.code);
|
|
3897
|
+
if (newBounds) {
|
|
3898
|
+
template = result.code.slice(newBounds.templateStart, newBounds.templateEnd);
|
|
3899
|
+
hasChanges = true;
|
|
3744
3900
|
}
|
|
3745
|
-
return options.cssProvider.resolveClassesToStyles(supported, componentName, rootAttrs);
|
|
3746
|
-
}
|
|
3747
|
-
});
|
|
3748
|
-
if (result) {
|
|
3749
|
-
const newBounds = getOuterTemplateBounds(result.code);
|
|
3750
|
-
if (newBounds) {
|
|
3751
|
-
template = result.code.slice(newBounds.templateStart, newBounds.templateEnd);
|
|
3752
|
-
hasChanges = true;
|
|
3753
3901
|
}
|
|
3902
|
+
} catch (err) {
|
|
3903
|
+
const componentName = id.split("/").pop();
|
|
3904
|
+
logger.warn(`[nuxt-og-image] ${componentName}: CSS template transform failed, using original template`, err.message);
|
|
3754
3905
|
}
|
|
3755
|
-
} catch (err) {
|
|
3756
|
-
const componentName = id.split("/").pop();
|
|
3757
|
-
logger.warn(`[nuxt-og-image] ${componentName}: CSS template transform failed, using original template`, err.message);
|
|
3758
3906
|
}
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
vars.set(name, def[2].trim());
|
|
3769
|
-
}
|
|
3770
|
-
}
|
|
3771
|
-
if (vars.size > 0) {
|
|
3772
|
-
const replacements = [];
|
|
3773
|
-
RE_NON_STYLE_VAR_ATTR.lastIndex = 0;
|
|
3774
|
-
template.replace(RE_NON_STYLE_VAR_ATTR, (match, attr, value) => {
|
|
3775
|
-
const resolved = resolveCssVars(value, vars);
|
|
3776
|
-
if (resolved !== value) {
|
|
3777
|
-
replacements.push({ from: match, to: "", attr, value: resolved });
|
|
3907
|
+
if (template.includes("var(")) {
|
|
3908
|
+
const vars = options.cssProvider?.getVars ? await options.cssProvider.getVars(rootAttrs) : /* @__PURE__ */ new Map();
|
|
3909
|
+
RE_INLINE_STYLE.lastIndex = 0;
|
|
3910
|
+
for (const m of template.matchAll(RE_INLINE_STYLE)) {
|
|
3911
|
+
RE_CSS_VAR_DEF.lastIndex = 0;
|
|
3912
|
+
for (const def of m[1].matchAll(RE_CSS_VAR_DEF)) {
|
|
3913
|
+
const name = `--${def[1]}`;
|
|
3914
|
+
if (!vars.has(name))
|
|
3915
|
+
vars.set(name, def[2].trim());
|
|
3778
3916
|
}
|
|
3779
|
-
return match;
|
|
3780
|
-
});
|
|
3781
|
-
for (const r of replacements) {
|
|
3782
|
-
let value = r.value;
|
|
3783
|
-
if (RE_COLOR_ATTR_NAME.test(r.attr)) {
|
|
3784
|
-
value = await downlevelColor(r.attr, value);
|
|
3785
|
-
}
|
|
3786
|
-
r.to = `${r.attr}="${escapeAttrValue(value)}"`;
|
|
3787
|
-
template = template.replace(r.from, r.to);
|
|
3788
|
-
hasChanges = true;
|
|
3789
3917
|
}
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
});
|
|
3798
|
-
for (const r of styleReplacements) {
|
|
3799
|
-
const declarations = r.content.split(";").filter(Boolean);
|
|
3800
|
-
const downleveled = [];
|
|
3801
|
-
for (const decl of declarations) {
|
|
3802
|
-
const colonIdx = decl.indexOf(":");
|
|
3803
|
-
if (colonIdx === -1) {
|
|
3804
|
-
downleveled.push(decl);
|
|
3805
|
-
continue;
|
|
3918
|
+
if (vars.size > 0) {
|
|
3919
|
+
const replacements = [];
|
|
3920
|
+
RE_NON_STYLE_VAR_ATTR.lastIndex = 0;
|
|
3921
|
+
template.replace(RE_NON_STYLE_VAR_ATTR, (match, attr, value) => {
|
|
3922
|
+
const resolved = resolveCssVars(value, vars);
|
|
3923
|
+
if (resolved !== value) {
|
|
3924
|
+
replacements.push({ from: match, to: "", attr, value: resolved });
|
|
3806
3925
|
}
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3926
|
+
return match;
|
|
3927
|
+
});
|
|
3928
|
+
for (const r of replacements) {
|
|
3929
|
+
let value = r.value;
|
|
3930
|
+
if (RE_COLOR_ATTR_NAME.test(r.attr)) {
|
|
3931
|
+
value = await downlevelColor(r.attr, value);
|
|
3813
3932
|
}
|
|
3814
|
-
|
|
3933
|
+
r.to = `${r.attr}="${escapeAttrValue(value)}"`;
|
|
3934
|
+
template = template.replace(r.from, r.to);
|
|
3935
|
+
hasChanges = true;
|
|
3815
3936
|
}
|
|
3816
|
-
const
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
if (srcPath.startsWith("data:") || srcPath.startsWith("http"))
|
|
3846
|
-
continue;
|
|
3847
|
-
let resolvedPath;
|
|
3848
|
-
if (srcPath.startsWith("/")) {
|
|
3849
|
-
resolvedPath = join(options.publicDir, srcPath);
|
|
3850
|
-
} else if (srcPath.startsWith("~/") || srcPath.startsWith("@/")) {
|
|
3851
|
-
resolvedPath = join(options.srcDir, srcPath.slice(2));
|
|
3852
|
-
} else if (!isAbsolute(srcPath)) {
|
|
3853
|
-
resolvedPath = resolve(componentDir, srcPath);
|
|
3854
|
-
} else {
|
|
3855
|
-
continue;
|
|
3937
|
+
const styleReplacements = [];
|
|
3938
|
+
RE_STYLE_VAR_ATTR.lastIndex = 0;
|
|
3939
|
+
template.replace(RE_STYLE_VAR_ATTR, (match, styleValue) => {
|
|
3940
|
+
const resolved = resolveCssVars(styleValue, vars);
|
|
3941
|
+
if (resolved !== styleValue)
|
|
3942
|
+
styleReplacements.push({ from: match, content: resolved });
|
|
3943
|
+
return match;
|
|
3944
|
+
});
|
|
3945
|
+
for (const r of styleReplacements) {
|
|
3946
|
+
const declarations = r.content.split(";").filter(Boolean);
|
|
3947
|
+
const downleveled = [];
|
|
3948
|
+
for (const decl of declarations) {
|
|
3949
|
+
const colonIdx = decl.indexOf(":");
|
|
3950
|
+
if (colonIdx === -1) {
|
|
3951
|
+
downleveled.push(decl);
|
|
3952
|
+
continue;
|
|
3953
|
+
}
|
|
3954
|
+
const prop = decl.slice(0, colonIdx).trim();
|
|
3955
|
+
let value = decl.slice(colonIdx + 1).trim();
|
|
3956
|
+
if (RE_COLOR_PROP_NAME.test(prop) && !value.includes("var(")) {
|
|
3957
|
+
value = await downlevelColor(prop, value);
|
|
3958
|
+
if (value.includes("color-mix("))
|
|
3959
|
+
value = resolveColorMix(value);
|
|
3960
|
+
}
|
|
3961
|
+
downleveled.push(`${prop}: ${value}`);
|
|
3962
|
+
}
|
|
3963
|
+
const to = `style="${escapeAttrValue(downleveled.join("; "))}"`;
|
|
3964
|
+
template = template.replace(r.from, to);
|
|
3965
|
+
hasChanges = true;
|
|
3856
3966
|
}
|
|
3857
|
-
|
|
3858
|
-
|
|
3967
|
+
RE_DYNAMIC_STYLE_VAR.lastIndex = 0;
|
|
3968
|
+
const dynamicReplacements = [];
|
|
3969
|
+
template.replace(RE_DYNAMIC_STYLE_VAR, (match, styleExpr) => {
|
|
3970
|
+
const resolved = resolveCssVars(styleExpr, vars);
|
|
3971
|
+
if (resolved !== styleExpr)
|
|
3972
|
+
dynamicReplacements.push({ from: match, to: `:style="${resolved}"` });
|
|
3973
|
+
return match;
|
|
3859
3974
|
});
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
let dataUri;
|
|
3864
|
-
if (ext === "svg") {
|
|
3865
|
-
const svgContent = fileBuffer.toString("utf-8");
|
|
3866
|
-
dataUri = `data:${mimeType},${encodeURIComponent(svgContent)}`;
|
|
3867
|
-
} else {
|
|
3868
|
-
const base64 = fileBuffer.toString("base64");
|
|
3869
|
-
dataUri = `data:${mimeType};base64,${base64}`;
|
|
3975
|
+
for (const r of dynamicReplacements) {
|
|
3976
|
+
template = template.replace(r.from, r.to);
|
|
3977
|
+
hasChanges = true;
|
|
3870
3978
|
}
|
|
3871
|
-
replacements.push({ from: fullMatch, to: `src="${dataUri}"` });
|
|
3872
|
-
hasChanges = true;
|
|
3873
|
-
}
|
|
3874
|
-
for (const { from, to } of replacements) {
|
|
3875
|
-
template = template.replace(from, to);
|
|
3876
3979
|
}
|
|
3877
3980
|
}
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
const
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
if (styles) {
|
|
3904
|
-
Object.assign(matchedStyles, styles);
|
|
3905
|
-
consumedClasses.add(cls);
|
|
3906
|
-
}
|
|
3907
|
-
}
|
|
3908
|
-
if (Object.keys(matchedStyles).length === 0)
|
|
3909
|
-
return;
|
|
3910
|
-
const existingStyle = el.attributes?.style || "";
|
|
3911
|
-
const existingProps = {};
|
|
3912
|
-
for (const decl of splitCssDeclarations(existingStyle)) {
|
|
3913
|
-
const [prop, ...valParts] = decl.split(":");
|
|
3914
|
-
const value = valParts.join(":").trim();
|
|
3915
|
-
if (prop?.trim() && value)
|
|
3916
|
-
existingProps[prop.trim()] = value;
|
|
3917
|
-
}
|
|
3918
|
-
const merged = { ...matchedStyles, ...existingProps };
|
|
3919
|
-
el.attributes.style = escapeAttrValue(
|
|
3920
|
-
Object.entries(merged).map(([p, v]) => `${p}:${v}`).join(";")
|
|
3921
|
-
);
|
|
3922
|
-
const remaining = elClasses.filter((c) => !consumedClasses.has(c));
|
|
3923
|
-
if (remaining.length > 0)
|
|
3924
|
-
el.attributes.class = remaining.join(" ");
|
|
3925
|
-
else
|
|
3926
|
-
delete el.attributes.class;
|
|
3927
|
-
if (el.attributes.viewbox) {
|
|
3928
|
-
el.attributes.viewBox = el.attributes.viewbox;
|
|
3929
|
-
delete el.attributes.viewbox;
|
|
3930
|
-
}
|
|
3981
|
+
if (!template.includes("data-no-inline")) {
|
|
3982
|
+
RE_IMAGE_SRC.lastIndex = 0;
|
|
3983
|
+
if (RE_IMAGE_SRC.test(template)) {
|
|
3984
|
+
RE_IMAGE_SRC.lastIndex = 0;
|
|
3985
|
+
const componentDir = dirname(id);
|
|
3986
|
+
const replacements = [];
|
|
3987
|
+
let match;
|
|
3988
|
+
while ((match = RE_IMAGE_SRC.exec(template)) !== null) {
|
|
3989
|
+
const [fullMatch, srcPath, ext] = match;
|
|
3990
|
+
if (!srcPath || !ext)
|
|
3991
|
+
continue;
|
|
3992
|
+
if (srcPath.startsWith("data:") || srcPath.startsWith("http"))
|
|
3993
|
+
continue;
|
|
3994
|
+
let resolvedPath;
|
|
3995
|
+
if (srcPath.startsWith("/")) {
|
|
3996
|
+
resolvedPath = join(options.publicDir, srcPath);
|
|
3997
|
+
} else if (srcPath.startsWith("~/") || srcPath.startsWith("@/")) {
|
|
3998
|
+
resolvedPath = join(options.srcDir, srcPath.slice(2));
|
|
3999
|
+
} else if (!isAbsolute(srcPath)) {
|
|
4000
|
+
resolvedPath = resolve(componentDir, srcPath);
|
|
4001
|
+
} else {
|
|
4002
|
+
continue;
|
|
4003
|
+
}
|
|
4004
|
+
const fileBuffer = await readFile(resolvedPath).catch(() => {
|
|
4005
|
+
return null;
|
|
3931
4006
|
});
|
|
3932
|
-
|
|
4007
|
+
if (!fileBuffer)
|
|
4008
|
+
continue;
|
|
4009
|
+
const mimeType = getMimeType(ext);
|
|
4010
|
+
let dataUri;
|
|
4011
|
+
if (ext === "svg") {
|
|
4012
|
+
const svgContent = fileBuffer.toString("utf-8");
|
|
4013
|
+
dataUri = `data:${mimeType},${encodeURIComponent(svgContent)}`;
|
|
4014
|
+
} else {
|
|
4015
|
+
const base64 = fileBuffer.toString("base64");
|
|
4016
|
+
dataUri = `data:${mimeType};base64,${base64}`;
|
|
4017
|
+
}
|
|
4018
|
+
replacements.push({ from: fullMatch, to: `src="${dataUri}"` });
|
|
3933
4019
|
hasChanges = true;
|
|
3934
4020
|
}
|
|
4021
|
+
for (const { from, to } of replacements) {
|
|
4022
|
+
template = template.replace(from, to);
|
|
4023
|
+
}
|
|
3935
4024
|
}
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
|
|
4025
|
+
}
|
|
4026
|
+
{
|
|
4027
|
+
const { descriptor } = parse(code);
|
|
4028
|
+
if (descriptor.styles.length > 0) {
|
|
4029
|
+
for (const styleBlock of descriptor.styles) {
|
|
4030
|
+
const rawCss = styleBlock.content;
|
|
4031
|
+
const scopeId = styleBlock.scoped ? rawCss.match(RE_SCOPED_DATA_V)?.[0] ?? null : null;
|
|
4032
|
+
const cleanCss = scopeId ? rawCss.replaceAll(scopeId, "") : rawCss;
|
|
4033
|
+
const simplified = await simplifyCss(cleanCss);
|
|
4034
|
+
const classMap = extractClassStyles(simplified, { skipPrefixes: [] });
|
|
4035
|
+
if (classMap.size > 0) {
|
|
4036
|
+
const wrappedTemplate = `<div>${template}</div>`;
|
|
4037
|
+
const doc = parse$1(wrappedTemplate);
|
|
4038
|
+
walkSync(doc, (node) => {
|
|
4039
|
+
if (node.type !== ELEMENT_NODE$2)
|
|
4040
|
+
return;
|
|
4041
|
+
const el = node;
|
|
4042
|
+
const classAttr = el.attributes?.class;
|
|
4043
|
+
if (!classAttr)
|
|
4044
|
+
return;
|
|
4045
|
+
const elClasses = classAttr.split(RE_WHITESPACE).filter(Boolean);
|
|
4046
|
+
const matchedStyles = {};
|
|
4047
|
+
const consumedClasses = /* @__PURE__ */ new Set();
|
|
4048
|
+
for (const cls of elClasses) {
|
|
4049
|
+
const styles = classMap.get(cls);
|
|
4050
|
+
if (styles) {
|
|
4051
|
+
Object.assign(matchedStyles, styles);
|
|
4052
|
+
consumedClasses.add(cls);
|
|
4053
|
+
}
|
|
4054
|
+
}
|
|
4055
|
+
if (Object.keys(matchedStyles).length === 0)
|
|
4056
|
+
return;
|
|
4057
|
+
const existingStyle = el.attributes?.style || "";
|
|
4058
|
+
const existingProps = {};
|
|
4059
|
+
for (const decl of splitCssDeclarations(existingStyle)) {
|
|
4060
|
+
const [prop, ...valParts] = decl.split(":");
|
|
4061
|
+
const value = valParts.join(":").trim();
|
|
4062
|
+
if (prop?.trim() && value)
|
|
4063
|
+
existingProps[prop.trim()] = value;
|
|
4064
|
+
}
|
|
4065
|
+
const merged = { ...matchedStyles, ...existingProps };
|
|
4066
|
+
el.attributes.style = escapeAttrValue(
|
|
4067
|
+
Object.entries(merged).map(([p, v]) => `${p}:${v}`).join(";")
|
|
4068
|
+
);
|
|
4069
|
+
const remaining = elClasses.filter((c) => !consumedClasses.has(c));
|
|
4070
|
+
if (remaining.length > 0)
|
|
4071
|
+
el.attributes.class = remaining.join(" ");
|
|
4072
|
+
else
|
|
4073
|
+
delete el.attributes.class;
|
|
4074
|
+
if (el.attributes.viewbox) {
|
|
4075
|
+
el.attributes.viewBox = el.attributes.viewbox;
|
|
4076
|
+
delete el.attributes.viewbox;
|
|
4077
|
+
}
|
|
4078
|
+
});
|
|
4079
|
+
template = renderSync(doc).replace(RE_WRAPPER_DIV_START, "").replace(RE_WRAPPER_DIV_END, "");
|
|
4080
|
+
hasChanges = true;
|
|
4081
|
+
}
|
|
4082
|
+
}
|
|
4083
|
+
for (const styleBlock of descriptor.styles) {
|
|
4084
|
+
const styleStart = code.indexOf(styleBlock.loc.source) - "<style".length;
|
|
4085
|
+
const fullTagStart = code.lastIndexOf("<style", styleStart + "<style".length);
|
|
4086
|
+
const fullTagEnd = code.indexOf("</style>", styleStart) + "</style>".length;
|
|
4087
|
+
if (fullTagStart >= 0 && fullTagEnd > fullTagStart) {
|
|
4088
|
+
s.remove(fullTagStart, fullTagEnd);
|
|
4089
|
+
}
|
|
3942
4090
|
}
|
|
3943
4091
|
}
|
|
3944
4092
|
}
|
|
4093
|
+
if (options.onFontRequirements) {
|
|
4094
|
+
extractFontRequirementsFromVue(code).then((reqs) => {
|
|
4095
|
+
options.onFontRequirements(id, reqs);
|
|
4096
|
+
});
|
|
4097
|
+
}
|
|
4098
|
+
if (!hasChanges)
|
|
4099
|
+
return;
|
|
4100
|
+
s.overwrite(templateStart, templateEnd, template);
|
|
4101
|
+
return {
|
|
4102
|
+
code: s.toString(),
|
|
4103
|
+
map: s.generateMap({ hires: true })
|
|
4104
|
+
};
|
|
3945
4105
|
}
|
|
3946
|
-
if (options.onFontRequirements) {
|
|
3947
|
-
extractFontRequirementsFromVue(code).then((reqs) => {
|
|
3948
|
-
options.onFontRequirements(id, reqs);
|
|
3949
|
-
});
|
|
3950
|
-
}
|
|
3951
|
-
if (!hasChanges)
|
|
3952
|
-
return;
|
|
3953
|
-
s.overwrite(templateStart, templateEnd, template);
|
|
3954
|
-
return {
|
|
3955
|
-
code: s.toString(),
|
|
3956
|
-
map: s.generateMap({ hires: true })
|
|
3957
|
-
};
|
|
3958
4106
|
}
|
|
3959
4107
|
};
|
|
3960
4108
|
});
|
|
@@ -4145,94 +4293,99 @@ const BUILTIN_COMPONENTS = /* @__PURE__ */ new Set([
|
|
|
4145
4293
|
const MAX_IMPORT_REWRITE_DEPTH = 5;
|
|
4146
4294
|
const RE_OG_IMAGE_QUERY = /\?og-image(?:-depth=(\d+))?$/;
|
|
4147
4295
|
const RE_VUE_IMPORT = /import\s+(\w+)\s+from\s+(['"])(.+?\.vue)\2/g;
|
|
4296
|
+
const RE_VUE_ID = /\.vue(?:\?|$)/;
|
|
4148
4297
|
const ComponentImportRewritePlugin = createUnplugin((options) => {
|
|
4149
4298
|
return {
|
|
4150
4299
|
name: "nuxt-og-image:component-import-rewrite",
|
|
4151
4300
|
enforce: "pre",
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
if (!descriptor.template?.ast) {
|
|
4168
|
-
return;
|
|
4169
|
-
}
|
|
4170
|
-
const usedComponents = /* @__PURE__ */ new Set();
|
|
4171
|
-
walkTemplateAst(descriptor.template.ast.children, (node) => {
|
|
4172
|
-
if (node.type === 1 && !HTML_ELEMENTS.has(node.tag) && !BUILTIN_COMPONENTS.has(node.tag)) {
|
|
4173
|
-
usedComponents.add(node.tag);
|
|
4301
|
+
transform: {
|
|
4302
|
+
filter: {
|
|
4303
|
+
id: RE_VUE_ID
|
|
4304
|
+
},
|
|
4305
|
+
handler(code, rawId) {
|
|
4306
|
+
const depthMatch = rawId.match(RE_OG_IMAGE_QUERY);
|
|
4307
|
+
const currentDepth = depthMatch?.[1] ? Number.parseInt(depthMatch[1]) : 0;
|
|
4308
|
+
if (depthMatch) {
|
|
4309
|
+
if (currentDepth >= MAX_IMPORT_REWRITE_DEPTH)
|
|
4310
|
+
return;
|
|
4311
|
+
} else {
|
|
4312
|
+
if (!rawId.endsWith(".vue"))
|
|
4313
|
+
return;
|
|
4314
|
+
if (!options.ogComponentPaths.some((dir) => rawId.startsWith(`${dir}/`) || rawId.startsWith(`${dir}\\`)))
|
|
4315
|
+
return;
|
|
4174
4316
|
}
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
const components = options.getComponents();
|
|
4180
|
-
const newImports = [];
|
|
4181
|
-
for (const tag of usedComponents) {
|
|
4182
|
-
if (tag === "Icon" || tag === "UIcon")
|
|
4183
|
-
continue;
|
|
4184
|
-
const match = components.find((c) => c.pascalName === tag || c.kebabName === tag);
|
|
4185
|
-
if (match?.filePath && match.filePath.endsWith(".vue")) {
|
|
4186
|
-
newImports.push({ name: tag, filePath: match.filePath });
|
|
4317
|
+
const nextQuery = `?og-image-depth=${currentDepth + 1}`;
|
|
4318
|
+
const { descriptor } = parse(code);
|
|
4319
|
+
if (!descriptor.template?.ast) {
|
|
4320
|
+
return;
|
|
4187
4321
|
}
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
const s = new MagicString(code);
|
|
4193
|
-
const rewritten = /* @__PURE__ */ new Set();
|
|
4194
|
-
const scriptBlock = descriptor.scriptSetup || descriptor.script;
|
|
4195
|
-
if (scriptBlock) {
|
|
4196
|
-
const scriptStart = scriptBlock.loc.start.offset;
|
|
4197
|
-
const scriptContent = scriptBlock.content;
|
|
4198
|
-
for (const m of scriptContent.matchAll(RE_VUE_IMPORT)) {
|
|
4199
|
-
const importName = m[1];
|
|
4200
|
-
if (newImports.some((i) => i.name === importName)) {
|
|
4201
|
-
const source = m[3];
|
|
4202
|
-
const matchStart = scriptStart + m.index + m[0].indexOf(source);
|
|
4203
|
-
s.overwrite(matchStart, matchStart + source.length, `${source}${nextQuery}`);
|
|
4204
|
-
rewritten.add(importName);
|
|
4322
|
+
const usedComponents = /* @__PURE__ */ new Set();
|
|
4323
|
+
walkTemplateAst(descriptor.template.ast.children, (node) => {
|
|
4324
|
+
if (node.type === 1 && !HTML_ELEMENTS.has(node.tag) && !BUILTIN_COMPONENTS.has(node.tag)) {
|
|
4325
|
+
usedComponents.add(node.tag);
|
|
4205
4326
|
}
|
|
4327
|
+
});
|
|
4328
|
+
if (usedComponents.size === 0) {
|
|
4329
|
+
return;
|
|
4206
4330
|
}
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4331
|
+
const components = options.getComponents();
|
|
4332
|
+
const newImports = [];
|
|
4333
|
+
for (const tag of usedComponents) {
|
|
4334
|
+
if (tag === "Icon" || tag === "UIcon")
|
|
4335
|
+
continue;
|
|
4336
|
+
const match = components.find((c) => c.pascalName === tag || c.kebabName === tag);
|
|
4337
|
+
if (match?.filePath && match.filePath.endsWith(".vue")) {
|
|
4338
|
+
newImports.push({ name: tag, filePath: match.filePath });
|
|
4339
|
+
}
|
|
4340
|
+
}
|
|
4341
|
+
if (newImports.length === 0) {
|
|
4342
|
+
return;
|
|
4343
|
+
}
|
|
4344
|
+
const s = new MagicString(code);
|
|
4345
|
+
const rewritten = /* @__PURE__ */ new Set();
|
|
4346
|
+
const scriptBlock = descriptor.scriptSetup || descriptor.script;
|
|
4347
|
+
if (scriptBlock) {
|
|
4348
|
+
const scriptStart = scriptBlock.loc.start.offset;
|
|
4349
|
+
const scriptContent = scriptBlock.content;
|
|
4350
|
+
for (const m of scriptContent.matchAll(RE_VUE_IMPORT)) {
|
|
4351
|
+
const importName = m[1];
|
|
4352
|
+
if (newImports.some((i) => i.name === importName)) {
|
|
4353
|
+
const source = m[3];
|
|
4354
|
+
const matchStart = scriptStart + m.index + m[0].indexOf(source);
|
|
4355
|
+
s.overwrite(matchStart, matchStart + source.length, `${source}${nextQuery}`);
|
|
4356
|
+
rewritten.add(importName);
|
|
4357
|
+
}
|
|
4358
|
+
}
|
|
4359
|
+
}
|
|
4360
|
+
const imports = newImports.filter((i) => !rewritten.has(i.name));
|
|
4361
|
+
if (imports.length === 0) {
|
|
4362
|
+
return {
|
|
4363
|
+
code: s.toString(),
|
|
4364
|
+
map: s.generateMap({ hires: true })
|
|
4365
|
+
};
|
|
4366
|
+
}
|
|
4367
|
+
const importStatements = imports.map((i) => `import ${i.name} from '${i.filePath}${nextQuery}'`).join("\n");
|
|
4368
|
+
if (descriptor.scriptSetup) {
|
|
4369
|
+
const scriptSetupStart = code.indexOf("<script setup");
|
|
4370
|
+
const tagEnd = code.indexOf(">", scriptSetupStart) + 1;
|
|
4371
|
+
s.appendRight(tagEnd, `
|
|
4220
4372
|
${importStatements}`);
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4373
|
+
} else if (descriptor.script) {
|
|
4374
|
+
const scriptStart = code.indexOf("<script");
|
|
4375
|
+
const tagEnd = code.indexOf(">", scriptStart) + 1;
|
|
4376
|
+
s.appendRight(tagEnd, `
|
|
4225
4377
|
${importStatements}`);
|
|
4226
|
-
|
|
4227
|
-
|
|
4378
|
+
} else {
|
|
4379
|
+
s.prepend(`<script setup>
|
|
4228
4380
|
${importStatements}
|
|
4229
4381
|
<\/script>
|
|
4230
4382
|
`);
|
|
4383
|
+
}
|
|
4384
|
+
return {
|
|
4385
|
+
code: s.toString(),
|
|
4386
|
+
map: s.generateMap({ hires: true })
|
|
4387
|
+
};
|
|
4231
4388
|
}
|
|
4232
|
-
return {
|
|
4233
|
-
code: s.toString(),
|
|
4234
|
-
map: s.generateMap({ hires: true })
|
|
4235
|
-
};
|
|
4236
4389
|
}
|
|
4237
4390
|
};
|
|
4238
4391
|
});
|
|
@@ -4790,6 +4943,10 @@ declare module '#og-image/renderers/takumi' {
|
|
|
4790
4943
|
export default _default
|
|
4791
4944
|
}
|
|
4792
4945
|
|
|
4946
|
+
declare module '#og-image/bindings/fetch' {
|
|
4947
|
+
export { guardedFetch } from '${typesPath.replace("/types", "")}/server/og-image/bindings/fetch/web'
|
|
4948
|
+
}
|
|
4949
|
+
|
|
4793
4950
|
declare module '#og-image/emoji-transform' {
|
|
4794
4951
|
import type { OgImageRenderEventContext } from '${typesPath}'
|
|
4795
4952
|
export function getEmojiSvg(ctx: OgImageRenderEventContext, emoji: string): Promise<string | null>
|
|
@@ -4948,6 +5105,8 @@ const module$1 = defineNuxtModule({
|
|
|
4948
5105
|
return;
|
|
4949
5106
|
}
|
|
4950
5107
|
const nitroCompatibility = setupNitroRuntimeCompatibility(nuxt);
|
|
5108
|
+
if (nuxt.options.dev && nitroCompatibility._tag === "nitro-v2")
|
|
5109
|
+
nuxt.options.nitro.noExternals ??= false;
|
|
4951
5110
|
const setRuntimeAlias = (id, path) => {
|
|
4952
5111
|
nuxt.options.alias[id] = path;
|
|
4953
5112
|
nuxt.options.nitro.alias ||= {};
|
|
@@ -5032,6 +5191,7 @@ Or pin to a specific version in package.json:
|
|
|
5032
5191
|
setRuntimeAlias("#og-image-cache", resolve("./runtime/server/og-image/cache/lru"));
|
|
5033
5192
|
const preset = resolveOgImagePreset(nuxt.options.nitro);
|
|
5034
5193
|
const targetCompatibility = getPresetNitroPresetCompatibility(preset);
|
|
5194
|
+
setRuntimeAlias("#og-image/bindings/fetch", resolve(`./runtime/server/og-image/bindings/fetch/${targetCompatibility.fetch ?? "node"}`));
|
|
5035
5195
|
const runtimeCompatibilityMeta = {};
|
|
5036
5196
|
const normalizedPreset = preset.replace(RE_LEGACY_SUFFIX, "");
|
|
5037
5197
|
const isCloudflareWorkers = ["cloudflare", "cloudflare-module", "cloudflare-durable"].includes(normalizedPreset);
|
|
@@ -5284,7 +5444,7 @@ ${familyInfo}`);
|
|
|
5284
5444
|
}
|
|
5285
5445
|
});
|
|
5286
5446
|
if (!nuxt.options.dev) {
|
|
5287
|
-
addBuildPlugin(TreeShakeComposablesPlugin, { server: true, client: true
|
|
5447
|
+
addBuildPlugin(TreeShakeComposablesPlugin, { server: true, client: true });
|
|
5288
5448
|
setRuntimeAlias("#og-image-cache", resolve("./runtime/server/og-image/cache/mock"));
|
|
5289
5449
|
}
|
|
5290
5450
|
}
|
|
@@ -5447,6 +5607,9 @@ ${familyInfo}`);
|
|
|
5447
5607
|
name: "getOgImagePath",
|
|
5448
5608
|
from: resolve("./runtime/app/utils")
|
|
5449
5609
|
});
|
|
5610
|
+
addServerImports([
|
|
5611
|
+
{ name: "getOgImageUrl", from: resolve("./runtime/server/nitro") }
|
|
5612
|
+
]);
|
|
5450
5613
|
const basePluginPath = `./runtime/app/plugins${config.zeroRuntime ? "/__zero-runtime" : ""}`;
|
|
5451
5614
|
addPlugin({ mode: "server", src: resolve(`${basePluginPath}/route-rule-og-image.server`) });
|
|
5452
5615
|
addPlugin({ mode: "server", src: resolve(`${basePluginPath}/og-image-canonical-urls.server`) });
|
|
@@ -5893,6 +6056,7 @@ export const staticFontCacheDir = ${JSON.stringify(getStaticFontCacheDir(nuxt.op
|
|
|
5893
6056
|
colorPreference = colorModeOptions.fallback;
|
|
5894
6057
|
if (!colorPreference || colorPreference === "system")
|
|
5895
6058
|
colorPreference = "light";
|
|
6059
|
+
const includeTwitter = config.includeTwitter ?? nuxt.options.seo?.automaticTwitterTags ?? true;
|
|
5896
6060
|
const runtimeConfig = {
|
|
5897
6061
|
version,
|
|
5898
6062
|
// binding options
|
|
@@ -5902,6 +6066,7 @@ export const staticFontCacheDir = ${JSON.stringify(getStaticFontCacheDir(nuxt.op
|
|
|
5902
6066
|
publicStoragePath: `root${publicDirAbs.replace(nuxt.options.rootDir, "").replaceAll("/", ":")}`,
|
|
5903
6067
|
defaults: config.defaults,
|
|
5904
6068
|
debug: config.debug,
|
|
6069
|
+
includeTwitter,
|
|
5905
6070
|
// avoid adding credentials
|
|
5906
6071
|
baseCacheKey,
|
|
5907
6072
|
buildCacheDir,
|
|
@@ -5943,6 +6108,7 @@ export const staticFontCacheDir = ${JSON.stringify(getStaticFontCacheDir(nuxt.op
|
|
|
5943
6108
|
...nuxt.options.runtimeConfig.public,
|
|
5944
6109
|
"nuxt-og-image": {
|
|
5945
6110
|
defaults: runtimeConfig.defaults,
|
|
6111
|
+
includeTwitter: runtimeConfig.includeTwitter,
|
|
5946
6112
|
hasServerRuntime: !nuxt.options._generate && !nuxt.options.nitro?.static
|
|
5947
6113
|
}
|
|
5948
6114
|
};
|