@monkeyplus/flow 6.0.6 → 6.0.7
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/modules/content/module.mjs +17 -2
- package/modules/content/query.d.ts +25 -0
- package/modules/content/query.mjs +105 -19
- package/modules/content/runtime/client.d.ts +2 -0
- package/modules/content/runtime/client.mjs +1 -0
- package/modules/images/ipx.d.ts +5 -0
- package/modules/images/ipx.mjs +31 -0
- package/modules/images/module.d.ts +4 -0
- package/modules/images/module.mjs +96 -0
- package/modules/images/plugin.d.ts +2 -0
- package/modules/images/plugin.mjs +19 -0
- package/modules/images/runtime/build.d.ts +6 -0
- package/modules/images/runtime/build.mjs +142 -0
- package/modules/images/runtime/helpers.d.ts +16 -0
- package/modules/images/runtime/helpers.mjs +45 -0
- package/modules/images/runtime/image.d.ts +7 -0
- package/modules/images/runtime/image.mjs +235 -0
- package/modules/images/runtime/renames.d.ts +2 -0
- package/modules/images/runtime/renames.mjs +79 -0
- package/modules/images/runtime/server.d.ts +3 -0
- package/modules/images/runtime/server.mjs +68 -0
- package/modules/images/runtime/types.d.ts +77 -0
- package/modules/images/runtime/types.mjs +0 -0
- package/package.json +7 -1
- package/server/lib/pages.mjs +20 -21
- package/server/lib/render.mjs +16 -0
- package/server/renderer.d.ts +1 -1
- package/server/renderer.mjs +2 -1
- package/src/public/components.d.ts +3 -0
- package/src/public/components.mjs +3 -0
- package/src/public/index.d.ts +5 -3
- package/src/public/index.mjs +1 -0
- package/src/public/modules/images.d.ts +2 -0
- package/src/public/modules/images.mjs +1 -0
- package/src/public/query-content.d.ts +7 -0
- package/src/public/query-content.mjs +127 -0
- package/src/public/vite.mjs +18 -2
- package/src/runtime/components/MkImage.d.ts +188 -0
- package/src/runtime/components/MkImage.mjs +130 -0
- package/src/runtime/components/MkLink.d.ts +22 -0
- package/src/runtime/components/MkLink.mjs +72 -0
- package/src/runtime/components/MkPicture.d.ts +199 -0
- package/src/runtime/components/MkPicture.mjs +171 -0
- package/src/runtime/components/image-shared.d.ts +27 -0
- package/src/runtime/components/image-shared.mjs +51 -0
- package/src/runtime/config.d.ts +18 -0
- package/src/runtime/config.mjs +5 -2
- package/src/runtime/locale-routing.d.ts +12 -0
- package/src/runtime/locale-routing.mjs +93 -0
- package/src/runtime/page-discovery.mjs +8 -15
- package/src/runtime/pages.d.ts +16 -0
- package/src/runtime/virtual.d.ts +17 -0
- package/src/runtime/vue.mjs +6 -0
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { joinURL, encodeParam, encodePath } from "ufo";
|
|
2
|
+
import { getNormalName, getPreset, guessExt, parseSize } from "./helpers.mjs";
|
|
3
|
+
const modifierKeyMap = {
|
|
4
|
+
background: "b",
|
|
5
|
+
fit: "fit",
|
|
6
|
+
format: "f",
|
|
7
|
+
height: "h",
|
|
8
|
+
position: "pos",
|
|
9
|
+
quality: "q",
|
|
10
|
+
resize: "s",
|
|
11
|
+
width: "w"
|
|
12
|
+
};
|
|
13
|
+
function resolveIpxUrl(src, modifiers = {}, baseURL = "/_ipx") {
|
|
14
|
+
const normalizedModifiers = { ...modifiers };
|
|
15
|
+
if (normalizedModifiers.width && normalizedModifiers.height) {
|
|
16
|
+
normalizedModifiers.resize = `${normalizedModifiers.width}x${normalizedModifiers.height}`;
|
|
17
|
+
delete normalizedModifiers.width;
|
|
18
|
+
delete normalizedModifiers.height;
|
|
19
|
+
}
|
|
20
|
+
const params = Object.entries(normalizedModifiers).filter(([, value]) => value !== void 0 && value !== null && value !== "").map(([key, value]) => `${encodeParam(modifierKeyMap[key] || key)}_${encodeParam(String(value))}`).join(",") || "_";
|
|
21
|
+
const serializedModifiers = Object.entries(normalizedModifiers).filter(([, value]) => value !== void 0 && value !== null && value !== "").reduce((result, [key, value]) => {
|
|
22
|
+
result[key] = String(value);
|
|
23
|
+
return result;
|
|
24
|
+
}, {});
|
|
25
|
+
return {
|
|
26
|
+
modifiers: serializedModifiers,
|
|
27
|
+
url: joinURL(baseURL, params, encodePath(src))
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function isRemoteUrl(value) {
|
|
31
|
+
return value.startsWith("http://") || value.startsWith("https://");
|
|
32
|
+
}
|
|
33
|
+
function normalizeDirImages(dirImages) {
|
|
34
|
+
return dirImages.replace(/^\/+/, "").split("/")[0] || "images";
|
|
35
|
+
}
|
|
36
|
+
function getPathname(value) {
|
|
37
|
+
if (isRemoteUrl(value)) {
|
|
38
|
+
return new URL(value).pathname;
|
|
39
|
+
}
|
|
40
|
+
return value;
|
|
41
|
+
}
|
|
42
|
+
function getBaseDir(value, dirImages) {
|
|
43
|
+
return getPathname(value).replace(/^\/+/, "").split("/")[0] || normalizeDirImages(dirImages);
|
|
44
|
+
}
|
|
45
|
+
function getDirectory(value) {
|
|
46
|
+
const pathname = getPathname(value).split(/[?#]/, 1)[0] || "";
|
|
47
|
+
const lastSlash = pathname.lastIndexOf("/");
|
|
48
|
+
return lastSlash >= 0 ? pathname.slice(0, lastSlash) : "";
|
|
49
|
+
}
|
|
50
|
+
function resolveRemoteRename(input, domains = {}) {
|
|
51
|
+
if (!isRemoteUrl(input)) {
|
|
52
|
+
return void 0;
|
|
53
|
+
}
|
|
54
|
+
const inputUrl = new URL(input);
|
|
55
|
+
for (const [base, target] of Object.entries(domains)) {
|
|
56
|
+
const normalizedBase = decodeURIComponent(base);
|
|
57
|
+
if (!normalizedBase) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (normalizedBase.startsWith("http://") || normalizedBase.startsWith("https://")) {
|
|
61
|
+
if (input.startsWith(normalizedBase)) {
|
|
62
|
+
return input.replace(normalizedBase, target);
|
|
63
|
+
}
|
|
64
|
+
const baseUrl = new URL(normalizedBase);
|
|
65
|
+
if (inputUrl.origin === baseUrl.origin && inputUrl.pathname.startsWith(baseUrl.pathname)) {
|
|
66
|
+
const suffix = inputUrl.pathname.slice(baseUrl.pathname.length).replace(/^\/+/, "");
|
|
67
|
+
return joinURL(target, suffix);
|
|
68
|
+
}
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (inputUrl.host === normalizedBase || inputUrl.hostname === normalizedBase) {
|
|
72
|
+
return joinURL(target, inputUrl.pathname);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return void 0;
|
|
76
|
+
}
|
|
77
|
+
function resolveRenamePath(path, rename) {
|
|
78
|
+
const normalizedRename = rename?.trim();
|
|
79
|
+
if (!normalizedRename) {
|
|
80
|
+
return path;
|
|
81
|
+
}
|
|
82
|
+
const directory = getDirectory(path);
|
|
83
|
+
let resolvedPath = normalizedRename.startsWith("/") ? normalizedRename : joinURL(directory || "/", normalizedRename);
|
|
84
|
+
const extension = guessExt(getPathname(path));
|
|
85
|
+
if (!guessExt(resolvedPath) && extension) {
|
|
86
|
+
resolvedPath += extension;
|
|
87
|
+
}
|
|
88
|
+
return resolvedPath;
|
|
89
|
+
}
|
|
90
|
+
export function createImageResolver(imagesOptions, stateImages, runtime = {}) {
|
|
91
|
+
const getImage = ((input, modifiers = {}, options = {}) => {
|
|
92
|
+
if (typeof input !== "string" || !input.length) {
|
|
93
|
+
throw new TypeError(`input must be a non-empty string (received ${typeof input})`);
|
|
94
|
+
}
|
|
95
|
+
const preset = getPreset({ options: imagesOptions }, options?.preset);
|
|
96
|
+
const resolvedOptions = {
|
|
97
|
+
...options,
|
|
98
|
+
modifiers: {
|
|
99
|
+
...preset?.modifiers,
|
|
100
|
+
...modifiers
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
if (input.startsWith("data:")) {
|
|
104
|
+
return {
|
|
105
|
+
url: input
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
if (!input.startsWith("/") && !input.startsWith("https://") && !input.startsWith("http://")) {
|
|
109
|
+
input = joinURL(imagesOptions.dirImages, input);
|
|
110
|
+
}
|
|
111
|
+
const expectedFormat = resolvedOptions.modifiers?.format;
|
|
112
|
+
if (resolvedOptions.modifiers?.width) {
|
|
113
|
+
resolvedOptions.modifiers.width = parseSize(resolvedOptions.modifiers.width);
|
|
114
|
+
}
|
|
115
|
+
if (resolvedOptions.modifiers?.height) {
|
|
116
|
+
resolvedOptions.modifiers.height = parseSize(resolvedOptions.modifiers.height);
|
|
117
|
+
}
|
|
118
|
+
const image = resolveIpxUrl(input, resolvedOptions.modifiers || {}, imagesOptions.baseURL || "/_ipx");
|
|
119
|
+
image.format = image.format || expectedFormat || "";
|
|
120
|
+
image.src = input;
|
|
121
|
+
image.ext = image.format && `.${image.format}` || guessExt(input);
|
|
122
|
+
let baseDir = getBaseDir(input, imagesOptions.dirImages);
|
|
123
|
+
const originalExt = guessExt(input);
|
|
124
|
+
let renamedImage = stateImages.all[input]?.rename;
|
|
125
|
+
if (!renamedImage && imagesOptions.domains) {
|
|
126
|
+
renamedImage = resolveRemoteRename(input, imagesOptions.domains);
|
|
127
|
+
}
|
|
128
|
+
const replacedPath = decodeURIComponent(resolveRenamePath(renamedImage || input, resolvedOptions.rename));
|
|
129
|
+
if (isRemoteUrl(input) && replacedPath.startsWith("/")) {
|
|
130
|
+
baseDir = getBaseDir(replacedPath, imagesOptions.dirImages);
|
|
131
|
+
}
|
|
132
|
+
image.generate = decodeURIComponent(image.url).replace(decodeURIComponent(input), replacedPath).replace(`/${baseDir}`, "").replace("_ipx", baseDir).split("?")[0];
|
|
133
|
+
if (image.generate.startsWith("/")) {
|
|
134
|
+
image.generate = image.generate.replace(/\/+/g, "/");
|
|
135
|
+
}
|
|
136
|
+
if (originalExt) {
|
|
137
|
+
image.generate = image.generate.replace(originalExt, image.ext);
|
|
138
|
+
} else if (!image.generate.includes(".") && image.ext) {
|
|
139
|
+
image.generate += image.ext;
|
|
140
|
+
}
|
|
141
|
+
stateImages.generate[image.url] = image;
|
|
142
|
+
if (image.src && image.generate && image.modifiers) {
|
|
143
|
+
runtime.onGenerate?.({
|
|
144
|
+
...image,
|
|
145
|
+
src: image.src,
|
|
146
|
+
generate: image.generate,
|
|
147
|
+
modifiers: image.modifiers
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
const src = runtime.generateOutput && image.generate ? image.generate : image.url;
|
|
151
|
+
if (resolvedOptions._meta) {
|
|
152
|
+
const meta = { ...stateImages.all[input] || { name: input, alt: void 0, title: void 0 } };
|
|
153
|
+
if (!meta.alt) {
|
|
154
|
+
meta.alt = getNormalName(meta.name);
|
|
155
|
+
}
|
|
156
|
+
if (!meta.title) {
|
|
157
|
+
meta.title = getNormalName(meta.name);
|
|
158
|
+
}
|
|
159
|
+
delete meta.name;
|
|
160
|
+
return { src, ...meta };
|
|
161
|
+
}
|
|
162
|
+
return src;
|
|
163
|
+
});
|
|
164
|
+
getImage.getSizes = (input, opts) => {
|
|
165
|
+
const width = parseSize(opts.modifiers?.width);
|
|
166
|
+
const height = parseSize(opts.modifiers?.height);
|
|
167
|
+
const hwRatio = width && height ? height / width : 0;
|
|
168
|
+
const variants = [];
|
|
169
|
+
const sizes = {};
|
|
170
|
+
if (typeof opts.sizes === "string") {
|
|
171
|
+
for (const entry of opts.sizes.split(/[\s,]+/).filter(Boolean)) {
|
|
172
|
+
const sizeEntry = entry.split(":");
|
|
173
|
+
if (sizeEntry.length !== 2) {
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
sizes[sizeEntry[0].trim()] = sizeEntry[1].trim();
|
|
177
|
+
}
|
|
178
|
+
} else {
|
|
179
|
+
Object.assign(sizes, opts.sizes);
|
|
180
|
+
}
|
|
181
|
+
for (const key in sizes) {
|
|
182
|
+
const screenMaxWidth = imagesOptions.screens?.[key] || parseInt(key, 10);
|
|
183
|
+
let size = String(sizes[key]);
|
|
184
|
+
const isFluid = size.endsWith("vw");
|
|
185
|
+
if (!isFluid && /^\d+$/.test(size)) {
|
|
186
|
+
size = `${size}px`;
|
|
187
|
+
}
|
|
188
|
+
if (!isFluid && !size.endsWith("px")) {
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
let calculatedWidth = parseInt(size, 10);
|
|
192
|
+
if (!screenMaxWidth || !calculatedWidth) {
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
if (isFluid) {
|
|
196
|
+
calculatedWidth = Math.round(calculatedWidth / 100 * screenMaxWidth);
|
|
197
|
+
}
|
|
198
|
+
const calculatedHeight = hwRatio ? Math.round(calculatedWidth * hwRatio) : height;
|
|
199
|
+
const resizeConfig = opts.modifiers?.resize;
|
|
200
|
+
const resize = resizeConfig?.[screenMaxWidth] || resizeConfig?.["*"];
|
|
201
|
+
const positionConfig = opts.modifiers?.position;
|
|
202
|
+
const position = positionConfig?.[screenMaxWidth] || positionConfig?.["*"] || positionConfig;
|
|
203
|
+
const currentModifiers = {
|
|
204
|
+
...opts.modifiers,
|
|
205
|
+
width: calculatedWidth,
|
|
206
|
+
height: calculatedHeight,
|
|
207
|
+
resize,
|
|
208
|
+
position
|
|
209
|
+
};
|
|
210
|
+
if (resize) {
|
|
211
|
+
delete currentModifiers.width;
|
|
212
|
+
}
|
|
213
|
+
variants.push({
|
|
214
|
+
width: calculatedWidth,
|
|
215
|
+
size,
|
|
216
|
+
screenMaxWidth,
|
|
217
|
+
media: `(max-width: ${screenMaxWidth}px)`,
|
|
218
|
+
src: getImage(input, currentModifiers, opts)
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
variants.sort((left, right) => left.screenMaxWidth - right.screenMaxWidth);
|
|
222
|
+
const defaultVariant = variants[variants.length - 1];
|
|
223
|
+
if (defaultVariant) {
|
|
224
|
+
defaultVariant.media = "";
|
|
225
|
+
}
|
|
226
|
+
return {
|
|
227
|
+
sizes: variants.map((variant) => `${variant.media ? `${variant.media} ` : ""}${variant.size}`).join(", "),
|
|
228
|
+
srcset: variants.map((variant) => `${variant.src} ${variant.width}w`).join(", "),
|
|
229
|
+
src: defaultVariant?.src
|
|
230
|
+
};
|
|
231
|
+
};
|
|
232
|
+
return {
|
|
233
|
+
getImage
|
|
234
|
+
};
|
|
235
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
2
|
+
import { basename, dirname, extname, join, relative, resolve, sep } from "node:path";
|
|
3
|
+
import { joinURL } from "ufo";
|
|
4
|
+
const RENAME_FILE = "_rename";
|
|
5
|
+
function normalizePublicPath(path) {
|
|
6
|
+
return `/${path}`.replace(/\\/g, "/").replace(/\/+/g, "/");
|
|
7
|
+
}
|
|
8
|
+
function isHeaderLine(original, renamed) {
|
|
9
|
+
return original.toLowerCase().includes("nombre") && renamed.toLowerCase().includes("nuevo");
|
|
10
|
+
}
|
|
11
|
+
function walkRenameFiles(rootDir, currentDir = rootDir, files = []) {
|
|
12
|
+
if (!existsSync(currentDir)) {
|
|
13
|
+
return files;
|
|
14
|
+
}
|
|
15
|
+
for (const entry of readdirSync(currentDir, { withFileTypes: true })) {
|
|
16
|
+
const entryPath = join(currentDir, entry.name);
|
|
17
|
+
if (entry.isDirectory()) {
|
|
18
|
+
walkRenameFiles(rootDir, entryPath, files);
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
if (entry.isFile() && entry.name === RENAME_FILE) {
|
|
22
|
+
files.push(entryPath);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return files;
|
|
26
|
+
}
|
|
27
|
+
function toPublicImagePath(rootDir, manifestDir, fileName) {
|
|
28
|
+
const relativeDir = relative(rootDir, manifestDir).split(sep).join("/");
|
|
29
|
+
return normalizePublicPath(joinURL(relativeDir, fileName));
|
|
30
|
+
}
|
|
31
|
+
function parseRenameLine(rootDir, filePath, line) {
|
|
32
|
+
const trimmed = line.trim();
|
|
33
|
+
if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("//")) {
|
|
34
|
+
return void 0;
|
|
35
|
+
}
|
|
36
|
+
const [original, renamed, title, ...altParts] = trimmed.split(/\s+/);
|
|
37
|
+
if (!original || !renamed || isHeaderLine(original, renamed)) {
|
|
38
|
+
return void 0;
|
|
39
|
+
}
|
|
40
|
+
const sourceExt = extname(original);
|
|
41
|
+
const renamedWithExt = extname(renamed) ? renamed : `${renamed}${sourceExt}`;
|
|
42
|
+
const manifestDir = dirname(filePath);
|
|
43
|
+
const sourcePath = toPublicImagePath(rootDir, manifestDir, original);
|
|
44
|
+
const renamedPath = toPublicImagePath(rootDir, manifestDir, renamedWithExt);
|
|
45
|
+
return {
|
|
46
|
+
key: sourcePath,
|
|
47
|
+
value: {
|
|
48
|
+
rename: renamedPath,
|
|
49
|
+
name: basename(renamedWithExt, extname(renamedWithExt)),
|
|
50
|
+
title,
|
|
51
|
+
alt: altParts.length ? altParts.join(" ") : void 0
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function readRenameDirectory(rootDir) {
|
|
56
|
+
const entries = {};
|
|
57
|
+
for (const filePath of walkRenameFiles(resolve(rootDir))) {
|
|
58
|
+
const raw = readFileSync(filePath, "utf8");
|
|
59
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
60
|
+
const parsed = parseRenameLine(rootDir, filePath, line);
|
|
61
|
+
if (!parsed) {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
entries[parsed.key] = parsed.value;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return entries;
|
|
68
|
+
}
|
|
69
|
+
export function loadImageRenames(sources) {
|
|
70
|
+
return sources.reduce((collection, source) => {
|
|
71
|
+
if (!source || !existsSync(source)) {
|
|
72
|
+
return collection;
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
...collection,
|
|
76
|
+
...readRenameDirectory(source)
|
|
77
|
+
};
|
|
78
|
+
}, {});
|
|
79
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { FlowImagesRuntimeConfig, FlowImageRuntimeUtils } from './types.ts';
|
|
2
|
+
export declare function getFlowImageRuntimeUtils(config?: FlowImagesRuntimeConfig | undefined): FlowImageRuntimeUtils | undefined;
|
|
3
|
+
export declare function resetFlowImageRuntimeState(): void;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { appendFileSync, mkdirSync } from "node:fs";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
import { loadImageRenames } from "./renames.mjs";
|
|
5
|
+
import { createImageResolver } from "./image.mjs";
|
|
6
|
+
let runtimeConfigRequire;
|
|
7
|
+
let cachedKey;
|
|
8
|
+
let cachedUtils;
|
|
9
|
+
let generatedEntryKeys = /* @__PURE__ */ new Set();
|
|
10
|
+
function getFlowImagesRuntimeConfig() {
|
|
11
|
+
try {
|
|
12
|
+
runtimeConfigRequire ??= createRequire(import.meta.url);
|
|
13
|
+
const runtime = runtimeConfigRequire("nitro/runtime-config");
|
|
14
|
+
return runtime.useRuntimeConfig?.().flow?.images;
|
|
15
|
+
} catch {
|
|
16
|
+
return void 0;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export function getFlowImageRuntimeUtils(config = getFlowImagesRuntimeConfig()) {
|
|
20
|
+
if (!config) {
|
|
21
|
+
return void 0;
|
|
22
|
+
}
|
|
23
|
+
const cacheKey = JSON.stringify(config);
|
|
24
|
+
const useCache = process.env.NODE_ENV === "production";
|
|
25
|
+
if (useCache && cachedKey === cacheKey && cachedUtils) {
|
|
26
|
+
return cachedUtils;
|
|
27
|
+
}
|
|
28
|
+
const renameSources = [config.dirRenames, config.publicDir].filter(Boolean);
|
|
29
|
+
const images = {
|
|
30
|
+
all: loadImageRenames(renameSources),
|
|
31
|
+
generate: {}
|
|
32
|
+
};
|
|
33
|
+
const resolver = createImageResolver(config.options, images, {
|
|
34
|
+
generateOutput: config.generate,
|
|
35
|
+
onGenerate(image) {
|
|
36
|
+
if (!config.generatedManifestPath) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const dedupeKey = `${image.url}:${image.generate}`;
|
|
40
|
+
if (generatedEntryKeys.has(dedupeKey)) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
generatedEntryKeys.add(dedupeKey);
|
|
44
|
+
mkdirSync(dirname(config.generatedManifestPath), { recursive: true });
|
|
45
|
+
appendFileSync(config.generatedManifestPath, `${JSON.stringify(image)}
|
|
46
|
+
`, "utf8");
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
const utils = {
|
|
50
|
+
getImage: resolver.getImage,
|
|
51
|
+
getImageMeta(src) {
|
|
52
|
+
return images.all[src];
|
|
53
|
+
},
|
|
54
|
+
getImageOptions() {
|
|
55
|
+
return { ...config.options };
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
if (useCache) {
|
|
59
|
+
cachedKey = cacheKey;
|
|
60
|
+
cachedUtils = utils;
|
|
61
|
+
}
|
|
62
|
+
return utils;
|
|
63
|
+
}
|
|
64
|
+
export function resetFlowImageRuntimeState() {
|
|
65
|
+
generatedEntryKeys = /* @__PURE__ */ new Set();
|
|
66
|
+
cachedKey = void 0;
|
|
67
|
+
cachedUtils = void 0;
|
|
68
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
export interface FlowImagesModuleOptions {
|
|
2
|
+
dirRenames: string;
|
|
3
|
+
dirFiles: string[];
|
|
4
|
+
lazy: boolean;
|
|
5
|
+
screens: Record<string, number>;
|
|
6
|
+
domains?: Record<string, string>;
|
|
7
|
+
presets: Record<string, Record<string, unknown>>;
|
|
8
|
+
baseURL: string;
|
|
9
|
+
dirImages: string;
|
|
10
|
+
}
|
|
11
|
+
export type FlowImageOptions = Omit<FlowImagesModuleOptions, 'dirRenames' | 'dirFiles'>;
|
|
12
|
+
export interface FlowImageMeta {
|
|
13
|
+
rename?: string;
|
|
14
|
+
name: string;
|
|
15
|
+
alt?: string;
|
|
16
|
+
title?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface ImageModifiers {
|
|
19
|
+
width?: number;
|
|
20
|
+
height?: number;
|
|
21
|
+
fit?: string;
|
|
22
|
+
format?: string;
|
|
23
|
+
[key: string]: any;
|
|
24
|
+
}
|
|
25
|
+
export interface ImageOptions {
|
|
26
|
+
provider?: string;
|
|
27
|
+
preset?: string;
|
|
28
|
+
rename?: string;
|
|
29
|
+
modifiers?: Partial<ImageModifiers>;
|
|
30
|
+
_meta?: boolean;
|
|
31
|
+
[key: string]: any;
|
|
32
|
+
}
|
|
33
|
+
export interface ImageSizesOptions extends ImageOptions {
|
|
34
|
+
sizes: Record<string, string | number> | string;
|
|
35
|
+
}
|
|
36
|
+
export interface ImageSizes {
|
|
37
|
+
srcset: string;
|
|
38
|
+
sizes: string;
|
|
39
|
+
src?: string;
|
|
40
|
+
}
|
|
41
|
+
export interface ResolvedImage {
|
|
42
|
+
url: string;
|
|
43
|
+
format?: string;
|
|
44
|
+
src?: string;
|
|
45
|
+
ext?: string;
|
|
46
|
+
generate?: string;
|
|
47
|
+
modifiers?: Record<string, string>;
|
|
48
|
+
}
|
|
49
|
+
export interface GeneratedImageEntry extends ResolvedImage {
|
|
50
|
+
src: string;
|
|
51
|
+
generate: string;
|
|
52
|
+
modifiers: Record<string, string>;
|
|
53
|
+
}
|
|
54
|
+
export type GetImageFunction = ((input: string, modifiers?: Record<string, any>, options?: ImageOptions) => string | Record<string, unknown>) & {
|
|
55
|
+
getSizes: (input: string, options: ImageSizesOptions) => ImageSizes;
|
|
56
|
+
};
|
|
57
|
+
export interface FlowImagesState {
|
|
58
|
+
all: Record<string, FlowImageMeta>;
|
|
59
|
+
generate: Record<string, unknown>;
|
|
60
|
+
}
|
|
61
|
+
export interface FlowImagesRuntimeConfig {
|
|
62
|
+
dirRenames: string;
|
|
63
|
+
dirFiles: string[];
|
|
64
|
+
publicDir: string;
|
|
65
|
+
outputDir: string;
|
|
66
|
+
generatedManifestPath: string;
|
|
67
|
+
generatedCacheDir?: string;
|
|
68
|
+
generatedCacheManifestPath?: string;
|
|
69
|
+
generate: boolean;
|
|
70
|
+
netlifyCache: boolean;
|
|
71
|
+
options: FlowImageOptions;
|
|
72
|
+
}
|
|
73
|
+
export interface FlowImageRuntimeUtils {
|
|
74
|
+
getImage: GetImageFunction;
|
|
75
|
+
getImageMeta: (src: string) => FlowImageMeta | undefined;
|
|
76
|
+
getImageOptions: () => FlowImageOptions;
|
|
77
|
+
}
|
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@monkeyplus/flow",
|
|
3
|
-
"version": "6.0.
|
|
3
|
+
"version": "6.0.7",
|
|
4
4
|
"description": "@monkeyplus/flow package-first runtime with Vite, Nitro, Vue and a workspace playground.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"publishConfig": {
|
|
@@ -40,6 +40,10 @@
|
|
|
40
40
|
"types": "./modules/strapi/runtime/client.d.ts",
|
|
41
41
|
"import": "./modules/strapi/runtime/client.mjs"
|
|
42
42
|
},
|
|
43
|
+
"./modules/images": {
|
|
44
|
+
"types": "./src/public/modules/images.d.ts",
|
|
45
|
+
"import": "./src/public/modules/images.mjs"
|
|
46
|
+
},
|
|
43
47
|
"./modules/sitemap": {
|
|
44
48
|
"types": "./src/public/modules/sitemap.d.ts",
|
|
45
49
|
"import": "./src/public/modules/sitemap.mjs"
|
|
@@ -72,8 +76,10 @@
|
|
|
72
76
|
"@unhead/vue": "^3.1.0",
|
|
73
77
|
"@vitejs/plugin-vue": "^6.0.5",
|
|
74
78
|
"@vue/server-renderer": "^3.5.13",
|
|
79
|
+
"ipx": "^3.1.1",
|
|
75
80
|
"nitro": "3.0.260429-beta",
|
|
76
81
|
"std-env": "^4.0.0",
|
|
82
|
+
"ufo": "^1.6.1",
|
|
77
83
|
"unhead": "^3.1.0",
|
|
78
84
|
"unplugin-icons": "^23.0.1",
|
|
79
85
|
"vite": "^8.0.5",
|
package/server/lib/pages.mjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import pageDefinitions from "virtual:flow/pages";
|
|
3
|
+
import { getFlowImageRuntimeUtils } from "../../modules/images/runtime/server.mjs";
|
|
4
|
+
import { localizeRoutePattern, normalizePath, toPublicRoute } from "../../src/runtime/locale-routing.mjs";
|
|
3
5
|
const dynamicRouteCache = /* @__PURE__ */ new Map();
|
|
4
6
|
let runtimeConfigRequire;
|
|
5
7
|
function getFlowRuntimeConfig() {
|
|
@@ -11,12 +13,6 @@ function getFlowRuntimeConfig() {
|
|
|
11
13
|
return {};
|
|
12
14
|
}
|
|
13
15
|
}
|
|
14
|
-
function normalizePath(value) {
|
|
15
|
-
if (!value || value === "/") {
|
|
16
|
-
return "/";
|
|
17
|
-
}
|
|
18
|
-
return value.length > 1 && value.endsWith("/") ? value.slice(0, -1) : value;
|
|
19
|
-
}
|
|
20
16
|
function scorePattern(pattern) {
|
|
21
17
|
return normalizePath(pattern).split("/").filter(Boolean).reduce((score, segment) => {
|
|
22
18
|
if (segment === "**") {
|
|
@@ -69,6 +65,7 @@ function createLocale(code) {
|
|
|
69
65
|
};
|
|
70
66
|
}
|
|
71
67
|
function createContext(definition, locale, localePage, pathname, params, dynamic) {
|
|
68
|
+
const imageUtils = getFlowImageRuntimeUtils();
|
|
72
69
|
return {
|
|
73
70
|
dynamic,
|
|
74
71
|
locale,
|
|
@@ -82,11 +79,12 @@ function createContext(definition, locale, localePage, pathname, params, dynamic
|
|
|
82
79
|
return locale;
|
|
83
80
|
},
|
|
84
81
|
async getUrl(namePage, localeCode, options) {
|
|
85
|
-
return await getUrl(namePage, localeCode, options);
|
|
82
|
+
return await getUrl(namePage, localeCode || locale.code, options);
|
|
86
83
|
},
|
|
87
84
|
async getUrls(withLocale, omitNoPublish) {
|
|
88
85
|
return await getUrls(withLocale, omitNoPublish);
|
|
89
|
-
}
|
|
86
|
+
},
|
|
87
|
+
...imageUtils || {}
|
|
90
88
|
}
|
|
91
89
|
};
|
|
92
90
|
}
|
|
@@ -109,9 +107,6 @@ function replacePath(pattern, url) {
|
|
|
109
107
|
}
|
|
110
108
|
return normalizePath(resolved.replaceAll("**", ""));
|
|
111
109
|
}
|
|
112
|
-
function toPublicRoutePattern(url) {
|
|
113
|
-
return normalizePath(url.replaceAll("*", ""));
|
|
114
|
-
}
|
|
115
110
|
function combineName(name, dynamicName) {
|
|
116
111
|
return `${name.replace(/\/*$/, "")}/${dynamicName.replace(/^\/*/, "")}`;
|
|
117
112
|
}
|
|
@@ -147,6 +142,7 @@ async function resolveDynamicEntry(definition, localeCode, localePage, ctx, path
|
|
|
147
142
|
export async function getUrl(namePage, localeCode, options = {}) {
|
|
148
143
|
const enabledLocales = getEnabledLocaleCodes();
|
|
149
144
|
const targetLocale = localeCode || enabledLocales[0];
|
|
145
|
+
const runtimeConfig = getFlowRuntimeConfig();
|
|
150
146
|
for (const definition of pageDefinitions) {
|
|
151
147
|
if (definition.name !== namePage) {
|
|
152
148
|
continue;
|
|
@@ -154,32 +150,34 @@ export async function getUrl(namePage, localeCode, options = {}) {
|
|
|
154
150
|
const localeEntries = targetLocale ? [targetLocale] : Object.keys(definition.locales);
|
|
155
151
|
for (const code of localeEntries) {
|
|
156
152
|
const localePage = definition.locales[code];
|
|
153
|
+
const localizedRoute = localizeRoutePattern(runtimeConfig.flow, code, localePage?.url || "/");
|
|
157
154
|
if (!localePage) {
|
|
158
155
|
continue;
|
|
159
156
|
}
|
|
160
157
|
if (options.url) {
|
|
161
|
-
return replacePath(
|
|
158
|
+
return replacePath(localizedRoute, options.url);
|
|
162
159
|
}
|
|
163
160
|
if (options.params) {
|
|
164
|
-
return replacePath(
|
|
161
|
+
return replacePath(localizedRoute, options.params);
|
|
165
162
|
}
|
|
166
163
|
if (localePage.dynamic && options.dynamicName) {
|
|
167
164
|
const locale = createLocale(code);
|
|
168
|
-
const ctx = createContext(definition, locale, localePage, localePage.url, {});
|
|
165
|
+
const ctx = createContext(definition, locale, localePage, toPublicRoute(runtimeConfig.flow, code, localePage.url), {});
|
|
169
166
|
const entries = await getDynamicEntries(definition, code, ctx);
|
|
170
167
|
const entry = entries.find((candidate) => candidate.name === options.dynamicName);
|
|
171
168
|
if (!entry) {
|
|
172
169
|
return void 0;
|
|
173
170
|
}
|
|
174
|
-
return replacePath(
|
|
171
|
+
return replacePath(localizedRoute, entry.url);
|
|
175
172
|
}
|
|
176
|
-
return
|
|
173
|
+
return toPublicRoute(runtimeConfig.flow, code, localePage.url);
|
|
177
174
|
}
|
|
178
175
|
}
|
|
179
176
|
return void 0;
|
|
180
177
|
}
|
|
181
178
|
export async function getUrls(withLocale = false, omitNoPublish = false) {
|
|
182
179
|
const enabledLocales = getEnabledLocaleCodes();
|
|
180
|
+
const runtimeConfig = getFlowRuntimeConfig();
|
|
183
181
|
const urls = [];
|
|
184
182
|
for (const definition of pageDefinitions) {
|
|
185
183
|
if (omitNoPublish && definition.noPublish) {
|
|
@@ -191,15 +189,15 @@ export async function getUrls(withLocale = false, omitNoPublish = false) {
|
|
|
191
189
|
}
|
|
192
190
|
if (localePage.dynamic) {
|
|
193
191
|
const locale = createLocale(localeCode);
|
|
194
|
-
const ctx = createContext(definition, locale, localePage, localePage.url, {});
|
|
192
|
+
const ctx = createContext(definition, locale, localePage, toPublicRoute(runtimeConfig.flow, localeCode, localePage.url), {});
|
|
195
193
|
const entries = await getDynamicEntries(definition, localeCode, ctx);
|
|
196
194
|
for (const entry of entries) {
|
|
197
|
-
const url2 = replacePath(localePage.url, entry.url);
|
|
195
|
+
const url2 = replacePath(localizeRoutePattern(runtimeConfig.flow, localeCode, localePage.url), entry.url);
|
|
198
196
|
urls.push(withLocale ? { url: url2, locale: localeCode, name: combineName(definition.name, entry.name) } : url2);
|
|
199
197
|
}
|
|
200
198
|
continue;
|
|
201
199
|
}
|
|
202
|
-
const url =
|
|
200
|
+
const url = toPublicRoute(runtimeConfig.flow, localeCode, localePage.url);
|
|
203
201
|
urls.push(withLocale ? { url, locale: localeCode, name: definition.name } : url);
|
|
204
202
|
}
|
|
205
203
|
}
|
|
@@ -214,14 +212,15 @@ export async function resolvePage(pathname) {
|
|
|
214
212
|
if (enabledLocales.length && !enabledLocales.includes(localeCode)) {
|
|
215
213
|
continue;
|
|
216
214
|
}
|
|
217
|
-
const
|
|
215
|
+
const localizedRoute = localizeRoutePattern(runtimeConfig.flow, localeCode, localePage.url);
|
|
216
|
+
const params = matchPattern(localizedRoute, pathname);
|
|
218
217
|
if (params) {
|
|
219
218
|
candidates.push({
|
|
220
219
|
definition,
|
|
221
220
|
localeCode,
|
|
222
221
|
localePage,
|
|
223
222
|
params,
|
|
224
|
-
score: scorePattern(
|
|
223
|
+
score: scorePattern(localizedRoute)
|
|
225
224
|
});
|
|
226
225
|
}
|
|
227
226
|
}
|
package/server/lib/render.mjs
CHANGED
|
@@ -5,7 +5,9 @@ import bases from "virtual:flow/bases";
|
|
|
5
5
|
import layouts from "virtual:flow/layouts";
|
|
6
6
|
import templates from "virtual:flow/templates";
|
|
7
7
|
import { createSSRApp, defineComponent, h } from "vue";
|
|
8
|
+
import { getFlowImageRuntimeUtils } from "../../modules/images/runtime/server.mjs";
|
|
8
9
|
import { installFlowVuePlugins } from "../../src/runtime/vue";
|
|
10
|
+
import { getUrl, getUrls } from "./pages.mjs";
|
|
9
11
|
function escapeHtml(value) {
|
|
10
12
|
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
11
13
|
}
|
|
@@ -104,6 +106,19 @@ async function renderBody(page) {
|
|
|
104
106
|
params: page.params,
|
|
105
107
|
path: page.pathname
|
|
106
108
|
};
|
|
109
|
+
const imageUtils = getFlowImageRuntimeUtils();
|
|
110
|
+
const utils = {
|
|
111
|
+
getLocale() {
|
|
112
|
+
return page.locale;
|
|
113
|
+
},
|
|
114
|
+
async getUrl(namePage, localeCode, options) {
|
|
115
|
+
return await getUrl(namePage, localeCode || page.locale.code, options);
|
|
116
|
+
},
|
|
117
|
+
async getUrls(withLocale, omitNoPublish) {
|
|
118
|
+
return await getUrls(withLocale, omitNoPublish);
|
|
119
|
+
},
|
|
120
|
+
...imageUtils || {}
|
|
121
|
+
};
|
|
107
122
|
const TemplateRoot = defineComponent({
|
|
108
123
|
render() {
|
|
109
124
|
const viewNode = h(TemplateComponent, context);
|
|
@@ -117,6 +132,7 @@ async function renderBody(page) {
|
|
|
117
132
|
});
|
|
118
133
|
const app = createSSRApp(TemplateRoot);
|
|
119
134
|
app.provide("context", context);
|
|
135
|
+
app.provide("utils", utils);
|
|
120
136
|
const head = createHead();
|
|
121
137
|
head.push(normalizeSeoToHead(page.head, fallbackTitle, fallbackDescription));
|
|
122
138
|
app.use(head);
|
package/server/renderer.d.ts
CHANGED