@monkeyplus/flow 5.0.0-rc.23 → 5.0.0-rc.230

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 (32) hide show
  1. package/dist/app/entry.async.d.ts +3 -0
  2. package/dist/app/entry.async.mjs +1 -0
  3. package/dist/app/entry.d.ts +2 -2
  4. package/dist/app/entry.mjs +3 -2
  5. package/dist/app/flow.d.ts +12 -3
  6. package/dist/app/flow.mjs +3 -0
  7. package/dist/chunks/dev-bundler.mjs +277 -0
  8. package/dist/core/runtime/client.manifest.d.mts +2 -0
  9. package/dist/core/runtime/client.manifest.mjs +6 -0
  10. package/dist/core/runtime/nitro/flow.d.ts +2 -1
  11. package/dist/core/runtime/nitro/flow.mjs +8 -14
  12. package/dist/core/runtime/nitro/renderer.d.ts +1 -1
  13. package/dist/core/runtime/nitro/renderer.mjs +68 -14
  14. package/dist/core/runtime/vite-node-shared.d.mts +1 -0
  15. package/dist/core/runtime/vite-node-shared.d.ts +8 -0
  16. package/dist/core/runtime/vite-node-shared.mjs +3 -0
  17. package/dist/core/runtime/vite-node.d.mts +2 -0
  18. package/dist/core/runtime/vite-node.mjs +41 -0
  19. package/dist/head/runtime/plugin.mjs +0 -1
  20. package/dist/index.mjs +528 -767
  21. package/dist/pages/runtime/helpers/index.d.ts +1 -1
  22. package/dist/pages/runtime/helpers/index.mjs +14 -3
  23. package/dist/pages/runtime/index.d.ts +10 -3
  24. package/dist/pages/runtime/index.mjs +5 -1
  25. package/dist/pages/runtime/pages.mjs +134 -0
  26. package/package.json +40 -36
  27. package/dist/pages/runtime/plugin.mjs +0 -71
  28. package/dist/vite-client/runtime/injectManifest.d.ts +0 -26
  29. package/dist/vite-client/runtime/injectManifest.mjs +0 -104
  30. package/dist/vite-client/runtime/plugin.d.ts +0 -2
  31. package/dist/vite-client/runtime/plugin.mjs +0 -26
  32. /package/dist/pages/runtime/{plugin.d.ts → pages.d.ts} +0 -0
@@ -0,0 +1,3 @@
1
+ import type { RuntimeConfig } from '@monkeyplus/flow-schema';
2
+ declare const _default: (ctx?: RuntimeConfig) => Promise<any>;
3
+ export default _default;
@@ -0,0 +1 @@
1
+ export default (ctx) => import("#app/entry").then((m) => m.default(ctx));
@@ -1,3 +1,3 @@
1
1
  import type { RuntimeConfig } from '@monkeyplus/flow-schema';
2
- declare const _default: (runtimeConfig: RuntimeConfig) => Promise<import("./flow").FlowApp>;
3
- export default _default;
2
+ export declare const entry: (runtimeConfig: RuntimeConfig) => Promise<import("./flow").FlowApp>;
3
+ export default entry;
@@ -1,7 +1,7 @@
1
1
  import { config, configure, render } from "eta";
2
2
  import { applyPlugins, createFlowApp } from "./flow.mjs";
3
3
  import plugins from "#build/plugins";
4
- export default async (runtimeConfig) => {
4
+ export const entry = async (runtimeConfig) => {
5
5
  const flow = createFlowApp({ runtimeConfig });
6
6
  try {
7
7
  await applyPlugins(flow, plugins);
@@ -17,7 +17,8 @@ export default async (runtimeConfig) => {
17
17
  });
18
18
  };
19
19
  } catch (err) {
20
- console.log(err);
20
+ console.error(err);
21
21
  }
22
22
  return flow;
23
23
  };
24
+ export default entry;
@@ -1,7 +1,7 @@
1
1
  import type { RadixRouter } from 'radix3';
2
2
  import type { Hookable } from 'hookable';
3
- import type { FlowPage, RuntimeConfig } from '@monkeyplus/flow-schema';
4
- declare type HookResult = Promise<void> | void;
3
+ import type { FlowAppUtils, FlowPage, RuntimeConfig } from '@monkeyplus/flow-schema';
4
+ type HookResult = Promise<void> | void;
5
5
  export interface FlowAppHooks {
6
6
  'flow:pages': (page: FlowPage[]) => HookResult;
7
7
  'eta:plugin': (options: any) => HookResult;
@@ -11,6 +11,11 @@ export interface FlowAppHooks {
11
11
  bodyStart: string[];
12
12
  }) => HookResult;
13
13
  'page:links': (links: string[]) => HookResult;
14
+ 'page:context': (ctx: {
15
+ page: string;
16
+ localeCode: string;
17
+ ctx: any;
18
+ }, utils: any, data: Record<string, any>) => HookResult;
14
19
  'page:chunks': (bundle: string, scripts: {
15
20
  head: string[];
16
21
  body: string[];
@@ -27,16 +32,20 @@ export interface FlowApp {
27
32
  callHook: FlowApp['hooks']['callHook'];
28
33
  eta: any;
29
34
  app: {
30
- utils: Record<string, any>;
35
+ utils: FlowAppUtils;
31
36
  plugins: Record<string, any>;
32
37
  globals: Record<string, any>;
33
38
  };
34
39
  engine: string;
40
+ site: {
41
+ origin: string;
42
+ };
35
43
  engines: Record<string, (template: string) => string>;
36
44
  generate: boolean;
37
45
  render: (view: Record<string, string>, data: any) => Promise<string> | void | string;
38
46
  provide: (name: string, value: any) => void;
39
47
  setUtil: (name: string, value: any) => void;
48
+ $config: RuntimeConfig;
40
49
  [key: string]: any;
41
50
  }
42
51
  export declare const FlowPluginIndicator = "__flow_plugin";
package/dist/app/flow.mjs CHANGED
@@ -20,6 +20,9 @@ export function createFlowApp({ runtimeConfig }) {
20
20
  },
21
21
  engine: "eta",
22
22
  engines: {},
23
+ site: {
24
+ origin: `${process.env.URL || "http://localhost"}`
25
+ },
23
26
  generate: runtimeConfig.generate
24
27
  };
25
28
  flowApp.engines.eta = (template) => {
@@ -0,0 +1,277 @@
1
+ import { pathToFileURL } from 'node:url';
2
+ import { existsSync } from 'node:fs';
3
+ import { builtinModules } from 'node:module';
4
+ import { resolve, normalize, isAbsolute } from 'pathe';
5
+ import { genObjectFromRawEntries, genDynamicImport } from 'knitwork';
6
+ import fse from 'fs-extra';
7
+ import { debounce } from 'perfect-debounce';
8
+ import { isIgnoredFlow, logger } from '@monkeyplus/flow-kit';
9
+ import { hash } from 'ohash';
10
+ import { ExternalsDefaults, isExternal } from 'externality';
11
+ import { withTrailingSlash, withoutLeadingSlash } from 'ufo';
12
+ import escapeRE from 'escape-string-regexp';
13
+ import { normalizeViteManifest } from 'vue-bundle-renderer';
14
+
15
+ function uniq(arr) {
16
+ return Array.from(new Set(arr));
17
+ }
18
+ const IS_CSS_RE = /\.(?:css|scss|sass|postcss|less|stylus|styl)(\?[^.]+)?$/;
19
+ function isCSS(file) {
20
+ return IS_CSS_RE.test(file);
21
+ }
22
+ function hashId(id) {
23
+ return `$id_${hash(id)}`;
24
+ }
25
+
26
+ function createIsExternal(viteServer, rootDir) {
27
+ const externalOpts = {
28
+ inline: [
29
+ /virtual:/,
30
+ /\.ts$/,
31
+ ...ExternalsDefaults.inline || [],
32
+ ...viteServer.config.ssr.noExternal
33
+ ],
34
+ external: [
35
+ ...viteServer.config.ssr.external || [],
36
+ /node_modules/
37
+ ],
38
+ resolve: {
39
+ type: "module",
40
+ extensions: [".ts", ".js", ".json", ".vue", ".mjs", ".jsx", ".tsx", ".wasm"]
41
+ }
42
+ };
43
+ return (id) => isExternal(id, rootDir, externalOpts);
44
+ }
45
+
46
+ async function writeManifest(ctx, css = []) {
47
+ const clientDist = resolve(ctx.nuxt.options.buildDir, "dist/client");
48
+ const serverDist = resolve(ctx.nuxt.options.buildDir, "dist/server");
49
+ const devClientManifest = {
50
+ "@vite/client": {
51
+ isEntry: true,
52
+ file: "@vite/client",
53
+ css,
54
+ module: true,
55
+ resourceType: "script"
56
+ },
57
+ [ctx.entry]: {
58
+ isEntry: true,
59
+ file: ctx.entry,
60
+ module: true,
61
+ resourceType: "script"
62
+ }
63
+ };
64
+ const clientManifest = ctx.nuxt.options.dev ? devClientManifest : await fse.readJSON(resolve(clientDist, "manifest.json"));
65
+ const buildAssetsDir = withTrailingSlash(withoutLeadingSlash(ctx.nuxt.options.app.buildAssetsDir));
66
+ const BASE_RE = new RegExp(`^${escapeRE(buildAssetsDir)}`);
67
+ for (const key in clientManifest) {
68
+ if (clientManifest[key].file)
69
+ clientManifest[key].file = clientManifest[key].file.replace(BASE_RE, "");
70
+ for (const item of ["css", "assets"]) {
71
+ if (clientManifest[key][item])
72
+ clientManifest[key][item] = clientManifest[key][item].map((i) => i.replace(BASE_RE, ""));
73
+ }
74
+ }
75
+ await fse.mkdirp(serverDist);
76
+ const manifest = normalizeViteManifest(clientManifest);
77
+ await ctx.nuxt.callHook("build:manifest", manifest);
78
+ await fse.writeFile(resolve(serverDist, "client.manifest.json"), JSON.stringify(manifest, null, 2), "utf8");
79
+ await fse.writeFile(resolve(serverDist, "client.manifest.mjs"), `export default ${JSON.stringify(manifest, null, 2)}`, "utf8");
80
+ }
81
+
82
+ async function transformRequest(opts, id) {
83
+ if (id && id.startsWith("/@id/__x00__"))
84
+ id = `\0${id.slice("/@id/__x00__".length)}`;
85
+ if (id && id.startsWith("/@id/"))
86
+ id = id.slice("/@id/".length);
87
+ if (id && !id.startsWith("/@fs/") && id.startsWith("/")) {
88
+ const resolvedPath = resolve(opts.viteServer.config.root, `.${id}`);
89
+ if (existsSync(resolvedPath))
90
+ id = resolvedPath;
91
+ }
92
+ id = id.replace(/^\/?(?=\w:)/, "/@fs/");
93
+ const externalId = id.replace(/\?v=\w+$|^\/@fs/, "");
94
+ if (await opts.isExternal(externalId)) {
95
+ const path = builtinModules.includes(externalId.split("node:").pop()) ? externalId : isAbsolute(externalId) ? pathToFileURL(externalId).href : externalId;
96
+ return {
97
+ code: `(global, module, _, exports, importMeta, ssrImport, ssrDynamicImport, ssrExportAll) =>
98
+ ${genDynamicImport(path, { wrapper: false })}
99
+ .then(r => {
100
+ if (r.default && r.default.__esModule)
101
+ r = r.default
102
+ exports.default = r.default
103
+ ssrExportAll(r)
104
+ })
105
+ .catch(e => {
106
+ console.error(e)
107
+ throw new Error(${JSON.stringify(`[vite dev] Error loading external "${id}".`)})
108
+ })`,
109
+ deps: [],
110
+ dynamicDeps: []
111
+ };
112
+ }
113
+ const res = await opts.viteServer.transformRequest(id, { ssr: true }).catch((err) => {
114
+ console.warn(`[SSR] Error transforming ${id}:`, err);
115
+ }) || { code: "", map: {}, deps: [], dynamicDeps: [] };
116
+ const code = `async function (global, module, exports, __vite_ssr_exports__, __vite_ssr_import_meta__, __vite_ssr_import__, __vite_ssr_dynamic_import__, __vite_ssr_exportAll__) {
117
+ ${res.code || "/* empty */"};
118
+ }`;
119
+ return { code, deps: res.deps || [], dynamicDeps: res.dynamicDeps || [] };
120
+ }
121
+ async function transformRequestRecursive(opts, id, parent = "<entry>", chunks = {}) {
122
+ if (chunks[id]) {
123
+ chunks[id].parents.push(parent);
124
+ return;
125
+ }
126
+ const res = await transformRequest(opts, id);
127
+ const deps = uniq([...res.deps, ...res.dynamicDeps]);
128
+ chunks[id] = {
129
+ id,
130
+ code: res.code,
131
+ deps,
132
+ parents: [parent]
133
+ };
134
+ for (const dep of deps)
135
+ await transformRequestRecursive(opts, dep, id, chunks);
136
+ return Object.values(chunks);
137
+ }
138
+ async function bundleRequest(opts, entryURL) {
139
+ const chunks = await transformRequestRecursive(opts, entryURL);
140
+ const listIds = (ids) => ids.map((id) => `// - ${id} (${hashId(id)})`).join("\n");
141
+ const chunksCode = chunks.map((chunk) => `
142
+ // --------------------
143
+ // Request: ${chunk.id}
144
+ // Parents:
145
+ ${listIds(chunk.parents)}
146
+ // Dependencies:
147
+ ${listIds(chunk.deps)}
148
+ // --------------------
149
+ const ${hashId(`${chunk.id}-${chunk.code}`)} = ${chunk.code}
150
+ `).join("\n");
151
+ const manifestCode = `const __modules__ = ${genObjectFromRawEntries(chunks.map((chunk) => [chunk.id, hashId(`${chunk.id}-${chunk.code}`)]))}`;
152
+ const ssrModuleLoader = `
153
+ const __pendingModules__ = new Map()
154
+ const __pendingImports__ = new Map()
155
+ const __ssrContext__ = { global: globalThis }
156
+
157
+ function __ssrLoadModule__(url, urlStack = []) {
158
+ const pendingModule = __pendingModules__.get(url)
159
+ if (pendingModule) { return pendingModule }
160
+ const modulePromise = __instantiateModule__(url, urlStack)
161
+ __pendingModules__.set(url, modulePromise)
162
+ modulePromise.catch(() => { __pendingModules__.delete(url) })
163
+ .finally(() => { __pendingModules__.delete(url) })
164
+ return modulePromise
165
+ }
166
+
167
+ async function __instantiateModule__(url, urlStack) {
168
+ const mod = __modules__[url]
169
+ if (mod.stubModule) { return mod.stubModule }
170
+ const stubModule = { [Symbol.toStringTag]: 'Module' }
171
+ Object.defineProperty(stubModule, '__esModule', { value: true })
172
+ mod.stubModule = stubModule
173
+ // https://vitejs.dev/guide/api-hmr.html
174
+ const importMeta = { url, hot: { accept() {}, prune() {}, dispose() {}, invalidate() {}, decline() {}, on() {} } }
175
+ urlStack = urlStack.concat(url)
176
+ const isCircular = url => urlStack.includes(url)
177
+ const pendingDeps = []
178
+ const ssrImport = async (dep) => {
179
+ // TODO: Handle externals if dep[0] !== '.' | '/'
180
+ if (!isCircular(dep) && !__pendingImports__.get(dep)?.some(isCircular)) {
181
+ pendingDeps.push(dep)
182
+ if (pendingDeps.length === 1) {
183
+ __pendingImports__.set(url, pendingDeps)
184
+ }
185
+ await __ssrLoadModule__(dep, urlStack)
186
+ if (pendingDeps.length === 1) {
187
+ __pendingImports__.delete(url)
188
+ } else {
189
+ pendingDeps.splice(pendingDeps.indexOf(dep), 1)
190
+ }
191
+ }
192
+ return __modules__[dep].stubModule
193
+ }
194
+ function ssrDynamicImport (dep) {
195
+ // TODO: Handle dynamic import starting with . relative to url
196
+ return ssrImport(dep)
197
+ }
198
+
199
+ function ssrExportAll(sourceModule) {
200
+ for (const key in sourceModule) {
201
+ if (key !== 'default') {
202
+ try {
203
+ Object.defineProperty(stubModule, key, {
204
+ enumerable: true,
205
+ configurable: true,
206
+ get() { return sourceModule[key] }
207
+ })
208
+ } catch (_err) { }
209
+ }
210
+ }
211
+ }
212
+
213
+ const cjsModule = {
214
+ get exports () {
215
+ return stubModule.default
216
+ },
217
+ set exports (v) {
218
+ stubModule.default = v
219
+ },
220
+ }
221
+
222
+ await mod(
223
+ __ssrContext__.global,
224
+ cjsModule,
225
+ stubModule.default,
226
+ stubModule,
227
+ importMeta,
228
+ ssrImport,
229
+ ssrDynamicImport,
230
+ ssrExportAll
231
+ )
232
+
233
+ return stubModule
234
+ }
235
+ `;
236
+ const code = [
237
+ chunksCode,
238
+ manifestCode,
239
+ ssrModuleLoader,
240
+ `export default await __ssrLoadModule__(${JSON.stringify(entryURL)})`
241
+ ].join("\n\n");
242
+ return {
243
+ code,
244
+ ids: chunks.map((i) => i.id)
245
+ };
246
+ }
247
+ async function initViteDevBundler(ctx, onBuild) {
248
+ const viteServer = ctx.ssrServer;
249
+ const options = {
250
+ viteServer,
251
+ isExternal: createIsExternal(viteServer, ctx.nuxt.options.rootDir)
252
+ };
253
+ const _doBuild = async () => {
254
+ const start = Date.now();
255
+ const { code, ids } = await bundleRequest(options, ctx.entry);
256
+ await fse.ensureFile(resolve(ctx.nuxt.options.buildDir, "dist/server/server.mjs"));
257
+ await fse.writeFile(resolve(ctx.nuxt.options.buildDir, "dist/server/server.mjs"), code, "utf-8");
258
+ await writeManifest(ctx, ids.filter(isCSS).map((i) => i.slice(1)));
259
+ const time = Date.now() - start;
260
+ logger.success(`Vite server built in ${time}ms`);
261
+ ctx.nuxt.callHook("bundler:change", {});
262
+ await onBuild();
263
+ };
264
+ const doBuild = debounce(_doBuild);
265
+ await _doBuild();
266
+ viteServer.watcher.on("all", (_event, file) => {
267
+ if (file.includes("/pages/"))
268
+ return;
269
+ file = normalize(file);
270
+ if (file.indexOf(ctx.nuxt.options.buildDir) === 0 || isIgnoredFlow(file))
271
+ return;
272
+ doBuild();
273
+ });
274
+ ctx.nuxt.hook("app:templatesGenerated", () => doBuild());
275
+ }
276
+
277
+ export { bundleRequest, initViteDevBundler };
@@ -0,0 +1,2 @@
1
+ declare function _default(): Promise<any>;
2
+ export default _default;
@@ -0,0 +1,6 @@
1
+ import { $fetch } from 'ohmyfetch';
2
+ import { getViteNodeOptions } from './vite-node-shared.mjs';
3
+
4
+ const viteNodeOptions = getViteNodeOptions();
5
+
6
+ export default () => $fetch('/manifest', { baseURL: viteNodeOptions.baseURL });
@@ -1,3 +1,4 @@
1
1
  import 'node-fetch-native/polyfill';
2
- declare const _default: import("h3").EventHandler<void>;
2
+ export declare const getFlowRenderer: () => Promise<any>;
3
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
3
4
  export default _default;
@@ -1,26 +1,20 @@
1
1
  import "node-fetch-native/polyfill";
2
- import { defineEventHandler } from "h3";
2
+ import { eventHandler } from "h3";
3
3
  import { defineCachedFunction, useRuntimeConfig } from "#internal/nitro";
4
- const getServerApp = cachedImport(() => import("#server"));
5
- const getFlowRenderer = cachedResult(async () => {
4
+ const getServerApp = () => import("#server").then((r) => r?.default?.default || r?.default || r);
5
+ export const getFlowRenderer = lazyCachedFunction(async () => {
6
6
  const createFlowApp = await getServerApp();
7
7
  if (!createFlowApp)
8
8
  throw new Error("Server bundle is not available");
9
9
  return createFlowApp(useRuntimeConfig());
10
10
  });
11
- export default defineEventHandler(async ({ context }) => {
11
+ export default eventHandler(async (event) => {
12
12
  const flow = await getFlowRenderer();
13
- context.flow = flow;
14
- context.render = flow.render;
15
- context.defineCachedFunction = defineCachedFunction;
13
+ event.context.flow = flow;
14
+ event.context.render = flow.render;
15
+ event.context.defineCachedFunction = defineCachedFunction;
16
16
  });
17
- function _interopDefault(e) {
18
- return e && typeof e === "object" && "default" in e ? e.default : e;
19
- }
20
- function cachedImport(importer) {
21
- return cachedResult(() => importer().then(_interopDefault).then(_interopDefault));
22
- }
23
- function cachedResult(fn) {
17
+ function lazyCachedFunction(fn) {
24
18
  let res = null;
25
19
  return () => {
26
20
  if (res === null)
@@ -1,2 +1,2 @@
1
- declare const _default: import("h3").EventHandler<any>;
1
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<any>>;
2
2
  export default _default;
@@ -1,46 +1,100 @@
1
- import { eventHandler, useQuery } from "h3";
1
+ import { eventHandler, getQuery, readBody } from "h3";
2
+ import { joinURL } from "ufo";
3
+ import { getFlowRenderer } from "./flow.mjs";
4
+ const normalizeCall = async (seo, ctx) => {
5
+ if (typeof seo === "function")
6
+ return await seo(ctx);
7
+ return seo || {};
8
+ };
9
+ function replacePath(_path, url) {
10
+ if (typeof url === "string") {
11
+ _path = joinURL(_path.replace("/**", ""), url);
12
+ } else {
13
+ Object.entries(url).forEach(([key, value]) => {
14
+ _path = _path.replace(`:${key}`, value);
15
+ });
16
+ if (url._ && _path.endsWith("**"))
17
+ _path = joinURL(_path.replace("/**", ""), url._);
18
+ }
19
+ return _path;
20
+ }
2
21
  export default eventHandler(async (event) => {
3
22
  const url = event.req.url?.split("?")[0];
4
- const flow = event.context.flow;
23
+ const flow = await getFlowRenderer();
5
24
  if (url.includes("_urls")) {
6
- const urls = await flow.app.utils.getUrls();
25
+ const urls = await flow.app.utils.getUrls(false, true);
7
26
  return urls;
8
27
  }
9
28
  const scripts = { head: [], bodyStart: [], bodyEnd: [] };
10
29
  const chunks = { head: [], body: [] };
11
30
  const generate = {};
12
- const { page, locale, params, view } = flow.router.byUrl.lookup(url) || {};
31
+ const { page, locale, params, view, name: pageName, path: pagePath } = flow.router.byUrl.lookup(url) || {};
13
32
  if (!page)
14
33
  return;
15
- await flow.callHook("page:scripts", scripts);
16
34
  if (view.bundle)
17
35
  await flow.callHook("page:chunks", view.bundle, chunks);
18
36
  const templateContext = {};
19
- const dynamic = params?._;
20
- const contextPage = {};
37
+ const isDynamic = !!Object.keys(params || {});
38
+ const contextPage = {
39
+ chunks,
40
+ locale,
41
+ _post: {}
42
+ };
21
43
  const contextInject = {};
22
44
  const utils = Object.assign({ getLocale: () => locale, injectContext: (key, value) => contextInject[key] = value, defineCachedFunction: event.context.defineCachedFunction }, flow.app.utils);
45
+ const contextHooks = {};
46
+ if (event.node.req.method.toLowerCase() === "post") {
47
+ const body = await readBody(event);
48
+ contextPage._post = body;
49
+ }
23
50
  templateContext.url = url;
51
+ templateContext.baseURL = flow.$config.app.baseURL;
52
+ const pageObject = {
53
+ name: pageName,
54
+ pathname: url,
55
+ url: joinURL(flow.site.origin, templateContext.baseURL, `${url}`),
56
+ origin: flow.site.origin,
57
+ joinOrigin: (_url, includeBase) => joinURL(flow.site.origin, includeBase ? templateContext.baseURL : "/", _url)
58
+ };
59
+ templateContext.page = pageObject;
60
+ contextPage.page = pageObject;
24
61
  templateContext.locale = locale;
25
62
  templateContext.view = view;
26
- if (dynamic && page.dynamic) {
63
+ if (isDynamic && page.dynamic) {
27
64
  const pages = await page.dynamic.method({ locale, utils });
28
- const dynamicPage = pages.find((_page) => _page.url === dynamic);
65
+ const dynamicPage = pages.find((_page) => {
66
+ const aUrl = replacePath(pagePath, _page.url);
67
+ const bUrl = replacePath(pagePath, params);
68
+ return aUrl === bUrl;
69
+ });
29
70
  if (!dynamicPage)
30
71
  return;
31
72
  if (page.dynamic.assign)
32
73
  templateContext[page.dynamic.assign] = dynamicPage.context;
33
74
  contextPage.dynamic = dynamicPage;
34
75
  }
76
+ await flow.callHook("page:context", { localeCode: locale.code, page: pageName, ctx: contextPage }, utils, contextHooks);
35
77
  templateContext.seo = {};
36
- templateContext.context = {};
37
- templateContext.sharedContext = {};
78
+ const sharedContext = await utils.getSharedContext({
79
+ ...contextHooks,
80
+ ...contextPage,
81
+ utils
82
+ });
83
+ templateContext.sharedContext = sharedContext || {};
38
84
  templateContext.utils = utils;
39
- templateContext.seo = await page.seo?.({ ...contextPage, utils }) || {};
40
- templateContext.context = await page.context?.({ ...contextPage, utils }) || {};
85
+ templateContext.seo = await normalizeCall(page?.seo, { ...contextHooks, ...contextPage, utils, sharedContext });
86
+ const contexData = await page.context?.({ ...contextHooks, ...contextPage, utils, sharedContext }) || {};
87
+ templateContext.context = {
88
+ ...contextHooks,
89
+ ...contexData
90
+ };
91
+ await flow.callHook("page:scripts", scripts, templateContext);
41
92
  templateContext.getHeadScripts = () => {
42
93
  return scripts.head.join("\n");
43
94
  };
95
+ templateContext.getBodyEndScripts = () => {
96
+ return scripts.bodyEnd.join("\n");
97
+ };
44
98
  templateContext.getHeadChunks = () => {
45
99
  return chunks.head.join("\n");
46
100
  };
@@ -50,7 +104,7 @@ export default eventHandler(async (event) => {
50
104
  templateContext.getInjectContext = () => {
51
105
  return `<script id="__FLOW_DATA__" type="application/json">${JSON.stringify(contextInject)}<\/script>`;
52
106
  };
53
- const query = useQuery(event);
107
+ const query = getQuery(event);
54
108
  if (query?.context)
55
109
  return { ...templateContext, utils: Object.keys(utils) };
56
110
  let html;
@@ -0,0 +1 @@
1
+ export function getViteNodeOptions(): any;
@@ -0,0 +1,8 @@
1
+ export interface ViteNodeRuntimeOptions {
2
+ baseURL: string
3
+ rootDir: string
4
+ entryPath: string
5
+ base: string
6
+ }
7
+
8
+ export function getViteNodeOptions (): ViteNodeRuntimeOptions;
@@ -0,0 +1,3 @@
1
+ export function getViteNodeOptions() {
2
+ return JSON.parse(process.env.NUXT_VITE_NODE_OPTIONS || '{}');
3
+ }
@@ -0,0 +1,2 @@
1
+ declare function _default(ssrContext: any): Promise<any>;
2
+ export default _default;
@@ -0,0 +1,41 @@
1
+ import { performance } from 'node:perf_hooks';
2
+ import { ViteNodeRunner } from 'vite-node/client';
3
+ import { $fetch } from 'ohmyfetch';
4
+ import consola from 'consola';
5
+ import { getViteNodeOptions } from './vite-node-shared.mjs';
6
+
7
+ const viteNodeOptions = getViteNodeOptions();
8
+ const runner = new ViteNodeRunner({
9
+ root: viteNodeOptions.root, // Equals to Nuxt `srcDir`
10
+ base: viteNodeOptions.base,
11
+ async fetchModule(id) {
12
+ return await $fetch(`/module/${encodeURI(id)}`, {
13
+ baseURL: viteNodeOptions.baseURL,
14
+ });
15
+ },
16
+ });
17
+
18
+ let render;
19
+
20
+ export default async(ssrContext) => {
21
+ // Workaround for stub mode
22
+ // https://github.com/nuxt/framework/pull/3983
23
+ process.server = true;
24
+
25
+ // Invalidate cache for files changed since last rendering
26
+ const invalidates = await $fetch('/invalidates', {
27
+ baseURL: viteNodeOptions.baseURL,
28
+ });
29
+ const updates = runner.moduleCache.invalidateDepTree(invalidates);
30
+
31
+ // Execute SSR bundle on demand
32
+ const start = performance.now();
33
+ render = render || (await runner.executeFile(viteNodeOptions.entryPath)).default;
34
+ if (updates.size) {
35
+ const time = Math.round((performance.now() - start) * 1000) / 1000;
36
+ consola.success(`Vite server hmr ${updates.size} files`, time ? `in ${time}ms` : '');
37
+ }
38
+
39
+ const result = await render(ssrContext);
40
+ return result;
41
+ };
@@ -1,6 +1,5 @@
1
1
  import { defineFlowPlugin } from "#app";
2
2
  export default defineFlowPlugin((flow) => {
3
3
  flow._useHead = (_meta) => {
4
- console.log(_meta);
5
4
  };
6
5
  });