@monkeyplus/flow 6.0.10 → 6.0.12

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 CHANGED
@@ -16,10 +16,21 @@
16
16
 
17
17
  El workspace consume la API fuente del root para mantener DX local, mientras que `dist/` genera un artefacto publicable con exports ya compilados.
18
18
 
19
+ ## Guia de uso
20
+
21
+ La guia detallada de authoring vive en `docs/authoring.md` e incluye:
22
+
23
+ - como registrar y configurar modulos
24
+ - como definir paginas estaticas y dinamicas
25
+ - como organizar templates, layouts y `*.context.ts`
26
+ - como trabajar con `FlowIsland` y `client/islands/*`
27
+
28
+ Si vas a crear o mantener una app en Flow, ese archivo deberia ser el punto de entrada principal.
29
+
19
30
  ## API pública
20
31
 
21
32
  ```ts
22
- import { defineFlowConfig, definePage } from '@monkeyplus/flow';
33
+ import { defineFlowConfig, defineLayoutContext, definePage } from '@monkeyplus/flow';
23
34
  import { FlowIsland } from '@monkeyplus/flow/components';
24
35
  import { getClientHead } from '@monkeyplus/flow/head';
25
36
  import sitemapModule from '@monkeyplus/flow/modules/sitemap';
@@ -29,6 +40,52 @@ import { createFlowViteConfig } from '@monkeyplus/flow/vite';
29
40
  import { installFlowVuePlugins } from '@monkeyplus/flow/vue';
30
41
  ```
31
42
 
43
+ ## Contexto de layout
44
+
45
+ Los layouts pueden tener un handler de contexto colocalizado usando `views/layouts/<Layout>.context.ts`.
46
+
47
+ Opcionalmente puedes definir `views/layouts/global.context.ts` para compartir datos entre todos los layouts. Flow fusiona primero el contexto global y luego el contexto especifico del layout, por lo que el layout especifico tiene prioridad si repite una clave.
48
+
49
+ ```ts
50
+ import type { GlobalLayoutContextValue } from './global.context';
51
+ // views/layouts/Default.context.ts
52
+ import { defineLayoutContext } from '@monkeyplus/flow';
53
+
54
+ const defaultLayoutContext = defineLayoutContext({
55
+ async setup(ctx) {
56
+ return {
57
+ menuName: 'main',
58
+ localeCode: ctx.locale.code,
59
+ };
60
+ },
61
+ });
62
+
63
+ export type DefaultLayoutContextValue = GlobalLayoutContextValue & Awaited<ReturnType<typeof defaultLayoutContext.setup>>;
64
+
65
+ export default defaultLayoutContext;
66
+ ```
67
+
68
+ Ese resultado se expone en el layout bajo `context.layout`.
69
+
70
+ ```vue
71
+ <script setup lang="ts">
72
+ import type { DefaultLayoutContextValue } from './Default.context';
73
+
74
+ const props = defineProps<{
75
+ context?: {
76
+ layout?: DefaultLayoutContextValue
77
+ }
78
+ }>();
79
+ </script>
80
+
81
+ <template>
82
+ <slot />
83
+ <small>{{ props.context?.layout?.menuName }}</small>
84
+ </template>
85
+ ```
86
+
87
+ Esto reemplaza el patrón anterior de `shared/contexts` para casos que pertenecen al layout. Si una lógica debe compartirse entre varios layouts, puedes ponerla en `views/layouts/global.context.ts` o extraerla a un helper `.ts` reutilizable y llamarlo desde cada `*.context.ts`.
88
+
32
89
  ## Playground
33
90
 
34
91
  La app de referencia vive en `playground/` y ya no en la raíz. Eso permite mantener el root enfocado en la distribución del paquete sin perder el flujo de desarrollo existente.
@@ -28,6 +28,9 @@ function getFlowImagesRuntimeConfig() {
28
28
  return getEnvFlowImagesRuntimeConfig();
29
29
  }
30
30
  }
31
+ function shouldExposeStrapiUrlToClient() {
32
+ return process.env.NODE_ENV !== "production";
33
+ }
31
34
  export function getFlowImageBootPayload(config = getFlowImagesRuntimeConfig()) {
32
35
  if (!config) {
33
36
  return void 0;
@@ -36,7 +39,8 @@ export function getFlowImageBootPayload(config = getFlowImagesRuntimeConfig()) {
36
39
  return {
37
40
  all: loadImageRenames(renameSources),
38
41
  options: { ...config.options },
39
- generateOutput: !!config.generate
42
+ generateOutput: !!config.generate,
43
+ ...shouldExposeStrapiUrlToClient() && config.strapiURL ? { strapiURL: config.strapiURL } : {}
40
44
  };
41
45
  }
42
46
  export function getFlowImageRuntimeUtils(config = getFlowImagesRuntimeConfig()) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@monkeyplus/flow",
3
- "version": "6.0.10",
3
+ "version": "6.0.12",
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,8 @@
1
1
  import { createRequire } from "node:module";
2
+ import layoutContexts from "virtual:flow/layout-contexts";
2
3
  import pageDefinitions from "virtual:flow/pages";
3
4
  import { getFlowImageRuntimeUtils } from "../../modules/images/runtime/server.mjs";
5
+ import { mergeLayoutContextValues } from "../../src/runtime/layout-context.mjs";
4
6
  import { localizeRoutePattern, normalizePath, toPublicRoute } from "../../src/runtime/locale-routing.mjs";
5
7
  const dynamicRouteCache = /* @__PURE__ */ new Map();
6
8
  let runtimeConfigRequire;
@@ -88,6 +90,24 @@ function createContext(definition, locale, localePage, pathname, params, dynamic
88
90
  }
89
91
  };
90
92
  }
93
+ function asContextRecord(value) {
94
+ return typeof value === "object" && value !== null ? value : {};
95
+ }
96
+ async function resolveLayoutContext(definition, ctx) {
97
+ const layoutKey = (definition.view.layout || "default").toLowerCase();
98
+ const globalLayoutContext = layoutContexts.global;
99
+ const layoutContext = layoutKey === "global" ? void 0 : layoutContexts[layoutKey];
100
+ const resolvedGlobalContext = globalLayoutContext?.setup ? asContextRecord(await globalLayoutContext.setup(ctx)) : void 0;
101
+ const resolvedLayoutContext = layoutContext?.setup ? asContextRecord(await layoutContext.setup(ctx)) : void 0;
102
+ return mergeLayoutContextValues(resolvedGlobalContext, resolvedLayoutContext);
103
+ }
104
+ function mergeResolvedContext(context, layoutContext, dynamic, assign) {
105
+ const resolvedContext = assign && dynamic?.context ? { ...context, [assign]: dynamic.context, dynamic } : dynamic ? { ...context, dynamic } : context;
106
+ return {
107
+ ...resolvedContext,
108
+ layout: layoutContext
109
+ };
110
+ }
91
111
  function isDynamicPattern(pattern) {
92
112
  return pattern.includes("/:") || pattern.includes("/**");
93
113
  }
@@ -235,9 +255,10 @@ export async function resolvePage(pathname) {
235
255
  }
236
256
  const ctx = createContext(match.definition, locale, match.localePage, pathname, match.params, dynamic);
237
257
  const head = match.localePage.head ? await match.localePage.head(ctx) : match.localePage.seo ? await match.localePage.seo(ctx) : {};
258
+ const layoutContext = await resolveLayoutContext(match.definition, ctx);
238
259
  const context = match.localePage.context ? await match.localePage.context(ctx) : {};
239
260
  const assign = match.localePage.dynamic?.assign;
240
- const resolvedContext = assign && dynamic?.context ? { ...context, [assign]: dynamic.context, dynamic } : dynamic ? { ...context, dynamic } : context;
261
+ const resolvedContext = mergeResolvedContext(context, layoutContext, dynamic, assign);
241
262
  return {
242
263
  definition: match.definition,
243
264
  locale,
@@ -268,6 +289,7 @@ export async function resolvePageByName(name, pathname) {
268
289
  const params = {};
269
290
  const ctx = createContext(definition, locale, localePage, pathname, params);
270
291
  const head = localePage.head ? await localePage.head(ctx) : localePage.seo ? await localePage.seo(ctx) : {};
292
+ const layoutContext = await resolveLayoutContext(definition, ctx);
271
293
  const context = localePage.context ? await localePage.context(ctx) : {};
272
294
  return {
273
295
  definition,
@@ -278,7 +300,7 @@ export async function resolvePageByName(name, pathname) {
278
300
  params,
279
301
  head,
280
302
  seo: head,
281
- context
303
+ context: mergeResolvedContext(context, layoutContext)
282
304
  };
283
305
  }
284
306
  return void 0;
@@ -2,6 +2,8 @@ export type { ContentDirectoryNode, ContentEntry, ContentFileNode, ContentTreeNo
2
2
  export type { FlowBootPayload } from '../runtime/boot.ts';
3
3
  export { defineFlowConfig, defineFlowModule, resolveFlowConfig } from '../runtime/config.ts';
4
4
  export type { FlowConfig, UserFlowConfig, } from '../runtime/config.ts';
5
+ export { defineLayoutContext } from '../runtime/layout-context.ts';
6
+ export type { LayoutContextDefinition, LayoutContextValue } from '../runtime/layout-context.ts';
5
7
  export { definePage } from '../runtime/pages.ts';
6
8
  export type { FlowHydrationMode, FlowLocale, HeadDefinition, PageContextInput, PageDefinition, PageLocaleDefinition, PageViewDefinition, } from '../runtime/pages.ts';
7
9
  export { queryContent } from './query-content.ts';
@@ -1,3 +1,4 @@
1
1
  export { defineFlowConfig, defineFlowModule, resolveFlowConfig } from "../runtime/config.mjs";
2
+ export { defineLayoutContext } from "../runtime/layout-context.mjs";
2
3
  export { definePage } from "../runtime/pages.mjs";
3
4
  export { queryContent } from "./query-content.mjs";
@@ -6,6 +6,7 @@ import { createFlowBuildHooks } from "../runtime/ssg.mjs";
6
6
  import {
7
7
  createVirtualBaseTemplatesModule,
8
8
  createVirtualIslandsModule,
9
+ createVirtualLayoutContextsModule,
9
10
  createVirtualLayoutsModule,
10
11
  createVirtualPagesModule,
11
12
  createVirtualTemplatesModule
@@ -50,6 +51,7 @@ export function createFlowNitroConfig(options = {}) {
50
51
  "virtual:flow/islands": createVirtualIslandsModule(projectRoot),
51
52
  "virtual:flow/templates": createVirtualTemplatesModule(projectRoot),
52
53
  "virtual:flow/layouts": createVirtualLayoutsModule(projectRoot),
54
+ "virtual:flow/layout-contexts": createVirtualLayoutContextsModule(projectRoot),
53
55
  "virtual:flow/bases": createVirtualBaseTemplatesModule(projectRoot),
54
56
  ...flowModules.nitro.virtual
55
57
  },
@@ -14,6 +14,7 @@ import {
14
14
  createVirtualClientPageAssetsModule,
15
15
  createVirtualClientPagesModule,
16
16
  createVirtualIslandsModule,
17
+ createVirtualLayoutContextsModule,
17
18
  createVirtualLayoutsModule,
18
19
  createVirtualPagesModule,
19
20
  createVirtualTemplatesModule
@@ -31,11 +32,15 @@ const flowRestartPatterns = [
31
32
  /^entry-server\.ts$/
32
33
  ];
33
34
  const flowFullReloadPatterns = [
35
+ /^views\/.+\.vue$/,
36
+ /^views\/layouts\/.+\.context\.(ts|js|mjs|mts)$/,
34
37
  /^client\/pages\/.+\.ts$/,
35
38
  /^client\/islands\/.+\.ts$/
36
39
  ];
37
40
  const flowStructureReloadPatterns = [
38
41
  /^pages\/.+\.ts$/,
42
+ /^views\/.+\.vue$/,
43
+ /^views\/layouts\/.+\.context\.(ts|js|mjs|mts)$/,
39
44
  /^views\/templates\/.+\.vue$/,
40
45
  /^views\/layouts\/.+\.vue$/,
41
46
  /^views\/base\/.+\.html$/,
@@ -55,6 +60,7 @@ function getVirtualModuleIdsForPath(projectPath) {
55
60
  }
56
61
  if (projectPath.startsWith("views/layouts/")) {
57
62
  ids.add("virtual:flow/layouts");
63
+ ids.add("virtual:flow/layout-contexts");
58
64
  }
59
65
  if (projectPath.startsWith("views/base/")) {
60
66
  ids.add("virtual:flow/bases");
@@ -103,7 +109,22 @@ function invalidateFileModules(server, filePath, event) {
103
109
  }
104
110
  }
105
111
  }
106
- function createFlowHotReload(projectRoot) {
112
+ function normalizeWatchPaths(projectRoot, ...groups) {
113
+ const paths = /* @__PURE__ */ new Set();
114
+ for (const group of groups) {
115
+ if (!Array.isArray(group)) {
116
+ continue;
117
+ }
118
+ for (const entry of group) {
119
+ if (typeof entry !== "string" || !entry) {
120
+ continue;
121
+ }
122
+ paths.add(resolve(projectRoot, entry));
123
+ }
124
+ }
125
+ return [...paths];
126
+ }
127
+ function createFlowHotReload(projectRoot, extraWatchPaths = []) {
107
128
  let restartPending = false;
108
129
  function toProjectPath(filePath) {
109
130
  return normalizePath(relative(projectRoot, filePath));
@@ -138,6 +159,9 @@ function createFlowHotReload(projectRoot) {
138
159
  return {
139
160
  name: "flow:hot-reload",
140
161
  configureServer(server) {
162
+ if (extraWatchPaths.length) {
163
+ server.watcher.add(extraWatchPaths);
164
+ }
141
165
  server.watcher.on("add", (filePath) => {
142
166
  void handleServerChange(server, filePath, "add");
143
167
  });
@@ -159,6 +183,7 @@ function createFlowVirtualServerModules(projectRoot, flowConfig) {
159
183
  ["virtual:flow/islands", () => createVirtualIslandsModule(projectRoot)],
160
184
  ["virtual:flow/templates", () => createVirtualTemplatesModule(projectRoot)],
161
185
  ["virtual:flow/layouts", () => createVirtualLayoutsModule(projectRoot)],
186
+ ["virtual:flow/layout-contexts", () => createVirtualLayoutContextsModule(projectRoot)],
162
187
  ["virtual:flow/bases", () => createVirtualBaseTemplatesModule(projectRoot)]
163
188
  ]);
164
189
  return {
@@ -214,11 +239,19 @@ export function createFlowViteConfig(options = {}) {
214
239
  const moduleServer = flowModules.vite.server || { watch: { additionalPaths: [] } };
215
240
  const configuredWatch = typeof configuredServer.watch === "object" && configuredServer.watch ? configuredServer.watch : {};
216
241
  const moduleWatch = typeof moduleServer.watch === "object" && moduleServer.watch ? moduleServer.watch : {};
242
+ const configuredComponents = typeof flowConfig.components === "object" && flowConfig.components ? flowConfig.components : {};
243
+ const componentDirs = Array.isArray(configuredComponents.dirs) ? configuredComponents.dirs.filter((entry) => typeof entry === "string") : [];
244
+ const extraWatchPaths = normalizeWatchPaths(
245
+ projectRoot,
246
+ configuredWatch.additionalPaths,
247
+ moduleWatch.additionalPaths,
248
+ componentDirs
249
+ );
217
250
  return defineConfig({
218
251
  plugins: [
219
252
  createFlowVirtualServerModules(projectRoot, flowConfig),
220
253
  createFlowVirtualClientPages(projectRoot),
221
- createFlowHotReload(projectRoot),
254
+ createFlowHotReload(projectRoot, extraWatchPaths),
222
255
  ...flowModules.vite.plugins,
223
256
  Icons({
224
257
  autoInstall: true,
@@ -4,6 +4,7 @@ export interface FlowImageBootPayload {
4
4
  all: Record<string, FlowImageMeta>;
5
5
  options: FlowImageOptions;
6
6
  generateOutput: boolean;
7
+ strapiURL?: string;
7
8
  }
8
9
  export interface FlowBootPayload {
9
10
  path: string;
@@ -42,7 +42,8 @@ export function createBootImageUtils(boot) {
42
42
  generate: {}
43
43
  };
44
44
  const resolver = createImageResolver(snapshot.options, stateImages, {
45
- generateOutput: snapshot.generateOutput
45
+ generateOutput: snapshot.generateOutput,
46
+ strapiURL: snapshot.strapiURL
46
47
  });
47
48
  return {
48
49
  getImage: resolver.getImage,
@@ -0,0 +1,7 @@
1
+ import type { MaybePromise, PageContextInput } from './pages.ts';
2
+ export interface LayoutContextDefinition {
3
+ setup: (ctx: PageContextInput) => MaybePromise<Record<string, unknown>>;
4
+ }
5
+ export type LayoutContextValue<T extends LayoutContextDefinition> = Awaited<ReturnType<T['setup']>>;
6
+ export declare function defineLayoutContext(context: LayoutContextDefinition): LayoutContextDefinition;
7
+ export declare function mergeLayoutContextValues(...contexts: Array<Record<string, unknown> | undefined>): Record<string, unknown>;
@@ -0,0 +1,14 @@
1
+ export function defineLayoutContext(context) {
2
+ return context;
3
+ }
4
+ export function mergeLayoutContextValues(...contexts) {
5
+ return contexts.reduce((result, context) => {
6
+ if (!context) {
7
+ return result;
8
+ }
9
+ return {
10
+ ...result,
11
+ ...context
12
+ };
13
+ }, {});
14
+ }
@@ -2,6 +2,7 @@ import type { FlowConfig } from './config';
2
2
  export declare function createVirtualPagesModule(projectRoot: string, flowConfig: FlowConfig): string;
3
3
  export declare function createVirtualTemplatesModule(projectRoot: string): string;
4
4
  export declare function createVirtualLayoutsModule(projectRoot: string): string;
5
+ export declare function createVirtualLayoutContextsModule(projectRoot: string): string;
5
6
  export declare function createVirtualBaseTemplatesModule(projectRoot: string): string;
6
7
  export declare function createVirtualIslandsModule(projectRoot: string): string;
7
8
  export declare function createVirtualClientPagesModule(projectRoot: string): string;
@@ -89,6 +89,27 @@ export function createVirtualLayoutsModule(projectRoot) {
89
89
  const files = existsSync(layoutsDir) ? collectFiles(layoutsDir, [".vue"]) : [];
90
90
  return createVirtualVueModuleSource(files, "layouts");
91
91
  }
92
+ export function createVirtualLayoutContextsModule(projectRoot) {
93
+ const layoutsDir = resolve(projectRoot, "views/layouts");
94
+ const files = existsSync(layoutsDir) ? collectFiles(layoutsDir, [".context.ts", ".context.js", ".context.mjs", ".context.mts"]) : [];
95
+ const imports = files.map((filePath, index) => {
96
+ return `import item_${index} from ${JSON.stringify(toAbsoluteImport(filePath))};`;
97
+ });
98
+ const entries = files.map((filePath, index) => {
99
+ const key = basename(filePath, extname(filePath)).replace(/\.context$/i, "").toLowerCase();
100
+ return `${JSON.stringify(key)}: item_${index}`;
101
+ });
102
+ return [
103
+ ...imports,
104
+ "",
105
+ "const layoutContexts = {",
106
+ ...entries.map((entry) => ` ${entry},`),
107
+ "};",
108
+ "",
109
+ "export default layoutContexts;",
110
+ ""
111
+ ].join("\n");
112
+ }
92
113
  export function createVirtualBaseTemplatesModule(projectRoot) {
93
114
  const baseDir = resolve(projectRoot, "views/base");
94
115
  const files = existsSync(baseDir) ? collectFiles(baseDir, [".html"]) : [];
@@ -113,6 +113,12 @@ declare module 'virtual:flow/layouts' {
113
113
  export default layouts;
114
114
  }
115
115
 
116
+ declare module 'virtual:flow/layout-contexts' {
117
+ const layoutContexts: Record<string, import('./layout-context').LayoutContextDefinition>;
118
+
119
+ export default layoutContexts;
120
+ }
121
+
116
122
  declare module 'virtual:flow/bases' {
117
123
  const bases: Record<string, string>;
118
124