@monkeyplus/flow 5.0.0-rc.99 → 6.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -0
- package/entry-server.d.ts +1 -0
- package/entry-server.mjs +5 -0
- package/modules/content/module.d.ts +6 -0
- package/modules/content/module.mjs +32 -0
- package/modules/content/query.mjs +104 -0
- package/modules/sitemap/handler.mjs +17 -0
- package/modules/sitemap/module.d.ts +6 -0
- package/modules/sitemap/module.mjs +28 -0
- package/modules/strapi/README.md +235 -0
- package/modules/strapi/module.d.ts +11 -0
- package/modules/strapi/module.mjs +69 -0
- package/modules/strapi/proxy.mjs +54 -0
- package/modules/strapi/runtime/client.d.ts +30 -0
- package/modules/strapi/runtime/client.mjs +248 -0
- package/package.json +73 -77
- package/server/lib/handler.d.ts +1 -0
- package/server/lib/handler.mjs +43 -0
- package/server/lib/pages.d.ts +20 -0
- package/server/lib/pages.mjs +276 -0
- package/server/lib/render.d.ts +7 -0
- package/server/lib/render.mjs +156 -0
- package/server/plugins/00.lifecycle.d.ts +2 -0
- package/server/plugins/00.lifecycle.mjs +9 -0
- package/server/renderer.d.ts +3 -0
- package/server/renderer.mjs +14 -0
- package/server/routes/api/health.get.d.ts +2 -0
- package/server/routes/api/health.get.mjs +5 -0
- package/server.d.ts +1 -0
- package/server.mjs +32 -0
- package/src/main.d.ts +1 -0
- package/src/main.mjs +29 -0
- package/src/public/boot.d.ts +1 -0
- package/src/public/components.d.ts +1 -0
- package/src/public/components.mjs +1 -0
- package/src/public/head.d.ts +1 -0
- package/src/public/head.mjs +1 -0
- package/src/public/index.d.ts +5 -0
- package/src/public/index.mjs +2 -0
- package/src/public/modules/content.d.ts +2 -0
- package/src/public/modules/content.mjs +1 -0
- package/src/public/modules/sitemap.d.ts +2 -0
- package/src/public/modules/sitemap.mjs +1 -0
- package/src/public/modules/strapi.d.ts +2 -0
- package/src/public/modules/strapi.mjs +1 -0
- package/src/public/nitro.d.ts +6 -0
- package/src/public/nitro.mjs +78 -0
- package/src/public/shared.d.ts +2 -0
- package/src/public/shared.mjs +18 -0
- package/src/public/vite.d.ts +6 -0
- package/src/public/vite.mjs +273 -0
- package/src/public/vue.d.ts +1 -0
- package/src/public/vue.mjs +1 -0
- package/src/runtime/boot.d.ts +9 -0
- package/src/runtime/components/FlowIsland.d.ts +42 -0
- package/src/runtime/components/FlowIsland.mjs +35 -0
- package/src/runtime/config.d.ts +73 -0
- package/src/runtime/config.mjs +26 -0
- package/src/runtime/head.d.ts +3 -0
- package/src/runtime/head.mjs +7 -0
- package/src/runtime/islands.d.ts +2 -0
- package/src/runtime/islands.mjs +52 -0
- package/src/runtime/modules.d.ts +12 -0
- package/src/runtime/modules.mjs +115 -0
- package/src/runtime/page-discovery.d.ts +6 -0
- package/src/runtime/page-discovery.mjs +175 -0
- package/src/runtime/pages.d.ts +107 -0
- package/src/runtime/pages.mjs +3 -0
- package/src/runtime/ssg.d.ts +9 -0
- package/src/runtime/ssg.mjs +37 -0
- package/src/runtime/virtual-pages.d.ts +8 -0
- package/src/runtime/virtual-pages.mjs +151 -0
- package/src/runtime/virtual.d.ts +103 -0
- package/src/runtime/vite-assets.d.ts +9 -0
- package/src/runtime/vue.d.ts +6 -0
- package/src/runtime/vue.mjs +6 -0
- package/src/styles.css +1 -0
- package/app.d.ts +0 -1
- package/bin/flow.mjs +0 -2
- package/dist/app/composables/index.d.ts +0 -5
- package/dist/app/composables/index.mjs +0 -12
- package/dist/app/entry.async.d.ts +0 -3
- package/dist/app/entry.async.mjs +0 -1
- package/dist/app/entry.d.ts +0 -3
- package/dist/app/entry.mjs +0 -23
- package/dist/app/flow.d.ts +0 -80
- package/dist/app/flow.mjs +0 -88
- package/dist/app/index.d.ts +0 -3
- package/dist/app/index.mjs +0 -3
- package/dist/chunks/dev-bundler.mjs +0 -247
- package/dist/chunks/external.mjs +0 -37
- package/dist/chunks/index.mjs +0 -1140
- package/dist/chunks/vite-node.mjs +0 -155
- package/dist/core/runtime/client.manifest.d.mts +0 -2
- package/dist/core/runtime/client.manifest.mjs +0 -6
- package/dist/core/runtime/nitro/flow.d.ts +0 -3
- package/dist/core/runtime/nitro/flow.mjs +0 -33
- package/dist/core/runtime/nitro/paths.d.ts +0 -4
- package/dist/core/runtime/nitro/paths.mjs +0 -15
- package/dist/core/runtime/nitro/renderer.d.ts +0 -2
- package/dist/core/runtime/nitro/renderer.mjs +0 -101
- package/dist/core/runtime/vite-node-shared.d.mts +0 -1
- package/dist/core/runtime/vite-node-shared.d.ts +0 -8
- package/dist/core/runtime/vite-node-shared.mjs +0 -3
- package/dist/core/runtime/vite-node.d.mts +0 -2
- package/dist/core/runtime/vite-node.mjs +0 -42
- package/dist/head/runtime/composables.d.ts +0 -9
- package/dist/head/runtime/composables.mjs +0 -2
- package/dist/head/runtime/index.d.ts +0 -1
- package/dist/head/runtime/index.mjs +0 -1
- package/dist/head/runtime/plugin.mjs +0 -5
- package/dist/index.d.ts +0 -11
- package/dist/index.mjs +0 -27
- package/dist/pages/runtime/helpers/index.d.ts +0 -5
- package/dist/pages/runtime/helpers/index.mjs +0 -31
- package/dist/pages/runtime/index.d.ts +0 -10
- package/dist/pages/runtime/index.mjs +0 -13
- package/dist/pages/runtime/pages.mjs +0 -100
- package/dist/vite-client/runtime/injectManifest.d.ts +0 -26
- package/dist/vite-client/runtime/injectManifest.mjs +0 -110
- package/dist/vite-client/runtime/plugin.mjs +0 -28
- package/types.d.ts +0 -2
- /package/{dist/head/runtime/plugin.d.ts → modules/content/query.d.ts} +0 -0
- /package/{dist/pages/runtime/pages.d.ts → modules/sitemap/handler.d.ts} +0 -0
- /package/{dist/vite-client/runtime/plugin.d.ts → modules/strapi/proxy.d.ts} +0 -0
- /package/{dist/pages/runtime/helpers/chunks.d.ts → src/public/boot.mjs} +0 -0
- /package/{dist/pages/runtime/helpers/chunks.mjs → src/runtime/boot.mjs} +0 -0
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import { useRuntimeConfig } from "nitro/runtime-config";
|
|
2
|
+
import pageDefinitions from "virtual:flow/pages";
|
|
3
|
+
const dynamicRouteCache = /* @__PURE__ */ new Map();
|
|
4
|
+
function normalizePath(value) {
|
|
5
|
+
if (!value || value === "/") {
|
|
6
|
+
return "/";
|
|
7
|
+
}
|
|
8
|
+
return value.length > 1 && value.endsWith("/") ? value.slice(0, -1) : value;
|
|
9
|
+
}
|
|
10
|
+
function scorePattern(pattern) {
|
|
11
|
+
return normalizePath(pattern).split("/").filter(Boolean).reduce((score, segment) => {
|
|
12
|
+
if (segment === "**") {
|
|
13
|
+
return score;
|
|
14
|
+
}
|
|
15
|
+
if (segment.startsWith(":")) {
|
|
16
|
+
return score + 1;
|
|
17
|
+
}
|
|
18
|
+
return score + 3;
|
|
19
|
+
}, pattern.includes("**") ? 0 : 2);
|
|
20
|
+
}
|
|
21
|
+
function matchPattern(pattern, pathname) {
|
|
22
|
+
const expected = normalizePath(pattern).split("/").filter(Boolean);
|
|
23
|
+
const received = normalizePath(pathname).split("/").filter(Boolean);
|
|
24
|
+
const params = {};
|
|
25
|
+
let index = 0;
|
|
26
|
+
let offset = 0;
|
|
27
|
+
while (index < expected.length) {
|
|
28
|
+
const segment = expected[index];
|
|
29
|
+
const current = received[offset];
|
|
30
|
+
if (segment === "**") {
|
|
31
|
+
params._ = received.slice(offset).join("/");
|
|
32
|
+
return params;
|
|
33
|
+
}
|
|
34
|
+
if (!current) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
if (segment.startsWith(":")) {
|
|
38
|
+
params[segment.slice(1)] = current;
|
|
39
|
+
index += 1;
|
|
40
|
+
offset += 1;
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (segment !== current) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
index += 1;
|
|
47
|
+
offset += 1;
|
|
48
|
+
}
|
|
49
|
+
return offset === received.length ? params : null;
|
|
50
|
+
}
|
|
51
|
+
function createLocale(code) {
|
|
52
|
+
const config = useRuntimeConfig();
|
|
53
|
+
const defaults = config.flow?.locale;
|
|
54
|
+
const [lang = defaults?.language || "es", loc = defaults?.location || "ec"] = code.split("-");
|
|
55
|
+
return {
|
|
56
|
+
code,
|
|
57
|
+
lang,
|
|
58
|
+
loc
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function createContext(definition, locale, localePage, pathname, params, dynamic) {
|
|
62
|
+
return {
|
|
63
|
+
dynamic,
|
|
64
|
+
locale,
|
|
65
|
+
path: pathname,
|
|
66
|
+
name: definition.name,
|
|
67
|
+
page: localePage,
|
|
68
|
+
params,
|
|
69
|
+
view: definition.view,
|
|
70
|
+
utils: {
|
|
71
|
+
getLocale() {
|
|
72
|
+
return locale;
|
|
73
|
+
},
|
|
74
|
+
async getUrl(namePage, localeCode, options) {
|
|
75
|
+
return await getUrl(namePage, localeCode, options);
|
|
76
|
+
},
|
|
77
|
+
async getUrls(withLocale, omitNoPublish) {
|
|
78
|
+
return await getUrls(withLocale, omitNoPublish);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function isDynamicPattern(pattern) {
|
|
84
|
+
return pattern.includes("/:") || pattern.includes("/**");
|
|
85
|
+
}
|
|
86
|
+
function replacePath(pattern, url) {
|
|
87
|
+
let resolved = pattern;
|
|
88
|
+
if (typeof url === "string") {
|
|
89
|
+
return normalizePath(resolved.replace("/**", `/${url}`));
|
|
90
|
+
}
|
|
91
|
+
for (const [key, value] of Object.entries(url)) {
|
|
92
|
+
if (key === "_") {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
resolved = resolved.replace(`:${key}`, value);
|
|
96
|
+
}
|
|
97
|
+
if (url._ && resolved.endsWith("/**")) {
|
|
98
|
+
resolved = resolved.replace("/**", `/${url._}`);
|
|
99
|
+
}
|
|
100
|
+
return normalizePath(resolved.replaceAll("**", ""));
|
|
101
|
+
}
|
|
102
|
+
function toPublicRoutePattern(url) {
|
|
103
|
+
return normalizePath(url.replaceAll("*", ""));
|
|
104
|
+
}
|
|
105
|
+
function combineName(name, dynamicName) {
|
|
106
|
+
return `${name.replace(/\/*$/, "")}/${dynamicName.replace(/^\/*/, "")}`;
|
|
107
|
+
}
|
|
108
|
+
function getEnabledLocaleCodes() {
|
|
109
|
+
const runtimeConfig = useRuntimeConfig();
|
|
110
|
+
return runtimeConfig.flow?.locale?.locales || [];
|
|
111
|
+
}
|
|
112
|
+
async function getDynamicEntries(definition, localeCode, ctx) {
|
|
113
|
+
const dynamic = ctx.page.dynamic;
|
|
114
|
+
if (!dynamic) {
|
|
115
|
+
return [];
|
|
116
|
+
}
|
|
117
|
+
const cacheKey = `${definition.name}:${localeCode}:${ctx.page.url}`;
|
|
118
|
+
if (dynamic.options?.once) {
|
|
119
|
+
const cached = dynamicRouteCache.get(cacheKey);
|
|
120
|
+
if (cached) {
|
|
121
|
+
return await cached;
|
|
122
|
+
}
|
|
123
|
+
const pending = Promise.resolve(dynamic.method(ctx));
|
|
124
|
+
dynamicRouteCache.set(cacheKey, pending);
|
|
125
|
+
return await pending;
|
|
126
|
+
}
|
|
127
|
+
return await dynamic.method(ctx);
|
|
128
|
+
}
|
|
129
|
+
async function resolveDynamicEntry(definition, localeCode, localePage, ctx, pathname) {
|
|
130
|
+
if (!localePage.dynamic || !isDynamicPattern(localePage.url)) {
|
|
131
|
+
return void 0;
|
|
132
|
+
}
|
|
133
|
+
const entries = await getDynamicEntries(definition, localeCode, ctx);
|
|
134
|
+
const resolvedPath = normalizePath(pathname);
|
|
135
|
+
return entries.find((entry) => replacePath(localePage.url, entry.url) === resolvedPath);
|
|
136
|
+
}
|
|
137
|
+
export async function getUrl(namePage, localeCode, options = {}) {
|
|
138
|
+
const enabledLocales = getEnabledLocaleCodes();
|
|
139
|
+
const targetLocale = localeCode || enabledLocales[0];
|
|
140
|
+
for (const definition of pageDefinitions) {
|
|
141
|
+
if (definition.name !== namePage) {
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
const localeEntries = targetLocale ? [targetLocale] : Object.keys(definition.locales);
|
|
145
|
+
for (const code of localeEntries) {
|
|
146
|
+
const localePage = definition.locales[code];
|
|
147
|
+
if (!localePage) {
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
if (options.url) {
|
|
151
|
+
return replacePath(localePage.url, options.url);
|
|
152
|
+
}
|
|
153
|
+
if (options.params) {
|
|
154
|
+
return replacePath(localePage.url, options.params);
|
|
155
|
+
}
|
|
156
|
+
if (localePage.dynamic && options.dynamicName) {
|
|
157
|
+
const locale = createLocale(code);
|
|
158
|
+
const ctx = createContext(definition, locale, localePage, localePage.url, {});
|
|
159
|
+
const entries = await getDynamicEntries(definition, code, ctx);
|
|
160
|
+
const entry = entries.find((candidate) => candidate.name === options.dynamicName);
|
|
161
|
+
if (!entry) {
|
|
162
|
+
return void 0;
|
|
163
|
+
}
|
|
164
|
+
return replacePath(localePage.url, entry.url);
|
|
165
|
+
}
|
|
166
|
+
return toPublicRoutePattern(localePage.url);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return void 0;
|
|
170
|
+
}
|
|
171
|
+
export async function getUrls(withLocale = false, omitNoPublish = false) {
|
|
172
|
+
const enabledLocales = getEnabledLocaleCodes();
|
|
173
|
+
const urls = [];
|
|
174
|
+
for (const definition of pageDefinitions) {
|
|
175
|
+
if (omitNoPublish && definition.noPublish) {
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
for (const [localeCode, localePage] of Object.entries(definition.locales)) {
|
|
179
|
+
if (enabledLocales.length && !enabledLocales.includes(localeCode)) {
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
if (localePage.dynamic) {
|
|
183
|
+
const locale = createLocale(localeCode);
|
|
184
|
+
const ctx = createContext(definition, locale, localePage, localePage.url, {});
|
|
185
|
+
const entries = await getDynamicEntries(definition, localeCode, ctx);
|
|
186
|
+
for (const entry of entries) {
|
|
187
|
+
const url2 = replacePath(localePage.url, entry.url);
|
|
188
|
+
urls.push(withLocale ? { url: url2, locale: localeCode, name: combineName(definition.name, entry.name) } : url2);
|
|
189
|
+
}
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
const url = toPublicRoutePattern(localePage.url);
|
|
193
|
+
urls.push(withLocale ? { url, locale: localeCode, name: definition.name } : url);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return urls;
|
|
197
|
+
}
|
|
198
|
+
export async function resolvePage(pathname) {
|
|
199
|
+
const runtimeConfig = useRuntimeConfig();
|
|
200
|
+
const enabledLocales = runtimeConfig.flow?.locale?.locales || [];
|
|
201
|
+
const candidates = [];
|
|
202
|
+
for (const definition of pageDefinitions) {
|
|
203
|
+
for (const [localeCode, localePage] of Object.entries(definition.locales)) {
|
|
204
|
+
if (enabledLocales.length && !enabledLocales.includes(localeCode)) {
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
const params = matchPattern(localePage.url, pathname);
|
|
208
|
+
if (params) {
|
|
209
|
+
candidates.push({
|
|
210
|
+
definition,
|
|
211
|
+
localeCode,
|
|
212
|
+
localePage,
|
|
213
|
+
params,
|
|
214
|
+
score: scorePattern(localePage.url)
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
const sortedCandidates = candidates.sort((left, right) => right.score - left.score);
|
|
220
|
+
for (const match of sortedCandidates) {
|
|
221
|
+
const locale = createLocale(match.localeCode);
|
|
222
|
+
const baseCtx = createContext(match.definition, locale, match.localePage, pathname, match.params);
|
|
223
|
+
const dynamic = await resolveDynamicEntry(match.definition, match.localeCode, match.localePage, baseCtx, pathname);
|
|
224
|
+
if (match.localePage.dynamic && !dynamic) {
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
const ctx = createContext(match.definition, locale, match.localePage, pathname, match.params, dynamic);
|
|
228
|
+
const head = match.localePage.head ? await match.localePage.head(ctx) : match.localePage.seo ? await match.localePage.seo(ctx) : {};
|
|
229
|
+
const context = match.localePage.context ? await match.localePage.context(ctx) : {};
|
|
230
|
+
const assign = match.localePage.dynamic?.assign;
|
|
231
|
+
const resolvedContext = assign && dynamic?.context ? { ...context, [assign]: dynamic.context, dynamic } : dynamic ? { ...context, dynamic } : context;
|
|
232
|
+
return {
|
|
233
|
+
definition: match.definition,
|
|
234
|
+
locale,
|
|
235
|
+
localeCode: match.localeCode,
|
|
236
|
+
localePage: match.localePage,
|
|
237
|
+
pathname,
|
|
238
|
+
params: match.params,
|
|
239
|
+
dynamic,
|
|
240
|
+
head,
|
|
241
|
+
seo: head,
|
|
242
|
+
context: resolvedContext
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
return void 0;
|
|
246
|
+
}
|
|
247
|
+
export async function resolvePageByName(name, pathname) {
|
|
248
|
+
const runtimeConfig = useRuntimeConfig();
|
|
249
|
+
const enabledLocales = runtimeConfig.flow?.locale?.locales || [];
|
|
250
|
+
const definition = pageDefinitions.find((candidate) => candidate.name === name);
|
|
251
|
+
if (!definition) {
|
|
252
|
+
return void 0;
|
|
253
|
+
}
|
|
254
|
+
for (const [localeCode, localePage] of Object.entries(definition.locales)) {
|
|
255
|
+
if (enabledLocales.length && !enabledLocales.includes(localeCode)) {
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
const locale = createLocale(localeCode);
|
|
259
|
+
const params = {};
|
|
260
|
+
const ctx = createContext(definition, locale, localePage, pathname, params);
|
|
261
|
+
const head = localePage.head ? await localePage.head(ctx) : localePage.seo ? await localePage.seo(ctx) : {};
|
|
262
|
+
const context = localePage.context ? await localePage.context(ctx) : {};
|
|
263
|
+
return {
|
|
264
|
+
definition,
|
|
265
|
+
locale,
|
|
266
|
+
localeCode,
|
|
267
|
+
localePage,
|
|
268
|
+
pathname,
|
|
269
|
+
params,
|
|
270
|
+
head,
|
|
271
|
+
seo: head,
|
|
272
|
+
context
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
return void 0;
|
|
276
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ResolvedPage } from './pages';
|
|
2
|
+
export interface FlowClientAssets {
|
|
3
|
+
entry: string;
|
|
4
|
+
css?: Array<Record<string, string>>;
|
|
5
|
+
js?: Array<Record<string, string>>;
|
|
6
|
+
}
|
|
7
|
+
export declare function renderDocument(page: ResolvedPage, clientAssets: FlowClientAssets): Promise<string>;
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import UApp from "@nuxt/ui/components/App.vue";
|
|
2
|
+
import { createHead } from "@unhead/vue/server";
|
|
3
|
+
import { renderToString } from "@vue/server-renderer";
|
|
4
|
+
import { transformHtmlTemplate } from "unhead/server";
|
|
5
|
+
import bases from "virtual:flow/bases";
|
|
6
|
+
import layouts from "virtual:flow/layouts";
|
|
7
|
+
import templates from "virtual:flow/templates";
|
|
8
|
+
import { createSSRApp, defineComponent, h } from "vue";
|
|
9
|
+
import { installFlowVuePlugins } from "../../src/runtime/vue";
|
|
10
|
+
function escapeHtml(value) {
|
|
11
|
+
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
12
|
+
}
|
|
13
|
+
function asString(value, fallback) {
|
|
14
|
+
return typeof value === "string" && value.length ? value : fallback;
|
|
15
|
+
}
|
|
16
|
+
function serializeBootPayload(payload) {
|
|
17
|
+
return JSON.stringify(payload).replaceAll("<", "\\u003c");
|
|
18
|
+
}
|
|
19
|
+
function serializeAttrs(attrs) {
|
|
20
|
+
return Object.entries(attrs).map(([key, value]) => `${key}="${escapeHtml(value)}"`).join(" ");
|
|
21
|
+
}
|
|
22
|
+
function renderClientHead(assets) {
|
|
23
|
+
const tags = [
|
|
24
|
+
...(assets.css || []).map((attrs) => `<link ${serializeAttrs({ rel: "stylesheet", ...attrs })}>`),
|
|
25
|
+
...(assets.js || []).map((attrs) => `<link ${serializeAttrs({ rel: "modulepreload", ...attrs })}>`),
|
|
26
|
+
`<script type="module" src="${escapeHtml(assets.entry)}"><\/script>`
|
|
27
|
+
];
|
|
28
|
+
return tags.join("\n ");
|
|
29
|
+
}
|
|
30
|
+
function applyBaseTemplate(template, payload) {
|
|
31
|
+
return template.replaceAll("{{lang}}", escapeHtml(payload.lang)).replaceAll("{{title}}", escapeHtml(payload.title)).replaceAll("{{description}}", escapeHtml(payload.description)).replaceAll("{{head}}", payload.head).replaceAll("{{body}}", payload.body);
|
|
32
|
+
}
|
|
33
|
+
function stripVueFragmentMarkers(value) {
|
|
34
|
+
let rendered = value.trim();
|
|
35
|
+
while (rendered.startsWith("<!--[-->") && rendered.endsWith("<!--]-->")) {
|
|
36
|
+
rendered = rendered.slice("<!--[-->".length, -"<!--]-->".length).trim();
|
|
37
|
+
}
|
|
38
|
+
return rendered;
|
|
39
|
+
}
|
|
40
|
+
function isRecord(value) {
|
|
41
|
+
return typeof value === "object" && value !== null;
|
|
42
|
+
}
|
|
43
|
+
function resolveString(value) {
|
|
44
|
+
return typeof value === "string" && value.length ? value : void 0;
|
|
45
|
+
}
|
|
46
|
+
function normalizeSeoToHead(head, fallbackTitle, fallbackDescription) {
|
|
47
|
+
const source = isRecord(head) ? { ...head } : {};
|
|
48
|
+
const meta = Array.isArray(source.meta) ? [...source.meta] : [];
|
|
49
|
+
const title = resolveString(source.title) || fallbackTitle;
|
|
50
|
+
const description = resolveString(source.description) || fallbackDescription;
|
|
51
|
+
const keywords = resolveString(source.keywords);
|
|
52
|
+
const imageSocial = resolveString(source.imageSocial);
|
|
53
|
+
const twitterSite = resolveString(source.twitterSite);
|
|
54
|
+
const google = source.google;
|
|
55
|
+
if (!meta.some((entry) => isRecord(entry) && entry.name === "description")) {
|
|
56
|
+
meta.push({ name: "description", content: description });
|
|
57
|
+
}
|
|
58
|
+
if (keywords && !meta.some((entry) => isRecord(entry) && entry.name === "keywords")) {
|
|
59
|
+
meta.push({ name: "keywords", content: keywords });
|
|
60
|
+
}
|
|
61
|
+
if (imageSocial) {
|
|
62
|
+
if (!meta.some((entry) => isRecord(entry) && entry.property === "og:image")) {
|
|
63
|
+
meta.push({ property: "og:image", content: imageSocial });
|
|
64
|
+
}
|
|
65
|
+
if (!meta.some((entry) => isRecord(entry) && entry.name === "twitter:image")) {
|
|
66
|
+
meta.push({ name: "twitter:image", content: imageSocial });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (twitterSite && !meta.some((entry) => isRecord(entry) && entry.name === "twitter:site")) {
|
|
70
|
+
meta.push({ name: "twitter:site", content: twitterSite });
|
|
71
|
+
}
|
|
72
|
+
if (google === false && !meta.some((entry) => isRecord(entry) && entry.name === "robots")) {
|
|
73
|
+
meta.push({ name: "robots", content: "noindex,nofollow" });
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
...source,
|
|
77
|
+
title,
|
|
78
|
+
meta
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
async function renderBody(page) {
|
|
82
|
+
const templateKey = page.definition.view.template.toLowerCase();
|
|
83
|
+
const layoutKey = (page.definition.view.layout || "default").toLowerCase();
|
|
84
|
+
const TemplateComponent = templates[templateKey];
|
|
85
|
+
const LayoutComponent = layouts[layoutKey];
|
|
86
|
+
const fallbackTitle = asString(page.head.title, page.definition.name);
|
|
87
|
+
const fallbackDescription = asString(page.head.description, "Flow page");
|
|
88
|
+
if (!TemplateComponent) {
|
|
89
|
+
const head2 = createHead();
|
|
90
|
+
head2.push({
|
|
91
|
+
title: fallbackTitle,
|
|
92
|
+
meta: [{ name: "description", content: fallbackDescription }]
|
|
93
|
+
});
|
|
94
|
+
return {
|
|
95
|
+
body: `<section class="shell"><div class="shell__panel">Missing template: ${escapeHtml(page.definition.view.template)}</div></section>`,
|
|
96
|
+
head: head2
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
const TemplateRoot = defineComponent({
|
|
100
|
+
render() {
|
|
101
|
+
const viewNode = h(TemplateComponent, {
|
|
102
|
+
page: page.definition,
|
|
103
|
+
context: page.context,
|
|
104
|
+
head: page.head,
|
|
105
|
+
seo: page.seo,
|
|
106
|
+
locale: page.locale,
|
|
107
|
+
params: page.params,
|
|
108
|
+
path: page.pathname
|
|
109
|
+
});
|
|
110
|
+
const content = LayoutComponent ? h(LayoutComponent, {}, {
|
|
111
|
+
default: () => viewNode
|
|
112
|
+
}) : viewNode;
|
|
113
|
+
return h(UApp, {}, {
|
|
114
|
+
default: () => content
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
const app = createSSRApp(TemplateRoot);
|
|
119
|
+
const head = createHead();
|
|
120
|
+
head.push(normalizeSeoToHead(page.head, fallbackTitle, fallbackDescription));
|
|
121
|
+
app.use(head);
|
|
122
|
+
installFlowVuePlugins(app);
|
|
123
|
+
return {
|
|
124
|
+
body: await renderToString(app),
|
|
125
|
+
head
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
export async function renderDocument(page, clientAssets) {
|
|
129
|
+
const mode = page.definition.view.mode || "islands";
|
|
130
|
+
const fallbackTitle = asString(page.head.title, page.definition.name);
|
|
131
|
+
const fallbackDescription = asString(page.head.description, "Flow page");
|
|
132
|
+
const boot = {
|
|
133
|
+
path: page.pathname,
|
|
134
|
+
bundle: page.definition.view.bundle || "_default",
|
|
135
|
+
template: page.definition.view.template,
|
|
136
|
+
title: fallbackTitle,
|
|
137
|
+
locale: page.locale,
|
|
138
|
+
mode
|
|
139
|
+
};
|
|
140
|
+
const rendered = await renderBody(page);
|
|
141
|
+
const body = stripVueFragmentMarkers(rendered.body);
|
|
142
|
+
const baseKey = (page.definition.view.base || "default").toLowerCase();
|
|
143
|
+
const baseTemplate = bases[baseKey] || bases.default || '<!doctype html><html lang="{{lang}}"><head><meta charset="UTF-8" /><title>{{title}}</title>{{head}}</head><body>{{body}}</body></html>';
|
|
144
|
+
const pageMarkup = mode === "app" ? `<div id="app" data-page="${escapeHtml(page.definition.name)}" data-template="${escapeHtml(page.definition.view.template)}" data-flow-mode="app">${body}</div>` : body;
|
|
145
|
+
const html = applyBaseTemplate(baseTemplate, {
|
|
146
|
+
lang: page.locale.lang,
|
|
147
|
+
title: fallbackTitle,
|
|
148
|
+
description: fallbackDescription,
|
|
149
|
+
head: [
|
|
150
|
+
`<script id="flow-boot" type="application/json">${serializeBootPayload(boot)}<\/script>`,
|
|
151
|
+
renderClientHead(clientAssets)
|
|
152
|
+
].join("\n "),
|
|
153
|
+
body: pageMarkup
|
|
154
|
+
});
|
|
155
|
+
return transformHtmlTemplate(rendered.head, html);
|
|
156
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { definePlugin } from "nitro";
|
|
2
|
+
export default definePlugin((nitroApp) => {
|
|
3
|
+
nitroApp.hooks.hook("request", (event) => {
|
|
4
|
+
const pathname = new URL(event.req.url, "http://localhost").pathname;
|
|
5
|
+
if (!pathname.startsWith("/api/")) {
|
|
6
|
+
console.info(`[nitro] request ${pathname}`);
|
|
7
|
+
}
|
|
8
|
+
});
|
|
9
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
function resolveRequest(input) {
|
|
2
|
+
if (input instanceof Request) {
|
|
3
|
+
return input;
|
|
4
|
+
}
|
|
5
|
+
return input?.req;
|
|
6
|
+
}
|
|
7
|
+
export default function renderPage(input) {
|
|
8
|
+
const viteEnvs = globalThis.__nitro_vite_envs__;
|
|
9
|
+
const request = resolveRequest(input);
|
|
10
|
+
if (!request) {
|
|
11
|
+
return new Response("Not Found", { status: 404 });
|
|
12
|
+
}
|
|
13
|
+
return viteEnvs?.ssr?.fetch(request);
|
|
14
|
+
}
|
package/server.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/server.mjs
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
interface ViteFetchEnv {
|
|
2
|
+
fetch(request: Request): Response | Promise<Response>;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
type FlowGlobalWithViteEnvs = typeof globalThis & {
|
|
6
|
+
__nitro_vite_envs__?: Record<string, ViteFetchEnv | undefined>;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
function isPageRequest(pathname: string) {
|
|
10
|
+
if (pathname.startsWith('/api/')) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (pathname.startsWith('/assets/')) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return !/\.[a-z0-9]+$/i.test(pathname);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default {
|
|
22
|
+
async fetch(request: Request) {
|
|
23
|
+
const pathname = new URL(request.url).pathname;
|
|
24
|
+
|
|
25
|
+
if (!isPageRequest(pathname)) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const viteEnvs = (globalThis as FlowGlobalWithViteEnvs).__nitro_vite_envs__;
|
|
30
|
+
return viteEnvs?.ssr?.fetch(request);
|
|
31
|
+
},
|
|
32
|
+
};
|
package/src/main.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/src/main.mjs
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { hydrateIslands } from "./runtime/islands.mjs";
|
|
2
|
+
import bundles from "virtual:flow/client-pages";
|
|
3
|
+
function readBoot() {
|
|
4
|
+
const element = document.getElementById("flow-boot");
|
|
5
|
+
if (!element?.textContent) {
|
|
6
|
+
return void 0;
|
|
7
|
+
}
|
|
8
|
+
try {
|
|
9
|
+
return JSON.parse(element.textContent);
|
|
10
|
+
} catch {
|
|
11
|
+
return void 0;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
async function bootstrap() {
|
|
15
|
+
const boot = readBoot();
|
|
16
|
+
if (boot) {
|
|
17
|
+
await hydrateIslands(boot);
|
|
18
|
+
}
|
|
19
|
+
const bundle = boot?.bundle || "_default";
|
|
20
|
+
const importer = bundles[bundle] || bundles._default;
|
|
21
|
+
if (!importer) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const mod = await importer();
|
|
25
|
+
if (mod.default && boot) {
|
|
26
|
+
await mod.default(boot);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
void bootstrap();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type { FlowBootPayload } from '../runtime/boot.ts';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as FlowIsland } from '../runtime/components/FlowIsland.ts';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as FlowIsland } from "../runtime/components/FlowIsland.mjs";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { getClientHead, useHead, useSeoMeta } from '../runtime/head.ts';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { getClientHead, useHead, useSeoMeta } from "../runtime/head.mjs";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { defineFlowConfig, defineFlowModule, resolveFlowConfig } from '../runtime/config.ts';
|
|
2
|
+
export { definePage } from '../runtime/pages.ts';
|
|
3
|
+
export type { FlowConfig, FlowLocale, HeadDefinition, PageContextInput, UserFlowConfig, } from '../runtime/config.ts';
|
|
4
|
+
export type { FlowBootPayload } from '../runtime/boot.ts';
|
|
5
|
+
export type { FlowHydrationMode, PageDefinition, PageLocaleDefinition, PageViewDefinition, } from '../runtime/pages.ts';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "../../../modules/content/module.mjs";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "../../../modules/sitemap/module.mjs";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "../../../modules/strapi/module.mjs";
|