@monkeyplus/flow 6.0.6 → 6.0.8

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.
Files changed (57) hide show
  1. package/modules/content/module.mjs +17 -2
  2. package/modules/content/query.d.ts +25 -0
  3. package/modules/content/query.mjs +105 -19
  4. package/modules/content/runtime/client.d.ts +2 -0
  5. package/modules/content/runtime/client.mjs +1 -0
  6. package/modules/images/ipx.d.ts +2 -0
  7. package/modules/images/ipx.mjs +55 -0
  8. package/modules/images/module.d.ts +4 -0
  9. package/modules/images/module.mjs +146 -0
  10. package/modules/images/runtime/build.d.ts +6 -0
  11. package/modules/images/runtime/build.mjs +174 -0
  12. package/modules/images/runtime/helpers.d.ts +16 -0
  13. package/modules/images/runtime/helpers.mjs +45 -0
  14. package/modules/images/runtime/image.d.ts +7 -0
  15. package/modules/images/runtime/image.mjs +252 -0
  16. package/modules/images/runtime/renames.d.ts +2 -0
  17. package/modules/images/runtime/renames.mjs +79 -0
  18. package/modules/images/runtime/server.d.ts +3 -0
  19. package/modules/images/runtime/server.mjs +80 -0
  20. package/modules/images/runtime/types.d.ts +79 -0
  21. package/modules/images/runtime/types.mjs +0 -0
  22. package/modules/sitemap/handler.mjs +6 -7
  23. package/modules/sitemap/module.mjs +236 -22
  24. package/modules/sitemap/xml.d.ts +7 -0
  25. package/modules/sitemap/xml.mjs +87 -0
  26. package/package.json +7 -1
  27. package/server/lib/pages.mjs +20 -21
  28. package/server/lib/render.mjs +16 -0
  29. package/server/renderer.d.ts +1 -1
  30. package/server/renderer.mjs +2 -1
  31. package/src/public/components.d.ts +3 -0
  32. package/src/public/components.mjs +3 -0
  33. package/src/public/index.d.ts +5 -3
  34. package/src/public/index.mjs +1 -0
  35. package/src/public/modules/images.d.ts +2 -0
  36. package/src/public/modules/images.mjs +1 -0
  37. package/src/public/nitro.mjs +4 -1
  38. package/src/public/query-content.d.ts +7 -0
  39. package/src/public/query-content.mjs +130 -0
  40. package/src/public/vite.mjs +18 -2
  41. package/src/runtime/components/MkImage.d.ts +188 -0
  42. package/src/runtime/components/MkImage.mjs +131 -0
  43. package/src/runtime/components/MkLink.d.ts +22 -0
  44. package/src/runtime/components/MkLink.mjs +72 -0
  45. package/src/runtime/components/MkPicture.d.ts +199 -0
  46. package/src/runtime/components/MkPicture.mjs +172 -0
  47. package/src/runtime/components/image-shared.d.ts +28 -0
  48. package/src/runtime/components/image-shared.mjs +68 -0
  49. package/src/runtime/config.d.ts +22 -0
  50. package/src/runtime/config.mjs +5 -2
  51. package/src/runtime/locale-routing.d.ts +12 -0
  52. package/src/runtime/locale-routing.mjs +93 -0
  53. package/src/runtime/modules.mjs +1 -0
  54. package/src/runtime/page-discovery.mjs +8 -15
  55. package/src/runtime/pages.d.ts +16 -0
  56. package/src/runtime/virtual.d.ts +17 -0
  57. package/src/runtime/vue.mjs +6 -0
@@ -0,0 +1,45 @@
1
+ function extname(value) {
2
+ const fileName = value.split("/").pop() || value;
3
+ const index = fileName.lastIndexOf(".");
4
+ return index > 0 ? fileName.slice(index) : "";
5
+ }
6
+ function basename(value) {
7
+ return value.split("/").pop() || value;
8
+ }
9
+ export const screens = {
10
+ xs: 320,
11
+ sm: 640,
12
+ md: 768,
13
+ lg: 1024,
14
+ xl: 1280,
15
+ xxl: 1536
16
+ };
17
+ export function parseSize(input = "") {
18
+ if (typeof input === "number") {
19
+ return input;
20
+ }
21
+ if (typeof input === "string" && input.replace("px", "").match(/^\d+$/g)) {
22
+ return parseInt(input, 10);
23
+ }
24
+ return void 0;
25
+ }
26
+ export function getNormalName(originalName) {
27
+ const name = basename(originalName).replace(extname(originalName), "");
28
+ return name.replace(/-/g, " ");
29
+ }
30
+ export function getFileExtension(url = "") {
31
+ return url.split(/[?#]/).shift().split("/").pop().split(".").pop();
32
+ }
33
+ export function guessExt(input = "") {
34
+ const ext = input.split(".").pop()?.split("?")[0];
35
+ if (ext && /^[\w0-9]+$/.test(ext)) {
36
+ return `.${ext}`;
37
+ }
38
+ return "";
39
+ }
40
+ export function getPreset(ctx, name) {
41
+ if (!name) {
42
+ return {};
43
+ }
44
+ return ctx.options?.presets?.[name] || {};
45
+ }
@@ -0,0 +1,7 @@
1
+ import type { FlowImageOptions, FlowImagesState, GeneratedImageEntry, GetImageFunction } from './types.ts';
2
+ export declare function createImageResolver(imagesOptions: FlowImageOptions, stateImages: FlowImagesState, runtime?: {
3
+ generateOutput?: boolean;
4
+ onGenerate?: (image: GeneratedImageEntry) => void;
5
+ }): {
6
+ getImage: GetImageFunction;
7
+ };
@@ -0,0 +1,252 @@
1
+ import { encodeParam, encodePath, joinURL } 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 getRelativePath(value, baseDir) {
51
+ const pathname = getPathname(value).split(/[?#]/, 1)[0] || "";
52
+ const normalizedBaseDir = baseDir.replace(/^\/+/, "").replace(/\/+$/, "");
53
+ const normalizedPath = pathname.replace(/^\/+/, "");
54
+ if (!normalizedBaseDir) {
55
+ return normalizedPath;
56
+ }
57
+ if (normalizedPath === normalizedBaseDir) {
58
+ return "";
59
+ }
60
+ if (normalizedPath.startsWith(`${normalizedBaseDir}/`)) {
61
+ return normalizedPath.slice(normalizedBaseDir.length + 1);
62
+ }
63
+ return normalizedPath;
64
+ }
65
+ function getModifierSegment(modifiers = {}) {
66
+ return Object.entries(modifiers).filter(([, value]) => value !== void 0 && value !== null && value !== "").map(([key, value]) => `${encodeParam(modifierKeyMap[key] || key)}_${encodeParam(String(value))}`).join(",") || "_";
67
+ }
68
+ function resolveRemoteRename(input, domains = {}) {
69
+ if (!isRemoteUrl(input)) {
70
+ return void 0;
71
+ }
72
+ const inputUrl = new URL(input);
73
+ for (const [base, target] of Object.entries(domains)) {
74
+ const normalizedBase = decodeURIComponent(base);
75
+ if (!normalizedBase) {
76
+ continue;
77
+ }
78
+ if (normalizedBase.startsWith("http://") || normalizedBase.startsWith("https://")) {
79
+ if (input.startsWith(normalizedBase)) {
80
+ return input.replace(normalizedBase, target);
81
+ }
82
+ const baseUrl = new URL(normalizedBase);
83
+ if (inputUrl.origin === baseUrl.origin && inputUrl.pathname.startsWith(baseUrl.pathname)) {
84
+ const suffix = inputUrl.pathname.slice(baseUrl.pathname.length).replace(/^\/+/, "");
85
+ return joinURL(target, suffix);
86
+ }
87
+ continue;
88
+ }
89
+ if (inputUrl.host === normalizedBase || inputUrl.hostname === normalizedBase) {
90
+ return joinURL(target, inputUrl.pathname);
91
+ }
92
+ }
93
+ return void 0;
94
+ }
95
+ function resolveRenamePath(path, rename) {
96
+ const normalizedRename = rename?.trim();
97
+ if (!normalizedRename) {
98
+ return path;
99
+ }
100
+ const directory = getDirectory(path);
101
+ let resolvedPath = normalizedRename.startsWith("/") ? normalizedRename : joinURL(directory || "/", normalizedRename);
102
+ const extension = guessExt(getPathname(path));
103
+ if (!guessExt(resolvedPath) && extension) {
104
+ resolvedPath += extension;
105
+ }
106
+ return resolvedPath;
107
+ }
108
+ export function createImageResolver(imagesOptions, stateImages, runtime = {}) {
109
+ const getImage = ((input, modifiers = {}, options = {}) => {
110
+ if (typeof input !== "string" || !input.length) {
111
+ throw new TypeError(`input must be a non-empty string (received ${typeof input})`);
112
+ }
113
+ const preset = getPreset({ options: imagesOptions }, options?.preset);
114
+ const resolvedOptions = {
115
+ ...options,
116
+ modifiers: {
117
+ ...preset?.modifiers,
118
+ ...modifiers
119
+ }
120
+ };
121
+ if (input.startsWith("data:")) {
122
+ return {
123
+ url: input
124
+ };
125
+ }
126
+ if (!input.startsWith("/") && !input.startsWith("https://") && !input.startsWith("http://")) {
127
+ input = joinURL(imagesOptions.dirImages, input);
128
+ }
129
+ const expectedFormat = resolvedOptions.modifiers?.format;
130
+ if (resolvedOptions.modifiers?.width) {
131
+ resolvedOptions.modifiers.width = parseSize(resolvedOptions.modifiers.width);
132
+ }
133
+ if (resolvedOptions.modifiers?.height) {
134
+ resolvedOptions.modifiers.height = parseSize(resolvedOptions.modifiers.height);
135
+ }
136
+ const image = resolveIpxUrl(input, resolvedOptions.modifiers || {}, imagesOptions.baseURL || "/_ipx");
137
+ image.format = image.format || expectedFormat || "";
138
+ image.src = input;
139
+ image.ext = image.format && `.${image.format}` || guessExt(input);
140
+ let baseDir = getBaseDir(input, imagesOptions.dirImages);
141
+ const originalExt = guessExt(input);
142
+ let renamedImage = stateImages.all[input]?.rename;
143
+ if (!renamedImage && imagesOptions.domains) {
144
+ renamedImage = resolveRemoteRename(input, imagesOptions.domains);
145
+ }
146
+ const replacedPath = decodeURIComponent(resolveRenamePath(renamedImage || input, resolvedOptions.rename));
147
+ if (isRemoteUrl(input) && replacedPath.startsWith("/")) {
148
+ baseDir = getBaseDir(replacedPath, imagesOptions.dirImages);
149
+ }
150
+ const modifierSegment = getModifierSegment(image.modifiers);
151
+ const relativePath = getRelativePath(replacedPath, baseDir);
152
+ image.generate = joinURL(`/${baseDir}`, modifierSegment === "_" ? "" : modifierSegment, relativePath);
153
+ if (originalExt) {
154
+ image.generate = image.generate.replace(originalExt, image.ext);
155
+ } else if (!image.generate.includes(".") && image.ext) {
156
+ image.generate += image.ext;
157
+ }
158
+ stateImages.generate[image.url] = image;
159
+ if (image.src && image.generate && image.modifiers) {
160
+ runtime.onGenerate?.({
161
+ ...image,
162
+ src: image.src,
163
+ generate: image.generate,
164
+ modifiers: image.modifiers
165
+ });
166
+ }
167
+ const src = runtime.generateOutput && image.generate ? image.generate : image.url;
168
+ if (resolvedOptions._meta) {
169
+ const meta = { ...stateImages.all[input] || { name: input, alt: void 0, title: void 0 } };
170
+ if (!meta.alt) {
171
+ meta.alt = getNormalName(meta.name);
172
+ }
173
+ if (!meta.title) {
174
+ meta.title = getNormalName(meta.name);
175
+ }
176
+ delete meta.name;
177
+ return { src, ...meta };
178
+ }
179
+ return src;
180
+ });
181
+ getImage.getSizes = (input, opts) => {
182
+ const width = parseSize(opts.modifiers?.width);
183
+ const height = parseSize(opts.modifiers?.height);
184
+ const hwRatio = width && height ? height / width : 0;
185
+ const variants = [];
186
+ const sizes = {};
187
+ if (typeof opts.sizes === "string") {
188
+ for (const entry of opts.sizes.split(/[\s,]+/).filter(Boolean)) {
189
+ const sizeEntry = entry.split(":");
190
+ if (sizeEntry.length !== 2) {
191
+ continue;
192
+ }
193
+ sizes[sizeEntry[0].trim()] = sizeEntry[1].trim();
194
+ }
195
+ } else {
196
+ Object.assign(sizes, opts.sizes);
197
+ }
198
+ for (const key in sizes) {
199
+ const screenMaxWidth = imagesOptions.screens?.[key] || Number.parseInt(key, 10);
200
+ let size = String(sizes[key]);
201
+ const isFluid = size.endsWith("vw");
202
+ if (!isFluid && /^\d+$/.test(size)) {
203
+ size = `${size}px`;
204
+ }
205
+ if (!isFluid && !size.endsWith("px")) {
206
+ continue;
207
+ }
208
+ let calculatedWidth = Number.parseInt(size, 10);
209
+ if (!screenMaxWidth || !calculatedWidth) {
210
+ continue;
211
+ }
212
+ if (isFluid) {
213
+ calculatedWidth = Math.round(calculatedWidth / 100 * screenMaxWidth);
214
+ }
215
+ const calculatedHeight = hwRatio ? Math.round(calculatedWidth * hwRatio) : height;
216
+ const resizeConfig = opts.modifiers?.resize;
217
+ const resize = resizeConfig?.[screenMaxWidth] || resizeConfig?.["*"];
218
+ const positionConfig = opts.modifiers?.position;
219
+ const position = positionConfig?.[screenMaxWidth] || positionConfig?.["*"] || positionConfig;
220
+ const currentModifiers = {
221
+ ...opts.modifiers,
222
+ width: calculatedWidth,
223
+ height: calculatedHeight,
224
+ resize,
225
+ position
226
+ };
227
+ if (resize) {
228
+ delete currentModifiers.width;
229
+ }
230
+ variants.push({
231
+ width: calculatedWidth,
232
+ size,
233
+ screenMaxWidth,
234
+ media: `(max-width: ${screenMaxWidth}px)`,
235
+ src: getImage(input, currentModifiers, opts)
236
+ });
237
+ }
238
+ variants.sort((left, right) => left.screenMaxWidth - right.screenMaxWidth);
239
+ const defaultVariant = variants[variants.length - 1];
240
+ if (defaultVariant) {
241
+ defaultVariant.media = "";
242
+ }
243
+ return {
244
+ sizes: variants.map((variant) => `${variant.media ? `${variant.media} ` : ""}${variant.size}`).join(", "),
245
+ srcset: variants.map((variant) => `${variant.src} ${variant.width}w`).join(", "),
246
+ src: defaultVariant?.src
247
+ };
248
+ };
249
+ return {
250
+ getImage
251
+ };
252
+ }
@@ -0,0 +1,2 @@
1
+ import type { FlowImageMeta } from './types.ts';
2
+ export declare function loadImageRenames(sources: string[]): Record<string, FlowImageMeta>;
@@ -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 { FlowImageRuntimeUtils, FlowImagesRuntimeConfig } from './types.ts';
2
+ export declare function getFlowImageRuntimeUtils(config?: FlowImagesRuntimeConfig | undefined): FlowImageRuntimeUtils | undefined;
3
+ export declare function resetFlowImageRuntimeState(): void;
@@ -0,0 +1,80 @@
1
+ import { appendFileSync, mkdirSync } from "node:fs";
2
+ import { createRequire } from "node:module";
3
+ import { dirname } from "node:path";
4
+ import process from "node:process";
5
+ import { createImageResolver } from "./image.mjs";
6
+ import { loadImageRenames } from "./renames.mjs";
7
+ let runtimeConfigRequire;
8
+ let cachedKey;
9
+ let cachedUtils;
10
+ let generatedEntryKeys = /* @__PURE__ */ new Set();
11
+ function getEnvFlowImagesRuntimeConfig() {
12
+ const raw = process.env.FLOW_IMAGES_RUNTIME_CONFIG;
13
+ if (!raw) {
14
+ return void 0;
15
+ }
16
+ try {
17
+ return JSON.parse(raw);
18
+ } catch {
19
+ return void 0;
20
+ }
21
+ }
22
+ function getFlowImagesRuntimeConfig() {
23
+ try {
24
+ runtimeConfigRequire ??= createRequire(import.meta.url);
25
+ const runtime = runtimeConfigRequire("nitro/runtime-config");
26
+ return runtime.useRuntimeConfig?.().flow?.images || getEnvFlowImagesRuntimeConfig();
27
+ } catch {
28
+ return getEnvFlowImagesRuntimeConfig();
29
+ }
30
+ }
31
+ export function getFlowImageRuntimeUtils(config = getFlowImagesRuntimeConfig()) {
32
+ if (!config) {
33
+ return void 0;
34
+ }
35
+ const cacheKey = JSON.stringify(config);
36
+ const useCache = process.env.NODE_ENV === "production";
37
+ if (useCache && cachedKey === cacheKey && cachedUtils) {
38
+ return cachedUtils;
39
+ }
40
+ const renameSources = [config.dirRenames, config.publicDir].filter(Boolean);
41
+ const images = {
42
+ all: loadImageRenames(renameSources),
43
+ generate: {}
44
+ };
45
+ const resolver = createImageResolver(config.options, images, {
46
+ generateOutput: config.generate,
47
+ onGenerate(image) {
48
+ if (!config.generatedManifestPath) {
49
+ return;
50
+ }
51
+ const dedupeKey = `${image.url}:${image.generate}`;
52
+ if (generatedEntryKeys.has(dedupeKey)) {
53
+ return;
54
+ }
55
+ generatedEntryKeys.add(dedupeKey);
56
+ mkdirSync(dirname(config.generatedManifestPath), { recursive: true });
57
+ appendFileSync(config.generatedManifestPath, `${JSON.stringify(image)}
58
+ `, "utf8");
59
+ }
60
+ });
61
+ const utils = {
62
+ getImage: resolver.getImage,
63
+ getImageMeta(src) {
64
+ return images.all[src];
65
+ },
66
+ getImageOptions() {
67
+ return { ...config.options };
68
+ }
69
+ };
70
+ if (useCache) {
71
+ cachedKey = cacheKey;
72
+ cachedUtils = utils;
73
+ }
74
+ return utils;
75
+ }
76
+ export function resetFlowImageRuntimeState() {
77
+ generatedEntryKeys = /* @__PURE__ */ new Set();
78
+ cachedKey = void 0;
79
+ cachedUtils = void 0;
80
+ }
@@ -0,0 +1,79 @@
1
+ export interface FlowImagesModuleOptions {
2
+ dirRenames: string;
3
+ dirFiles: string[];
4
+ buildBatchSize: number;
5
+ lazy: boolean;
6
+ screens: Record<string, number>;
7
+ domains?: Record<string, string>;
8
+ presets: Record<string, Record<string, unknown>>;
9
+ baseURL: string;
10
+ dirImages: string;
11
+ }
12
+ export type FlowImageOptions = Omit<FlowImagesModuleOptions, 'dirRenames' | 'dirFiles' | 'buildBatchSize'>;
13
+ export interface FlowImageMeta {
14
+ rename?: string;
15
+ name: string;
16
+ alt?: string;
17
+ title?: string;
18
+ }
19
+ export interface ImageModifiers {
20
+ width?: number;
21
+ height?: number;
22
+ fit?: string;
23
+ format?: string;
24
+ [key: string]: any;
25
+ }
26
+ export interface ImageOptions {
27
+ provider?: string;
28
+ preset?: string;
29
+ rename?: string;
30
+ modifiers?: Partial<ImageModifiers>;
31
+ _meta?: boolean;
32
+ [key: string]: any;
33
+ }
34
+ export interface ImageSizesOptions extends ImageOptions {
35
+ sizes: Record<string, string | number> | string;
36
+ }
37
+ export interface ImageSizes {
38
+ srcset: string;
39
+ sizes: string;
40
+ src?: string;
41
+ }
42
+ export interface ResolvedImage {
43
+ url: string;
44
+ format?: string;
45
+ src?: string;
46
+ ext?: string;
47
+ generate?: string;
48
+ modifiers?: Record<string, string>;
49
+ }
50
+ export interface GeneratedImageEntry extends ResolvedImage {
51
+ src: string;
52
+ generate: string;
53
+ modifiers: Record<string, string>;
54
+ }
55
+ export type GetImageFunction = ((input: string, modifiers?: Record<string, any>, options?: ImageOptions) => string | Record<string, unknown>) & {
56
+ getSizes: (input: string, options: ImageSizesOptions) => ImageSizes;
57
+ };
58
+ export interface FlowImagesState {
59
+ all: Record<string, FlowImageMeta>;
60
+ generate: Record<string, unknown>;
61
+ }
62
+ export interface FlowImagesRuntimeConfig {
63
+ dirRenames: string;
64
+ dirFiles: string[];
65
+ buildBatchSize?: number;
66
+ publicDir: string;
67
+ outputDir: string;
68
+ generatedManifestPath: string;
69
+ generatedCacheDir?: string;
70
+ generatedCacheManifestPath?: string;
71
+ generate: boolean;
72
+ netlifyCache: boolean;
73
+ options: FlowImageOptions;
74
+ }
75
+ export interface FlowImageRuntimeUtils {
76
+ getImage: GetImageFunction;
77
+ getImageMeta: (src: string) => FlowImageMeta | undefined;
78
+ getImageOptions: () => FlowImageOptions;
79
+ }
File without changes
@@ -1,17 +1,16 @@
1
1
  import { defineEventHandler, getRequestURL, setHeader } from "nitro/h3";
2
2
  import { useRuntimeConfig } from "nitro/runtime-config";
3
3
  import { getUrls } from "../../server/lib/pages.mjs";
4
- function escapeXml(value) {
5
- return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&apos;");
6
- }
4
+ import { buildSitemapXml } from "./xml.mjs";
7
5
  export default defineEventHandler(async (event) => {
8
6
  const urls = await getUrls(true, true);
9
7
  const runtimeConfig = useRuntimeConfig();
10
8
  const origin = runtimeConfig.flow?.siteUrl || getRequestURL(event).origin;
11
- const xml = `<?xml version="1.0" encoding="UTF-8"?>
12
- <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
13
- ${urls.map((entry) => typeof entry === "string" ? ` <url><loc>${escapeXml(new URL(entry, origin).toString())}</loc></url>` : ` <url><loc>${escapeXml(new URL(entry.url, origin).toString())}</loc><xhtml:link rel="alternate" hreflang="${escapeXml(entry.locale)}" href="${escapeXml(new URL(entry.url, origin).toString())}" xmlns:xhtml="http://www.w3.org/1999/xhtml" /></url>`).join("\n")}
14
- </urlset>`;
9
+ const xml = buildSitemapXml(urls, origin, {
10
+ locales: runtimeConfig.flow?.locale?.locales || ["es-ec"],
11
+ language: runtimeConfig.flow?.locale?.language || "es",
12
+ location: runtimeConfig.flow?.locale?.location || "ec"
13
+ });
15
14
  setHeader(event, "content-type", "application/xml; charset=utf-8");
16
15
  return xml;
17
16
  });