@monkeyplus/flow 6.0.1 → 6.0.3

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/entry-server.mjs CHANGED
@@ -1,5 +1,4 @@
1
1
  import { renderPageRequest } from './server/lib/handler.mjs';
2
-
3
2
  export default {
4
- fetch: renderPageRequest,
5
- };
3
+ fetch: renderPageRequest,
4
+ };
@@ -1,6 +1,230 @@
1
- import { resolve } from "node:path";
1
+ import { mkdirSync, writeFileSync } from "node:fs";
2
+ import { dirname, resolve } from "node:path";
3
+ import { resolvePackagePath } from "../../src/public/shared.mjs";
2
4
  import { defineFlowModule } from "../../src/runtime/config.mjs";
3
- import { resolvePackageFile, resolvePackagePath } from "../../src/public/shared.mjs";
5
+ function createGeneratedSitemapHandler(siteUrl, locales) {
6
+ return `
7
+ import pageDefinitions from 'virtual:flow/pages';
8
+
9
+ const dynamicRouteCache = new Map();
10
+ const configuredSiteUrl = ${JSON.stringify(siteUrl || "")};
11
+ const enabledLocales = ${JSON.stringify(locales)};
12
+
13
+ function escapeXml(value) {
14
+ return value
15
+ .replaceAll('&', '&')
16
+ .replaceAll('<', '&lt;')
17
+ .replaceAll('>', '&gt;')
18
+ .replaceAll('"', '&quot;')
19
+ .replaceAll("'", '&apos;');
20
+ }
21
+
22
+ function normalizePath(value) {
23
+ if (!value || value === '/') {
24
+ return '/';
25
+ }
26
+
27
+ return value.length > 1 && value.endsWith('/') ? value.slice(0, -1) : value;
28
+ }
29
+
30
+ function replacePath(pattern, url) {
31
+ let resolved = pattern;
32
+
33
+ if (typeof url === 'string') {
34
+ return normalizePath(resolved.replace('/**', '/' + url));
35
+ }
36
+
37
+ for (const [key, value] of Object.entries(url)) {
38
+ if (key === '_') {
39
+ continue;
40
+ }
41
+
42
+ resolved = resolved.replace(':' + key, value);
43
+ }
44
+
45
+ if (url._ && resolved.endsWith('/**')) {
46
+ resolved = resolved.replace('/**', '/' + url._);
47
+ }
48
+
49
+ return normalizePath(resolved.replaceAll('**', ''));
50
+ }
51
+
52
+ function toPublicRoutePattern(url) {
53
+ return normalizePath(url.replaceAll('*', ''));
54
+ }
55
+
56
+ function createLocale(code) {
57
+ const [lang = 'es', loc = 'ec'] = code.split('-');
58
+
59
+ return {
60
+ code,
61
+ lang,
62
+ loc,
63
+ };
64
+ }
65
+
66
+ async function getUrl(namePage, localeCode, options = {}) {
67
+ const enabledLocales = getEnabledLocaleCodes();
68
+ const targetLocale = localeCode || enabledLocales[0];
69
+
70
+ for (const definition of pageDefinitions) {
71
+ if (definition.name !== namePage) {
72
+ continue;
73
+ }
74
+
75
+ const localeEntries = targetLocale ? [targetLocale] : Object.keys(definition.locales);
76
+
77
+ for (const code of localeEntries) {
78
+ const localePage = definition.locales[code];
79
+
80
+ if (!localePage) {
81
+ continue;
82
+ }
83
+
84
+ if (options.url) {
85
+ return replacePath(localePage.url, options.url);
86
+ }
87
+
88
+ if (options.params) {
89
+ return replacePath(localePage.url, options.params);
90
+ }
91
+
92
+ if (localePage.dynamic && options.dynamicName) {
93
+ const locale = createLocale(code);
94
+ const ctx = createContext(definition, locale, localePage, localePage.url, {});
95
+ const entries = await getDynamicEntries(definition, code, ctx);
96
+ const entry = entries.find(candidate => candidate.name === options.dynamicName);
97
+
98
+ if (!entry) {
99
+ return undefined;
100
+ }
101
+
102
+ return replacePath(localePage.url, entry.url);
103
+ }
104
+
105
+ return toPublicRoutePattern(localePage.url);
106
+ }
107
+ }
108
+
109
+ return undefined;
110
+ }
111
+
112
+ async function getUrls(withLocale = false, omitNoPublish = false) {
113
+ const enabledLocales = getEnabledLocaleCodes();
114
+ const urls = [];
115
+
116
+ for (const definition of pageDefinitions) {
117
+ if (omitNoPublish && definition.noPublish) {
118
+ continue;
119
+ }
120
+
121
+ for (const [localeCode, localePage] of Object.entries(definition.locales)) {
122
+ if (enabledLocales.length && !enabledLocales.includes(localeCode)) {
123
+ continue;
124
+ }
125
+
126
+ if (localePage.dynamic) {
127
+ const locale = createLocale(localeCode);
128
+ const ctx = createContext(definition, locale, localePage, localePage.url, {});
129
+ const entries = await getDynamicEntries(definition, localeCode, ctx);
130
+
131
+ for (const entry of entries) {
132
+ const url = replacePath(localePage.url, entry.url);
133
+ urls.push(withLocale ? { url, locale: localeCode } : url);
134
+ }
135
+
136
+ continue;
137
+ }
138
+
139
+ const url = toPublicRoutePattern(localePage.url);
140
+ urls.push(withLocale ? { url, locale: localeCode } : url);
141
+ }
142
+ }
143
+
144
+ return urls;
145
+ }
146
+
147
+ function getEnabledLocaleCodes() {
148
+ return enabledLocales;
149
+ }
150
+
151
+ function createContext(definition, locale, localePage, pathname, params, dynamic) {
152
+ return {
153
+ dynamic,
154
+ locale,
155
+ path: pathname,
156
+ name: definition.name,
157
+ page: localePage,
158
+ params,
159
+ view: definition.view,
160
+ utils: {
161
+ getLocale() {
162
+ return locale;
163
+ },
164
+ async getUrl(namePage, localeCode, options) {
165
+ return await getUrl(namePage, localeCode, options);
166
+ },
167
+ async getUrls(withLocale, omitNoPublish) {
168
+ return await getUrls(withLocale, omitNoPublish);
169
+ },
170
+ },
171
+ };
172
+ }
173
+
174
+ async function getDynamicEntries(definition, localeCode, ctx) {
175
+ const dynamic = ctx.page.dynamic;
176
+
177
+ if (!dynamic) {
178
+ return [];
179
+ }
180
+
181
+ const cacheKey = definition.name + ':' + localeCode + ':' + ctx.page.url;
182
+
183
+ if (dynamic.options?.once) {
184
+ const cached = dynamicRouteCache.get(cacheKey);
185
+
186
+ if (cached) {
187
+ return await cached;
188
+ }
189
+
190
+ const pending = Promise.resolve(dynamic.method(ctx));
191
+ dynamicRouteCache.set(cacheKey, pending);
192
+ return await pending;
193
+ }
194
+
195
+ return await dynamic.method(ctx);
196
+ }
197
+
198
+ export default async function sitemapHandler(event) {
199
+ const urls = await getUrls(true, true);
200
+ const requestUrl = event?.req?.url
201
+ ? new URL(event.req.url, configuredSiteUrl || 'http://localhost')
202
+ : new URL(configuredSiteUrl || 'http://localhost');
203
+ const origin = configuredSiteUrl || requestUrl.origin;
204
+
205
+ const xml = '<?xml version="1.0" encoding="UTF-8"?>\\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\\n' + urls
206
+ .map((entry) => typeof entry === 'string'
207
+ ? ' <url><loc>' + escapeXml(new URL(entry, origin).toString()) + '</loc></url>'
208
+ : ' <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>')
209
+ .join('\\n') + '\\n</urlset>';
210
+
211
+ return new Response(xml, {
212
+ headers: {
213
+ 'content-type': 'application/xml; charset=utf-8',
214
+ },
215
+ });
216
+ }
217
+ `;
218
+ }
219
+ function ensureGeneratedSitemapHandler(projectRoot, flowRuntimeConfig) {
220
+ const handlerPath = resolve(projectRoot, ".flow/generated/modules/sitemap/handler.mjs");
221
+ mkdirSync(dirname(handlerPath), { recursive: true });
222
+ writeFileSync(handlerPath, createGeneratedSitemapHandler(
223
+ typeof flowRuntimeConfig.siteUrl === "string" ? flowRuntimeConfig.siteUrl : void 0,
224
+ Array.isArray(flowRuntimeConfig.locale?.locales) ? flowRuntimeConfig.locale.locales : []
225
+ ), "utf8");
226
+ return handlerPath;
227
+ }
4
228
  export default defineFlowModule({
5
229
  meta: {
6
230
  name: "sitemap",
@@ -11,8 +235,9 @@ export default defineFlowModule({
11
235
  prerender: true
12
236
  },
13
237
  setup(options, context) {
238
+ const contextRuntimeConfig = typeof context.nitro.runtimeConfig.flow === "object" && context.nitro.runtimeConfig.flow ? context.nitro.runtimeConfig.flow : {};
14
239
  const localHandlerPath = resolve(context.projectRoot, "modules/sitemap/handler.ts");
15
- const handlerPath = context.projectRoot === resolvePackagePath() ? localHandlerPath : resolvePackageFile("modules/sitemap/handler.ts", "modules/sitemap/handler.mjs", "modules/sitemap/handler.js");
240
+ const handlerPath = context.projectRoot === resolvePackagePath() ? localHandlerPath : ensureGeneratedSitemapHandler(context.projectRoot, contextRuntimeConfig);
16
241
  context.nitro.handlers.push({
17
242
  method: "get",
18
243
  route: options.route,
@@ -54,8 +54,13 @@ async function getServerRuntimeConfig() {
54
54
  if (typeof window !== "undefined") {
55
55
  return void 0;
56
56
  }
57
- const runtime = await import("nitro/runtime-config");
58
- return runtime.useRuntimeConfig();
57
+ try {
58
+ const loadRuntimeModule = new Function("specifier", "return import(specifier);");
59
+ const runtime = await loadRuntimeModule("nitro/runtime-config");
60
+ return typeof runtime.useRuntimeConfig === "function" ? runtime.useRuntimeConfig() : void 0;
61
+ } catch {
62
+ return void 0;
63
+ }
59
64
  }
60
65
  async function getPublicStrapiConfig() {
61
66
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@monkeyplus/flow",
3
- "version": "6.0.1",
3
+ "version": "6.0.3",
4
4
  "description": "@monkeyplus/flow package-first runtime with Vite, Nitro, Vue and a workspace playground.",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
@@ -1,6 +1,16 @@
1
- import { useRuntimeConfig } from "nitro/runtime-config";
1
+ import { createRequire } from "node:module";
2
2
  import pageDefinitions from "virtual:flow/pages";
3
3
  const dynamicRouteCache = /* @__PURE__ */ new Map();
4
+ let runtimeConfigRequire;
5
+ function getFlowRuntimeConfig() {
6
+ try {
7
+ runtimeConfigRequire ??= createRequire(import.meta.url);
8
+ const runtime = runtimeConfigRequire("nitro/runtime-config");
9
+ return typeof runtime.useRuntimeConfig === "function" ? runtime.useRuntimeConfig() : {};
10
+ } catch {
11
+ return {};
12
+ }
13
+ }
4
14
  function normalizePath(value) {
5
15
  if (!value || value === "/") {
6
16
  return "/";
@@ -49,7 +59,7 @@ function matchPattern(pattern, pathname) {
49
59
  return offset === received.length ? params : null;
50
60
  }
51
61
  function createLocale(code) {
52
- const config = useRuntimeConfig();
62
+ const config = getFlowRuntimeConfig();
53
63
  const defaults = config.flow?.locale;
54
64
  const [lang = defaults?.language || "es", loc = defaults?.location || "ec"] = code.split("-");
55
65
  return {
@@ -106,7 +116,7 @@ function combineName(name, dynamicName) {
106
116
  return `${name.replace(/\/*$/, "")}/${dynamicName.replace(/^\/*/, "")}`;
107
117
  }
108
118
  function getEnabledLocaleCodes() {
109
- const runtimeConfig = useRuntimeConfig();
119
+ const runtimeConfig = getFlowRuntimeConfig();
110
120
  return runtimeConfig.flow?.locale?.locales || [];
111
121
  }
112
122
  async function getDynamicEntries(definition, localeCode, ctx) {
@@ -196,7 +206,7 @@ export async function getUrls(withLocale = false, omitNoPublish = false) {
196
206
  return urls;
197
207
  }
198
208
  export async function resolvePage(pathname) {
199
- const runtimeConfig = useRuntimeConfig();
209
+ const runtimeConfig = getFlowRuntimeConfig();
200
210
  const enabledLocales = runtimeConfig.flow?.locale?.locales || [];
201
211
  const candidates = [];
202
212
  for (const definition of pageDefinitions) {
@@ -245,7 +255,7 @@ export async function resolvePage(pathname) {
245
255
  return void 0;
246
256
  }
247
257
  export async function resolvePageByName(name, pathname) {
248
- const runtimeConfig = useRuntimeConfig();
258
+ const runtimeConfig = getFlowRuntimeConfig();
249
259
  const enabledLocales = runtimeConfig.flow?.locale?.locales || [];
250
260
  const definition = pageDefinitions.find((candidate) => candidate.name === name);
251
261
  if (!definition) {
@@ -1,3 +1,3 @@
1
1
  export default function renderPage(input: Request | {
2
2
  req?: Request;
3
- }): Response | Promise<Response> | undefined;
3
+ }): Promise<Response | undefined>;
@@ -1,14 +1,36 @@
1
+ let prerenderFetchHandlerPromise;
1
2
  function resolveRequest(input) {
2
3
  if (input instanceof Request) {
3
4
  return input;
4
5
  }
5
6
  return input?.req;
6
7
  }
7
- export default function renderPage(input) {
8
+ async function resolvePrerenderFetchHandler() {
9
+ prerenderFetchHandlerPromise ||= (async () => {
10
+ try {
11
+ const ssrServiceUrl = new URL("../../vite/services/ssr/index.js", import.meta.url);
12
+ const ssrService = await import(
13
+ /* @vite-ignore */
14
+ ssrServiceUrl.href
15
+ );
16
+ const defaultService = ssrService.default;
17
+ const fetchHandler = defaultService?.fetch;
18
+ return typeof fetchHandler === "function" ? fetchHandler.bind(defaultService) : void 0;
19
+ } catch {
20
+ return void 0;
21
+ }
22
+ })();
23
+ return await prerenderFetchHandlerPromise;
24
+ }
25
+ export default async function renderPage(input) {
8
26
  const viteEnvs = globalThis.__nitro_vite_envs__;
9
27
  const request = resolveRequest(input);
28
+ const viteFetchHandler = viteEnvs?.ssr?.fetch;
10
29
  if (!request) {
11
30
  return new Response("Not Found", { status: 404 });
12
31
  }
13
- return viteEnvs?.ssr?.fetch(request);
32
+ if (typeof viteFetchHandler === "function") {
33
+ return viteFetchHandler(request);
34
+ }
35
+ return (await resolvePrerenderFetchHandler())?.(request);
14
36
  }
package/server.mjs CHANGED
@@ -1,32 +1,30 @@
1
- interface ViteFetchEnv {
2
- fetch(request: Request): Response | Promise<Response>;
1
+ function isPageRequest(pathname) {
2
+ if (pathname.startsWith('/api/')) {
3
+ return false;
4
+ }
5
+ if (pathname.startsWith('/assets/')) {
6
+ return false;
7
+ }
8
+ return !/\.[a-z0-9]+$/i.test(pathname);
3
9
  }
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);
10
+ function resolveViteFetchHandler() {
11
+ const viteEnvs = Reflect.get(globalThis, '__nitro_vite_envs__');
12
+ if (!viteEnvs || typeof viteEnvs !== 'object') {
13
+ return undefined;
14
+ }
15
+ const ssrEnv = Reflect.get(viteEnvs, 'ssr');
16
+ if (!ssrEnv || typeof ssrEnv !== 'object') {
17
+ return undefined;
18
+ }
19
+ const fetchHandler = Reflect.get(ssrEnv, 'fetch');
20
+ return typeof fetchHandler === 'function' ? fetchHandler.bind(ssrEnv) : undefined;
19
21
  }
20
-
21
22
  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
- };
23
+ async fetch(request) {
24
+ const pathname = new URL(request.url).pathname;
25
+ if (!isPageRequest(pathname)) {
26
+ return;
27
+ }
28
+ return resolveViteFetchHandler()?.(request);
29
+ },
30
+ };
@@ -17,6 +17,8 @@ export function createFlowNitroConfig(options = {}) {
17
17
  const flowModules = loadFlowModules(projectRoot, flowConfig);
18
18
  const flowNitroConfig = { ...flowConfig.nitro || {} };
19
19
  const flowNitroHooks = flowNitroConfig.hooks || {};
20
+ const configuredNoExternals = Array.isArray(flowNitroConfig.noExternals) ? flowNitroConfig.noExternals : [];
21
+ const flowPackagePattern = /^@monkeyplus\/flow(?:\/.*)?$/;
20
22
  const flowRuntimeConfig = typeof flowModules.nitro.runtimeConfig.flow === "object" && flowModules.nitro.runtimeConfig.flow ? flowModules.nitro.runtimeConfig.flow : {};
21
23
  const moduleRuntimeConfig = { ...flowModules.nitro.runtimeConfig };
22
24
  delete moduleRuntimeConfig.flow;
@@ -55,6 +57,7 @@ export function createFlowNitroConfig(options = {}) {
55
57
  existingHooks: flowNitroHooks
56
58
  }),
57
59
  plugins: flowModules.nitro.plugins,
60
+ noExternals: [...configuredNoExternals, flowPackagePattern],
58
61
  handlers: flowModules.nitro.handlers,
59
62
  routeRules: {
60
63
  "/api/**": { cors: true },
@@ -1,11 +1,11 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { relative, resolve } from "node:path";
3
+ import ui from "@nuxt/ui/vite";
3
4
  import Vue from "@vitejs/plugin-vue";
4
5
  import { nitro } from "nitro/vite";
5
- import { defineConfig, normalizePath } from "vite";
6
6
  import IconsResolver from "unplugin-icons/resolver";
7
7
  import Icons from "unplugin-icons/vite";
8
- import ui from "@nuxt/ui/vite";
8
+ import { defineConfig, normalizePath } from "vite";
9
9
  import { resolveFlowConfig } from "../runtime/config.mjs";
10
10
  import { loadFlowModules } from "../runtime/modules.mjs";
11
11
  import { getPrerenderRoutes } from "../runtime/page-discovery.mjs";
@@ -208,6 +208,7 @@ export function createFlowViteConfig(options = {}) {
208
208
  const flowModules = loadFlowModules(projectRoot, flowConfig);
209
209
  const flowNitroConfig = { ...flowConfig.nitro || {} };
210
210
  const flowNitroHooks = flowNitroConfig.hooks || {};
211
+ const flowPackagePattern = /^@monkeyplus\/flow(?:\/.*)?$/;
211
212
  const userPrerenderRoutesHook = typeof flowNitroHooks["prerender:routes"] === "function" ? flowNitroHooks["prerender:routes"] : void 0;
212
213
  return defineConfig({
213
214
  plugins: [
@@ -232,7 +233,7 @@ export function createFlowViteConfig(options = {}) {
232
233
  ...flowNitroConfig,
233
234
  hooks: {
234
235
  ...flowNitroHooks,
235
- async "prerender:routes"(routes) {
236
+ "prerender:routes": async function(routes) {
236
237
  const discoveredRoutes = await getPrerenderRoutes(projectRoot, flowConfig);
237
238
  for (const route of discoveredRoutes) {
238
239
  routes.add(route);
@@ -268,6 +269,9 @@ export function createFlowViteConfig(options = {}) {
268
269
  "@": resolve(projectRoot, "src"),
269
270
  ...flowModules.vite.resolve.alias
270
271
  }
272
+ },
273
+ ssr: {
274
+ noExternal: [flowPackagePattern]
271
275
  }
272
276
  });
273
277
  }
@@ -1,7 +1,7 @@
1
+ import islands from "virtual:flow/islands";
1
2
  import { createSSRApp } from "vue";
2
3
  import { getClientHead } from "./head.mjs";
3
4
  import { installFlowVuePlugins } from "./vue.mjs";
4
- import islands from "virtual:flow/islands";
5
5
  function parseIslandProps(value) {
6
6
  if (!value) {
7
7
  return {};
@@ -2,5 +2,5 @@ import type { App } from 'vue';
2
2
  interface DefaultPlugins {
3
3
  ui: boolean;
4
4
  }
5
- export declare function installFlowVuePlugins(app: App, {}?: DefaultPlugins): App<any>;
5
+ export declare function installFlowVuePlugins(app: App, { ui: enableUi }?: DefaultPlugins): App<any>;
6
6
  export {};
@@ -1,6 +1,7 @@
1
1
  import ui from "@nuxt/ui/vue-plugin";
2
- export function installFlowVuePlugins(app, {} = { ui: true }) {
3
- if (ui)
2
+ export function installFlowVuePlugins(app, { ui: enableUi } = { ui: true }) {
3
+ if (enableUi) {
4
4
  app.use(ui);
5
+ }
5
6
  return app;
6
7
  }