@monkeyplus/flow 5.0.0-rc.12 → 5.0.0-rc.121

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.
@@ -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));
@@ -17,7 +17,7 @@ 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
  };
@@ -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,10 @@ 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
+ }, utils: any, data: Record<string, any>) => HookResult;
14
18
  'page:chunks': (bundle: string, scripts: {
15
19
  head: string[];
16
20
  body: string[];
@@ -27,16 +31,20 @@ export interface FlowApp {
27
31
  callHook: FlowApp['hooks']['callHook'];
28
32
  eta: any;
29
33
  app: {
30
- utils: Record<string, any>;
34
+ utils: FlowAppUtils;
31
35
  plugins: Record<string, any>;
32
36
  globals: Record<string, any>;
33
37
  };
34
38
  engine: string;
39
+ site: {
40
+ origin: string;
41
+ };
35
42
  engines: Record<string, (template: string) => string>;
36
43
  generate: boolean;
37
44
  render: (view: Record<string, string>, data: any) => Promise<string> | void | string;
38
45
  provide: (name: string, value: any) => void;
39
46
  setUtil: (name: string, value: any) => void;
47
+ $config: RuntimeConfig;
40
48
  [key: string]: any;
41
49
  }
42
50
  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
+ export declare const getFlowRenderer: () => Promise<any>;
2
3
  declare const _default: import("h3").EventHandler<void>;
3
4
  export default _default;
@@ -1,25 +1,20 @@
1
- import { defineEventHandler } from "h3";
2
1
  import "node-fetch-native/polyfill";
3
- import { useRuntimeConfig } from "#internal/nitro";
4
- const getServerApp = cachedImport(() => import("#server"));
5
- const getFlowRenderer = cachedResult(async () => {
2
+ import { eventHandler } from "h3";
3
+ import { defineCachedFunction, useRuntimeConfig } from "#internal/nitro";
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;
13
+ event.context.flow = flow;
14
+ event.context.render = flow.render;
15
+ event.context.defineCachedFunction = defineCachedFunction;
15
16
  });
16
- function _interopDefault(e) {
17
- return e && typeof e === "object" && "default" in e ? e.default : e;
18
- }
19
- function cachedImport(importer) {
20
- return cachedResult(() => importer().then(_interopDefault).then(_interopDefault));
21
- }
22
- function cachedResult(fn) {
17
+ function lazyCachedFunction(fn) {
23
18
  let res = null;
24
19
  return () => {
25
20
  if (res === null)
@@ -1,46 +1,101 @@
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 dynamyc = params?._;
20
- const contextPage = {};
37
+ const isDynamic = !!Object.keys(params || {});
38
+ const contextPage = {
39
+ chunks,
40
+ locale
41
+ };
21
42
  const contextInject = {};
22
- const utils = Object.assign({ getLocale: () => locale, injectContext: (key, value) => contextInject[key] = value }, flow.app.utils);
43
+ const utils = Object.assign({ getLocale: () => locale, injectContext: (key, value) => contextInject[key] = value, defineCachedFunction: event.context.defineCachedFunction }, flow.app.utils);
44
+ const contextHooks = {
45
+ _post: {}
46
+ };
47
+ if (event.node.req.method.toLowerCase() === "post") {
48
+ const body = await readBody(event);
49
+ contextHooks._post = body;
50
+ }
51
+ await flow.callHook("page:context", { localeCode: locale.code, page: pageName }, utils, contextHooks);
23
52
  templateContext.url = url;
53
+ templateContext.baseURL = flow.$config.app.baseURL;
54
+ const pageObject = {
55
+ name: pageName,
56
+ pathname: url,
57
+ url: joinURL(flow.site.origin, templateContext.baseURL, `${url}`),
58
+ origin: flow.site.origin,
59
+ joinOrigin: (_url, includeBase) => joinURL(flow.site.origin, includeBase ? templateContext.baseURL : "/", _url)
60
+ };
61
+ templateContext.page = pageObject;
62
+ contextPage.page = pageObject;
24
63
  templateContext.locale = locale;
25
64
  templateContext.view = view;
26
- if (dynamyc && page.dynamic) {
65
+ if (isDynamic && page.dynamic) {
27
66
  const pages = await page.dynamic.method({ locale, utils });
28
- const dynamicPage = pages.find((_page) => _page.url === dynamyc);
67
+ const dynamicPage = pages.find((_page) => {
68
+ const aUrl = replacePath(pagePath, _page.url);
69
+ const bUrl = replacePath(pagePath, params);
70
+ return aUrl === bUrl;
71
+ });
29
72
  if (!dynamicPage)
30
73
  return;
31
74
  if (page.dynamic.assign)
32
75
  templateContext[page.dynamic.assign] = dynamicPage.context;
33
- contextPage.dynamyc = dynamicPage;
76
+ contextPage.dynamic = dynamicPage;
34
77
  }
35
78
  templateContext.seo = {};
36
- templateContext.context = {};
37
- templateContext.sharedContext = {};
79
+ const sharedContext = await utils.getSharedContext({
80
+ ...contextHooks,
81
+ ...contextPage,
82
+ utils
83
+ });
84
+ templateContext.sharedContext = sharedContext || {};
38
85
  templateContext.utils = utils;
39
- templateContext.seo = await page.seo?.({ ...contextPage, utils }) || {};
40
- templateContext.context = await page.context?.({ ...contextPage, utils }) || {};
86
+ templateContext.seo = await normalizeCall(page?.seo, { ...contextHooks, ...contextPage, utils, sharedContext });
87
+ const contexData = await page.context?.({ ...contextHooks, ...contextPage, utils, sharedContext }) || {};
88
+ templateContext.context = {
89
+ ...contextHooks,
90
+ ...contexData
91
+ };
92
+ await flow.callHook("page:scripts", scripts, templateContext);
41
93
  templateContext.getHeadScripts = () => {
42
94
  return scripts.head.join("\n");
43
95
  };
96
+ templateContext.getBodyEndScripts = () => {
97
+ return scripts.bodyEnd.join("\n");
98
+ };
44
99
  templateContext.getHeadChunks = () => {
45
100
  return chunks.head.join("\n");
46
101
  };
@@ -50,10 +105,14 @@ export default eventHandler(async (event) => {
50
105
  templateContext.getInjectContext = () => {
51
106
  return `<script id="__FLOW_DATA__" type="application/json">${JSON.stringify(contextInject)}<\/script>`;
52
107
  };
53
- const query = useQuery(event);
108
+ const query = getQuery(event);
54
109
  if (query?.context)
55
110
  return { ...templateContext, utils: Object.keys(utils) };
56
- let html = await flow.render(view, templateContext);
111
+ let html;
112
+ if (view.render)
113
+ html = await view.render(templateContext, event);
114
+ else
115
+ html = await flow.render(view, templateContext);
57
116
  event.res.setHeader("Content-Type", view.contentType || "text/html;charset=UTF-8");
58
117
  if (view.postRender)
59
118
  html = await view.postRender(html, event);
@@ -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,42 @@
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
+ console.log(viteNodeOptions);
9
+ const runner = new ViteNodeRunner({
10
+ root: viteNodeOptions.root, // Equals to Nuxt `srcDir`
11
+ base: viteNodeOptions.base,
12
+ async fetchModule(id) {
13
+ return await $fetch(`/module/${encodeURI(id)}`, {
14
+ baseURL: viteNodeOptions.baseURL,
15
+ });
16
+ },
17
+ });
18
+
19
+ let render;
20
+
21
+ export default async(ssrContext) => {
22
+ // Workaround for stub mode
23
+ // https://github.com/nuxt/framework/pull/3983
24
+ process.server = true;
25
+
26
+ // Invalidate cache for files changed since last rendering
27
+ const invalidates = await $fetch('/invalidates', {
28
+ baseURL: viteNodeOptions.baseURL,
29
+ });
30
+ const updates = runner.moduleCache.invalidateDepTree(invalidates);
31
+
32
+ // Execute SSR bundle on demand
33
+ const start = performance.now();
34
+ render = render || (await runner.executeFile(viteNodeOptions.entryPath)).default;
35
+ if (updates.size) {
36
+ const time = Math.round((performance.now() - start) * 1000) / 1000;
37
+ consola.success(`Vite server hmr ${updates.size} files`, time ? `in ${time}ms` : '');
38
+ }
39
+
40
+ const result = await render(ssrContext);
41
+ return result;
42
+ };
@@ -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
  });