@monkeyplus/flow 5.0.0-rc.9 → 5.0.0-rc.91

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.
@@ -1,6 +1,6 @@
1
1
  import type { RadixRouter } from 'radix3';
2
2
  import type { Hookable } from 'hookable';
3
- import type { FlowPage, RuntimeConfig } from '@monkeyplus/flow-schema';
3
+ import type { FlowAppUtils, FlowPage, RuntimeConfig } from '@monkeyplus/flow-schema';
4
4
  declare type HookResult = Promise<void> | void;
5
5
  export interface FlowAppHooks {
6
6
  'flow:pages': (page: FlowPage[]) => 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,11 +31,14 @@ 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;
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,266 @@
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 { h as hashId, u as uniq, i as isCSS } from './index.mjs';
10
+ import { ExternalsDefaults, isExternal } from 'externality';
11
+ import { joinURL } from 'ufo';
12
+ import 'hookable';
13
+ import 'url';
14
+ import 'unimport';
15
+ import 'unplugin';
16
+ import 'fs';
17
+ import 'escape-string-regexp';
18
+ import 'scule';
19
+ import 'consola';
20
+ import 'vite';
21
+ import 'nitropack';
22
+ import 'defu';
23
+ import 'h3';
24
+ import 'chokidar';
25
+ import 'untyped';
26
+ import 'mlly';
27
+ import '@rollup/plugin-replace';
28
+ import 'node:crypto';
29
+
30
+ function createIsExternal(viteServer, rootDir) {
31
+ const externalOpts = {
32
+ inline: [
33
+ /virtual:/,
34
+ /\.ts$/,
35
+ ...ExternalsDefaults.inline,
36
+ ...viteServer.config.ssr.noExternal
37
+ ],
38
+ external: [
39
+ ...viteServer.config.ssr.external,
40
+ /node_modules/
41
+ ],
42
+ resolve: {
43
+ type: "module",
44
+ extensions: [".ts", ".js", ".json", ".vue", ".mjs", ".jsx", ".tsx", ".wasm"]
45
+ }
46
+ };
47
+ return (id) => isExternal(id, rootDir, externalOpts);
48
+ }
49
+
50
+ async function writeManifest(ctx, extraEntries = []) {
51
+ const clientDist = resolve(ctx.flow.options.buildDir, "dist/client");
52
+ const serverDist = resolve(ctx.flow.options.buildDir, "dist/server");
53
+ const entries = [
54
+ "@vite/client",
55
+ "entry.mjs",
56
+ ...extraEntries
57
+ ];
58
+ const devClientManifest = {
59
+ publicPath: joinURL(ctx.flow.options.app.baseURL, ctx.flow.options.app.buildAssetsDir),
60
+ all: entries,
61
+ initial: entries,
62
+ async: [],
63
+ modules: {}
64
+ };
65
+ const clientManifest = ctx.flow.options.dev ? devClientManifest : await fse.readJSON(resolve(clientDist, "manifest.json"));
66
+ await fse.mkdirp(serverDist);
67
+ await fse.writeFile(resolve(serverDist, "client.manifest.json"), JSON.stringify(clientManifest, null, 2), "utf8");
68
+ await fse.writeFile(resolve(serverDist, "client.manifest.mjs"), `export default ${JSON.stringify(clientManifest, null, 2)}`, "utf8");
69
+ }
70
+
71
+ async function transformRequest(opts, id) {
72
+ if (id && id.startsWith("/@id/__x00__"))
73
+ id = `\0${id.slice("/@id/__x00__".length)}`;
74
+ if (id && id.startsWith("/@id/"))
75
+ id = id.slice("/@id/".length);
76
+ if (id && !id.startsWith("/@fs/") && id.startsWith("/")) {
77
+ const resolvedPath = resolve(opts.viteServer.config.root, `.${id}`);
78
+ if (existsSync(resolvedPath))
79
+ id = resolvedPath;
80
+ }
81
+ id = id.replace(/^\/?(?=\w:)/, "/@fs/");
82
+ const externalId = id.replace(/\?v=\w+$|^\/@fs/, "");
83
+ if (await opts.isExternal(externalId)) {
84
+ const path = builtinModules.includes(externalId.split("node:").pop()) ? externalId : isAbsolute(externalId) ? pathToFileURL(externalId).href : externalId;
85
+ return {
86
+ code: `(global, module, _, exports, importMeta, ssrImport, ssrDynamicImport, ssrExportAll) =>
87
+ ${genDynamicImport(path, { wrapper: false })}
88
+ .then(r => {
89
+ if (r.default && r.default.__esModule)
90
+ r = r.default
91
+ exports.default = r.default
92
+ ssrExportAll(r)
93
+ })
94
+ .catch(e => {
95
+ console.error(e)
96
+ throw new Error(${JSON.stringify(`[vite dev] Error loading external "${id}".`)})
97
+ })`,
98
+ deps: [],
99
+ dynamicDeps: []
100
+ };
101
+ }
102
+ const res = await opts.viteServer.transformRequest(id, { ssr: true }).catch((err) => {
103
+ console.warn(`[SSR] Error transforming ${id}:`, err);
104
+ }) || { code: "", map: {}, deps: [], dynamicDeps: [] };
105
+ const code = `async function (global, module, exports, __vite_ssr_exports__, __vite_ssr_import_meta__, __vite_ssr_import__, __vite_ssr_dynamic_import__, __vite_ssr_exportAll__) {
106
+ ${res.code || "/* empty */"};
107
+ }`;
108
+ return { code, deps: res.deps || [], dynamicDeps: res.dynamicDeps || [] };
109
+ }
110
+ async function transformRequestRecursive(opts, id, parent = "<entry>", chunks = {}) {
111
+ if (chunks[id]) {
112
+ chunks[id].parents.push(parent);
113
+ return;
114
+ }
115
+ const res = await transformRequest(opts, id);
116
+ const deps = uniq([...res.deps, ...res.dynamicDeps]);
117
+ chunks[id] = {
118
+ id,
119
+ code: res.code,
120
+ deps,
121
+ parents: [parent]
122
+ };
123
+ for (const dep of deps)
124
+ await transformRequestRecursive(opts, dep, id, chunks);
125
+ return Object.values(chunks);
126
+ }
127
+ async function bundleRequest(opts, entryURL) {
128
+ const chunks = await transformRequestRecursive(opts, entryURL);
129
+ const listIds = (ids) => ids.map((id) => `// - ${id} (${hashId(id)})`).join("\n");
130
+ const chunksCode = chunks.map((chunk) => `
131
+ // --------------------
132
+ // Request: ${chunk.id}
133
+ // Parents:
134
+ ${listIds(chunk.parents)}
135
+ // Dependencies:
136
+ ${listIds(chunk.deps)}
137
+ // --------------------
138
+ const ${hashId(`${chunk.id}-${chunk.code}`)} = ${chunk.code}
139
+ `).join("\n");
140
+ const manifestCode = `const __modules__ = ${genObjectFromRawEntries(chunks.map((chunk) => [chunk.id, hashId(`${chunk.id}-${chunk.code}`)]))}`;
141
+ const ssrModuleLoader = `
142
+ const __pendingModules__ = new Map()
143
+ const __pendingImports__ = new Map()
144
+ const __ssrContext__ = { global: globalThis }
145
+
146
+ function __ssrLoadModule__(url, urlStack = []) {
147
+ const pendingModule = __pendingModules__.get(url)
148
+ if (pendingModule) { return pendingModule }
149
+ const modulePromise = __instantiateModule__(url, urlStack)
150
+ __pendingModules__.set(url, modulePromise)
151
+ modulePromise.catch(() => { __pendingModules__.delete(url) })
152
+ .finally(() => { __pendingModules__.delete(url) })
153
+ return modulePromise
154
+ }
155
+
156
+ async function __instantiateModule__(url, urlStack) {
157
+ const mod = __modules__[url]
158
+ if (mod.stubModule) { return mod.stubModule }
159
+ const stubModule = { [Symbol.toStringTag]: 'Module' }
160
+ Object.defineProperty(stubModule, '__esModule', { value: true })
161
+ mod.stubModule = stubModule
162
+ // https://vitejs.dev/guide/api-hmr.html
163
+ const importMeta = { url, hot: { accept() {}, prune() {}, dispose() {}, invalidate() {}, decline() {}, on() {} } }
164
+ urlStack = urlStack.concat(url)
165
+ const isCircular = url => urlStack.includes(url)
166
+ const pendingDeps = []
167
+ const ssrImport = async (dep) => {
168
+ // TODO: Handle externals if dep[0] !== '.' | '/'
169
+ if (!isCircular(dep) && !__pendingImports__.get(dep)?.some(isCircular)) {
170
+ pendingDeps.push(dep)
171
+ if (pendingDeps.length === 1) {
172
+ __pendingImports__.set(url, pendingDeps)
173
+ }
174
+ await __ssrLoadModule__(dep, urlStack)
175
+ if (pendingDeps.length === 1) {
176
+ __pendingImports__.delete(url)
177
+ } else {
178
+ pendingDeps.splice(pendingDeps.indexOf(dep), 1)
179
+ }
180
+ }
181
+ return __modules__[dep].stubModule
182
+ }
183
+ function ssrDynamicImport (dep) {
184
+ // TODO: Handle dynamic import starting with . relative to url
185
+ return ssrImport(dep)
186
+ }
187
+
188
+ function ssrExportAll(sourceModule) {
189
+ for (const key in sourceModule) {
190
+ if (key !== 'default') {
191
+ try {
192
+ Object.defineProperty(stubModule, key, {
193
+ enumerable: true,
194
+ configurable: true,
195
+ get() { return sourceModule[key] }
196
+ })
197
+ } catch (_err) { }
198
+ }
199
+ }
200
+ }
201
+
202
+ const cjsModule = {
203
+ get exports () {
204
+ return stubModule.default
205
+ },
206
+ set exports (v) {
207
+ stubModule.default = v
208
+ },
209
+ }
210
+
211
+ await mod(
212
+ __ssrContext__.global,
213
+ cjsModule,
214
+ stubModule.default,
215
+ stubModule,
216
+ importMeta,
217
+ ssrImport,
218
+ ssrDynamicImport,
219
+ ssrExportAll
220
+ )
221
+
222
+ return stubModule
223
+ }
224
+ `;
225
+ const code = [
226
+ chunksCode,
227
+ manifestCode,
228
+ ssrModuleLoader,
229
+ `export default await __ssrLoadModule__(${JSON.stringify(entryURL)})`
230
+ ].join("\n\n");
231
+ return {
232
+ code,
233
+ ids: chunks.map((i) => i.id)
234
+ };
235
+ }
236
+ async function initViteDevBundler(ctx, onBuild) {
237
+ const viteServer = ctx.ssrServer;
238
+ const options = {
239
+ viteServer,
240
+ isExternal: createIsExternal(viteServer, ctx.nuxt.options.rootDir)
241
+ };
242
+ const _doBuild = async () => {
243
+ const start = Date.now();
244
+ const { code, ids } = await bundleRequest(options, ctx.entry);
245
+ await fse.ensureFile(resolve(ctx.flow.options.buildDir, "dist/server/server.mjs"));
246
+ await fse.writeFile(resolve(ctx.nuxt.options.buildDir, "dist/server/server.mjs"), code, "utf-8");
247
+ await writeManifest(ctx, ids.filter(isCSS).map((i) => i.slice(1)));
248
+ const time = Date.now() - start;
249
+ logger.success(`Vite server built in ${time}ms`);
250
+ await onBuild();
251
+ ctx.flow.callHook("bundler:change", {});
252
+ };
253
+ const doBuild = debounce(_doBuild);
254
+ await _doBuild();
255
+ viteServer.watcher.on("all", (_event, file) => {
256
+ if (file.includes("/pages/"))
257
+ return;
258
+ file = normalize(file);
259
+ if (file.indexOf(ctx.nuxt.options.buildDir) === 0 || isIgnoredFlow(file))
260
+ return;
261
+ doBuild();
262
+ });
263
+ ctx.nuxt.hook("app:templatesGenerated", () => doBuild());
264
+ }
265
+
266
+ export { bundleRequest, initViteDevBundler };