@monkeyplus/flow 3.7.4 → 4.0.0-beta.1

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/dist/index.mjs ADDED
@@ -0,0 +1,845 @@
1
+ import path from 'path';
2
+ import { applyToDefaults } from '@hapi/hoek';
3
+ import consola from 'consola';
4
+ import * as R from 'ramda';
5
+ import { notFound } from '@hapi/boom';
6
+ import os from 'os';
7
+ import chalk from 'chalk';
8
+ import fs from 'fs-extra';
9
+
10
+ const name = "@monkeyplus/flow";
11
+ const version = "4.0.0-beta.1";
12
+ const description = "Utils hapi";
13
+ const author = "Andres Navarrete";
14
+ const license = "MIT";
15
+ const main = "./dist/index.cjs";
16
+ const module = "./dist/index.mjs";
17
+ const types = "./dist/index.d.ts";
18
+ const files = [
19
+ "dist/",
20
+ "types/"
21
+ ];
22
+ const exports = {
23
+ ".": {
24
+ "import": "./dist/index.mjs",
25
+ require: "./dist/index.cjs"
26
+ }
27
+ };
28
+ const scripts = {
29
+ build: "unbuild",
30
+ dev: "pnpm stub && pnpm types",
31
+ stub: "unbuild --stub",
32
+ types: "tsup ./src/index.ts --dts-only --external types && esno ../../scripts/replace",
33
+ lint: "eslint --ext .js,.ts .",
34
+ fix: "eslint --fix --ext .ts .",
35
+ prepublishOnly: "pnpm run build",
36
+ publish: "pnpm publish",
37
+ start: "esno src/index.ts",
38
+ test: "vitest"
39
+ };
40
+ const dependencies$1 = {
41
+ "@hapi/boom": "9.x.x",
42
+ "@hapi/hoek": "9.x.x",
43
+ chalk: "^5.0.0",
44
+ consola: "^2.15.3",
45
+ "fs-extra": "^10.0.0",
46
+ ramda: "^0.28.0"
47
+ };
48
+ const devDependencies = {
49
+ "@types/fs-extra": "^9.0.13",
50
+ "@types/hapi__hapi": "^20.0.10",
51
+ "@types/ramda": "^0.27.64"
52
+ };
53
+ const peerDependencies = {
54
+ "@hapi/hapi": "^20.x"
55
+ };
56
+ const gitHead = "1d5387b9c77909019f36d360a0ac53ddaa40e4af";
57
+ const pkg = {
58
+ name: name,
59
+ version: version,
60
+ description: description,
61
+ author: author,
62
+ license: license,
63
+ main: main,
64
+ module: module,
65
+ types: types,
66
+ files: files,
67
+ exports: exports,
68
+ scripts: scripts,
69
+ dependencies: dependencies$1,
70
+ devDependencies: devDependencies,
71
+ peerDependencies: peerDependencies,
72
+ gitHead: gitHead
73
+ };
74
+
75
+ const logger$1 = consola.withScope(pkg.name);
76
+ const dependencies = {
77
+ "@hapi/vision": "6.x.x"
78
+ };
79
+
80
+ const LifeCircle = {
81
+ register: (server) => {
82
+ server.ext("onPreStart", async () => {
83
+ await server.methods.flow.pages();
84
+ });
85
+ server.ext("onRequest", async (req, h) => {
86
+ await server.methods.flow.pages();
87
+ const stateRequest = {
88
+ global: {},
89
+ local: {},
90
+ context: {},
91
+ utils: {},
92
+ extensions: {}
93
+ };
94
+ req.plugins.flow = stateRequest;
95
+ const { flow: state } = server.app;
96
+ const pathRedirect = state.pages.redirects[req.path];
97
+ if (state.truncates[req.path])
98
+ return h.redirect("/").takeover();
99
+ if (pathRedirect)
100
+ return h.redirect(pathRedirect).takeover();
101
+ return h.continue;
102
+ });
103
+ server.ext("onPreHandler", async (req, h) => {
104
+ const { dynamic } = req.route.settings.plugins?.flow || {};
105
+ const flow = req.route.settings.app?.flow;
106
+ if (!dynamic)
107
+ return h.continue;
108
+ const pages = await dynamic.pages({
109
+ server,
110
+ route: req.route,
111
+ locale: flow?.locale
112
+ });
113
+ if (!pages || !pages.length)
114
+ return h.continue;
115
+ const page = pages.find((_page) => _page.url === req.params.url);
116
+ if (!page)
117
+ return notFound();
118
+ Object.assign(req.plugins.flow.local, {
119
+ [dynamic.assign || "dynamic"]: page.context ?? { a: "" }
120
+ });
121
+ req.app.dynamic = R.omit(["context"], page);
122
+ return h.continue;
123
+ });
124
+ }
125
+ };
126
+
127
+ const ServerInfo = {
128
+ register: (server) => {
129
+ const serverInfo = () => {
130
+ const interfaces = os.networkInterfaces();
131
+ const hostname = "localhost";
132
+ const protocol = "http";
133
+ console.log("");
134
+ logger$1.success(chalk.green(`Flow v${pkg.version} dev server running at:`));
135
+ console.log("");
136
+ Object.keys(interfaces).forEach((key) => (interfaces[key] || []).filter((details) => details.family === "IPv4").map((detail) => {
137
+ return {
138
+ type: detail.address.includes("127.0.0.1") ? "Local: " : "Network: ",
139
+ host: detail.address.replace("127.0.0.1", hostname)
140
+ };
141
+ }).forEach(({ type, host }) => {
142
+ const url = `${protocol}://${host}:${chalk.bold(server.info.port)}`;
143
+ console.log(` > ${type} ${chalk.cyan(url)}`);
144
+ }));
145
+ };
146
+ return { serverInfo };
147
+ }
148
+ };
149
+
150
+ function getLocalDefaults(loc, opts) {
151
+ const bundle = R.path(["locales", loc, "view", "bundle"], opts);
152
+ const view = R.path(["locales", loc, "view"], opts);
153
+ const runContext = R.path(["locales", loc, "context"], opts);
154
+ const seo = R.path(["locales", loc, "seo"], opts);
155
+ const blogInfo = R.path(["locales", loc, "blogInfo"], opts);
156
+ return {
157
+ bundle,
158
+ view,
159
+ runContext,
160
+ seo: seo || {},
161
+ blogInfo
162
+ };
163
+ }
164
+ function getLevel(locale, defLevel) {
165
+ const level = typeof defLevel === "string" ? defLevel : typeof defLevel === "object" ? defLevel.locales[locale] || defLevel.default : "";
166
+ return level;
167
+ }
168
+ const definePages = (options) => (defaultLocale) => (levelOptions = {}) => {
169
+ const prefixName = levelOptions.prefixName || "";
170
+ const pages = (locales) => locales.map(({ locale, url, seo }) => {
171
+ const page = getLocalDefaults(locale, options);
172
+ const levelBase = getLevel(locale, levelOptions.level);
173
+ const level = getLevel(locale, options.level);
174
+ const [language, ubication] = locale.split("-");
175
+ const prefixLang = language === defaultLocale.language ? "" : language;
176
+ const prefixUbication = ubication === defaultLocale.ubication ? "" : ubication;
177
+ const prefixLevel = path.posix.join("/", prefixLang, prefixUbication, levelBase, level);
178
+ const publicPath = defaultLocale.publicPath || "/";
179
+ const fullPath = path.posix.join("/", publicPath, prefixLevel, url);
180
+ const redirect = R.last(fullPath) === "/" ? path.posix.join(fullPath, "index.html") : void 0;
181
+ const app = {
182
+ localeName: path.posix.join(prefixName, locale, options.name),
183
+ name: options.name,
184
+ locale,
185
+ language,
186
+ level,
187
+ redirect,
188
+ seo: R.mergeDeepLeft(seo || {}, page.seo),
189
+ blogInfo: page.blogInfo,
190
+ urlObject: {
191
+ publicPath,
192
+ level: prefixLevel,
193
+ url,
194
+ path: fullPath,
195
+ static: R.last(fullPath) === "/" ? `${fullPath}index.html` : `${fullPath}.html`
196
+ },
197
+ view: { ...options.view, ...page.view },
198
+ runSharedContext: options.context,
199
+ runLocalContext: page.runContext
200
+ };
201
+ return app;
202
+ });
203
+ return {
204
+ name: options.name,
205
+ pages
206
+ };
207
+ };
208
+
209
+ const handlerPage = async (req, h) => {
210
+ const { configs } = req.server.plugins.flow;
211
+ const { flow } = req.route.settings.app;
212
+ const { dynamic } = req.app;
213
+ const local = {};
214
+ const global = {};
215
+ const page = R.clone({
216
+ ...R.pick(["publicPath", "path"], flow.urlObject),
217
+ ...R.pick([
218
+ "name",
219
+ "localeName",
220
+ "locale",
221
+ "level",
222
+ "template",
223
+ "bundle",
224
+ "language"
225
+ ], flow)
226
+ });
227
+ if (dynamic) {
228
+ Object.assign(page, {
229
+ path: dynamic.url,
230
+ ...R.omit(["url"], dynamic)
231
+ });
232
+ }
233
+ const utils = {
234
+ ...req.plugins.flow.utils,
235
+ getLocalUrl: (name) => {
236
+ return h.getUrl(flow.locale, name);
237
+ },
238
+ getUrl: h.getUrl
239
+ };
240
+ const seo = {
241
+ ...flow.seo,
242
+ ...req.pre.seo,
243
+ url: configs.url,
244
+ pageUrl: `${configs.url}${req.url.pathname}`
245
+ };
246
+ const contextMethod = {
247
+ seo,
248
+ req,
249
+ h,
250
+ page,
251
+ extensions: req.plugins.flow.extensions,
252
+ global: req.plugins.flow.global,
253
+ utils
254
+ };
255
+ const extraContexts = req.plugins.flow.context;
256
+ for (const key in extraContexts) {
257
+ if (extraContexts[key]?.method) {
258
+ const assign = extraContexts[key].assign;
259
+ const data = await extraContexts[key].method(contextMethod);
260
+ const _context = { [key]: data };
261
+ if (assign === "global")
262
+ Object.assign(global, _context);
263
+ else if (assign === "local")
264
+ Object.assign(local, _context);
265
+ else
266
+ Object.assign(seo, _context);
267
+ }
268
+ }
269
+ if (flow.runSharedContext) {
270
+ const _context = await flow.runSharedContext(contextMethod);
271
+ Object.assign(local, _context);
272
+ }
273
+ if (flow.runLocalContext) {
274
+ const _context = await flow.runLocalContext(contextMethod);
275
+ Object.assign(local, _context);
276
+ }
277
+ const context = {
278
+ view: flow.view,
279
+ context: Object.freeze({ ...local, ...req.plugins.flow.local }),
280
+ seo,
281
+ page,
282
+ utils: {},
283
+ hapi: { req, h },
284
+ global: Object.freeze({ ...global, ...req.plugins.flow.global })
285
+ };
286
+ if (req.query.context) {
287
+ context.utils = Object.keys(utils);
288
+ context.hapi = ["req", "h"];
289
+ return context;
290
+ }
291
+ context.utils = utils;
292
+ return h.view(`${configs.dirTemplates}${flow.view.template}`, context);
293
+ };
294
+
295
+ const logger = consola.withScope("@monkeyplus/flow");
296
+ const defaults = {
297
+ locales: ["es-ec"],
298
+ defaultUbication: "ec",
299
+ defaultLanguage: "es",
300
+ publicPath: "/",
301
+ locale: "es-ec",
302
+ relativeTo: "",
303
+ dirTemplates: "",
304
+ outputDir: "build",
305
+ url: process.env.CUSTOM_URL || process.env.URL,
306
+ engines: ["eta"],
307
+ plugins: {}
308
+ };
309
+ function getConfigs(server, options) {
310
+ const relativeTo = path.resolve(options.relativeTo);
311
+ const getObject = (object) => {
312
+ const config = applyToDefaults(defaults, { ...object, ...options });
313
+ return {
314
+ ...config,
315
+ relativeTo,
316
+ locale: `${config.defaultLanguage}-${config.defaultUbication}`
317
+ };
318
+ };
319
+ try {
320
+ const pathWeb = path.resolve(options.relativeTo, "flow.config");
321
+ logger.info("Flow apply settings from file: flow.config");
322
+ const configBase = require(pathWeb);
323
+ const config = getObject(configBase);
324
+ return config;
325
+ } catch (error) {
326
+ logger.info("Flow no config file apply default settings");
327
+ return getObject({});
328
+ }
329
+ }
330
+ const isDinamyc = (url) => /\{url?(.+)\}/.test(url);
331
+
332
+ const Decorators = {
333
+ register: (server) => {
334
+ const { flow: state } = server.app;
335
+ const { configs, helpers } = server.plugins.flow;
336
+ const flowDecorate = (route, options) => {
337
+ const { plugins, app } = route.settings;
338
+ Object.assign(plugins, { generate: ".html" });
339
+ const flow = app.flow;
340
+ const runCommons = (pageInfo) => {
341
+ helpers.addStaticPage(pageInfo.localeName, {
342
+ url: route.path,
343
+ locale: pageInfo.locale,
344
+ localeName: pageInfo.localeName,
345
+ name: pageInfo.name
346
+ });
347
+ if (pageInfo.redirect)
348
+ helpers.addRedirect(pageInfo.redirect, route.path);
349
+ };
350
+ if (flow) {
351
+ runCommons(flow);
352
+ } else {
353
+ const routes = definePages({
354
+ name: options.name,
355
+ view: options.view,
356
+ locales: {
357
+ [options.locale || configs.locale]: {}
358
+ },
359
+ context: options.context
360
+ })({
361
+ language: configs.defaultLanguage,
362
+ ubication: configs.defaultUbication,
363
+ publicPath: configs.publicPath
364
+ })().pages([
365
+ { locale: options.locale || configs.locale, url: route.path }
366
+ ]);
367
+ const pageInfo = routes[0];
368
+ runCommons(pageInfo);
369
+ Object.assign(app, { flow: pageInfo, content: options.content });
370
+ }
371
+ const extensions = plugins?.flow?.extensions || {};
372
+ if (isDinamyc(route.path)) {
373
+ if (!route.settings.plugins?.flow?.dynamic)
374
+ throw new Error("Dynamic pages require a method");
375
+ helpers.addDynamicPages(flow.localeName, ((options2) => async () => {
376
+ const _pages = await route.settings.plugins?.flow?.dynamic?.pages(options2);
377
+ const obj = {};
378
+ for (const sPage of _pages || []) {
379
+ const localeName = `${app?.flow.localeName}/${sPage.name}`;
380
+ obj[localeName] = {
381
+ locale: app?.flow.locale,
382
+ localeName,
383
+ name: localeName.replace(`${app?.flow.locale}/`, ""),
384
+ url: route.path.replace("{url*}", sPage.url),
385
+ context: sPage.context,
386
+ dynamicSlug: sPage.dynamicSlug
387
+ };
388
+ }
389
+ return obj;
390
+ })({
391
+ server,
392
+ route,
393
+ locale: app?.flow.locale
394
+ }));
395
+ }
396
+ for (const extension in extensions) {
397
+ const optionExtension = plugins?.flow?.extensions[extension];
398
+ const routeMethod = state.extensions[extension]?.routeMethod;
399
+ if (routeMethod)
400
+ routeMethod({ pageInfo: app.flow, route }, optionExtension);
401
+ }
402
+ return handlerPage;
403
+ };
404
+ const getUrl = (_locale, _name) => {
405
+ try {
406
+ const pageName = path.posix.join(_locale, _name);
407
+ const page = state.pages.all[pageName];
408
+ if (page) {
409
+ return page.url;
410
+ } else {
411
+ logger$1.warn("Not found link with name: %s", pageName);
412
+ return "/404";
413
+ }
414
+ } catch (error) {
415
+ logger$1.error(error);
416
+ return "/";
417
+ }
418
+ };
419
+ server.method("flow.getUrl", getUrl, {});
420
+ server.decorate("handler", "flow", flowDecorate);
421
+ server.decorate("toolkit", "getUrl", server.methods.flow.getUrl);
422
+ }
423
+ };
424
+
425
+ const useGenerator = {
426
+ register: (server) => {
427
+ const { configs, helpers } = server.plugins.flow;
428
+ const getFlowPages = async () => {
429
+ const files = {};
430
+ const allPages = await server.methods.flow.pages();
431
+ const pages = Object.values(allPages);
432
+ logger.success("Read %i static pages", pages.length);
433
+ for (const page of pages)
434
+ files[page.url] = ".html";
435
+ const routes = server.table();
436
+ for (const route of routes) {
437
+ const isDynamic = /\{url?(.+)\}/.test(route.path);
438
+ const generate = route.settings.plugins?.generate;
439
+ if (!isDynamic && generate) {
440
+ if (!files[route.path])
441
+ files[route.path] = generate;
442
+ }
443
+ }
444
+ return files;
445
+ };
446
+ const writeFile = async (file, payload) => {
447
+ await fs.ensureFile(file);
448
+ await fs.writeFile(file, payload, "utf8");
449
+ };
450
+ const useCreateFile = (isVirtual = false) => async (dir, url, ext) => {
451
+ if (typeof ext === "object") {
452
+ if (!isVirtual) {
453
+ const dirFile = path.dirname(url);
454
+ await fs.ensureDir(path.join(dir, dirFile));
455
+ const file = await fs.readFile(ext.file);
456
+ await fs.writeFile(path.join(dir, url), file);
457
+ }
458
+ return { type: ext.type || "assets", url };
459
+ } else {
460
+ const r = await server.inject({
461
+ url,
462
+ method: "get"
463
+ });
464
+ let file = "";
465
+ if (typeof ext === "boolean") {
466
+ if (!isVirtual) {
467
+ file = path.join(dir, url);
468
+ await writeFile(file, r.payload);
469
+ }
470
+ return { type: "assets", url };
471
+ } else if (typeof ext === "string") {
472
+ if (ext === ".html") {
473
+ if (!isVirtual) {
474
+ const isIndex = R.last(url) === "/";
475
+ const nameFile = isIndex ? `${url}index.html` : `${url}.html`;
476
+ file = path.join(dir, nameFile);
477
+ await writeFile(file, r.payload);
478
+ }
479
+ return { type: "page", url };
480
+ }
481
+ }
482
+ return { type: "asset", url };
483
+ }
484
+ };
485
+ const injectAssets = (assets) => {
486
+ for (const asset of assets) {
487
+ const dirs = asset.dirs;
488
+ for (const dir of dirs) {
489
+ const dirFiles = fs.readdirSync(path.join(asset.relativeTo, dir));
490
+ for (const file of dirFiles) {
491
+ const pathFile = path.join(asset.relativeTo, dir, file);
492
+ const ext = path.extname(pathFile);
493
+ if (!!ext) {
494
+ const pathRoute = path.posix.join("/", asset.prefix, dir, file);
495
+ const pathRouteOverride = asset.override?.[pathRoute];
496
+ server.route({
497
+ path: pathRouteOverride || pathRoute,
498
+ method: "get",
499
+ options: {
500
+ plugins: {
501
+ generate: {
502
+ isAsset: true,
503
+ file: pathFile,
504
+ type: asset.type,
505
+ isRemane: !!pathRouteOverride
506
+ }
507
+ }
508
+ },
509
+ handler: {
510
+ file: {
511
+ path: pathFile,
512
+ confine: false
513
+ }
514
+ }
515
+ });
516
+ } else {
517
+ injectAssets([
518
+ {
519
+ dirs: [path.posix.join(dir, file)],
520
+ prefix: asset.prefix,
521
+ relativeTo: asset.relativeTo,
522
+ override: asset.override,
523
+ type: asset.type || file
524
+ }
525
+ ]);
526
+ }
527
+ }
528
+ }
529
+ }
530
+ };
531
+ const runGenerate = async ({
532
+ assets = [],
533
+ ommitAssets = [],
534
+ virtualGenerate = false
535
+ }) => {
536
+ await server.methods.flow.pages();
537
+ const _assetsApp = [...Object.values(server.app.flow.generate.folders)];
538
+ const _assets = [...assets, ..._assetsApp];
539
+ if (!virtualGenerate)
540
+ injectAssets(_assets);
541
+ const dirOutut = helpers.getPath(configs.outputDir ?? "build");
542
+ const staticDirs = Object.keys(server.app.flow.generate.staticFolders).map((dir) => helpers.getPath(dir));
543
+ const files = await getFlowPages();
544
+ const pairFiles = R.toPairs(files);
545
+ const ommit = ommitAssets;
546
+ try {
547
+ await fs.remove(dirOutut);
548
+ for (const dirStatic of staticDirs)
549
+ await fs.copy(dirStatic, dirOutut);
550
+ for (const omitFile of ommit)
551
+ await fs.remove(path.join(dirOutut, omitFile));
552
+ const createFile = useCreateFile(virtualGenerate);
553
+ const files2 = await Promise.all(pairFiles.map(async (v) => {
554
+ return await createFile(dirOutut, ...v);
555
+ }));
556
+ const groups = R.groupBy((file) => {
557
+ if (file.url.includes("images/"))
558
+ return "image";
559
+ else
560
+ return (path.extname(file.url) || file.type).toLowerCase();
561
+ }, files2);
562
+ const _groups = {
563
+ pages: 0,
564
+ js: 0,
565
+ css: 0,
566
+ images: 0,
567
+ fonts: 0,
568
+ others: 0
569
+ };
570
+ for (const type in groups) {
571
+ const cp = (ext) => ext === type;
572
+ const qty = groups[type].length;
573
+ if (type === "page")
574
+ _groups.pages += qty;
575
+ else if (type === ".js")
576
+ _groups.js += qty;
577
+ else if (type === ".css")
578
+ _groups.css += qty;
579
+ else if ([".gif", ".png", ".jpg", ".jpeg", "image"].find(cp))
580
+ _groups.images += qty;
581
+ else if ([".woff", ".eot", ".ttf"].find(cp))
582
+ _groups.fonts += qty;
583
+ else
584
+ _groups.others += qty;
585
+ }
586
+ for (const type in _groups)
587
+ logger.success("%i %s generated", _groups[type], type);
588
+ const bf = server.app.flow.generate.postGenerate;
589
+ if (Object.keys(bf).length) {
590
+ logger.info("Post generate");
591
+ }
592
+ for (const key in bf)
593
+ await bf[key]();
594
+ } catch (error) {
595
+ logger.error(error);
596
+ await fs.remove(dirOutut);
597
+ throw new Error(error);
598
+ }
599
+ };
600
+ return { runGenerate };
601
+ }
602
+ };
603
+
604
+ const definePage = (pageOptions) => (levelOptions) => (configs) => {
605
+ const pages = [];
606
+ for (const locale in pageOptions.locales) {
607
+ if (configs.locales.find((_locale) => _locale === locale)) {
608
+ const singlePage = pageOptions.locales[locale];
609
+ pages.push({ url: singlePage.url, locale });
610
+ }
611
+ }
612
+ const infoPages = definePages({
613
+ name: pageOptions.name,
614
+ view: pageOptions.view,
615
+ context: pageOptions.context,
616
+ level: pageOptions.level,
617
+ locales: pageOptions.locales
618
+ })({
619
+ language: configs.defaultLanguage,
620
+ ubication: configs.defaultUbication,
621
+ publicPath: configs.publicPath
622
+ })(levelOptions).pages(pages);
623
+ const {
624
+ options: sharedOptions,
625
+ rules: sharedRules,
626
+ vhost: sharedVHost
627
+ } = pageOptions;
628
+ const sharedPre = sharedOptions?.pre || [];
629
+ const routes = infoPages.map((page) => {
630
+ const localPage = pageOptions.locales[page.locale];
631
+ const route = {
632
+ path: page.urlObject.path,
633
+ method: "get",
634
+ options: {
635
+ app: {
636
+ flow: page
637
+ },
638
+ plugins: {
639
+ flow: {
640
+ extensions: pageOptions.extensions || {},
641
+ dynamic: localPage.dynamic
642
+ }
643
+ }
644
+ },
645
+ handler: {
646
+ flow: {
647
+ name: page.localeName,
648
+ locale: page.locale,
649
+ view: page.view
650
+ }
651
+ }
652
+ };
653
+ if (sharedRules || localPage.rules)
654
+ route.rules = localPage.rules || sharedRules;
655
+ if (sharedVHost || localPage.vhost)
656
+ route.vhost = localPage.vhost || sharedVHost;
657
+ if (sharedOptions || localPage.options) {
658
+ const localOptions = localPage?.options || {};
659
+ const localPre = localPage?.options?.pre || [];
660
+ const pre = [...sharedPre, ...localPre];
661
+ const options = R.mergeDeepRight(sharedOptions || {}, localOptions);
662
+ route.options = R.mergeDeepRight(options, route.options);
663
+ if (route.options && pre.length)
664
+ route.options.pre = pre;
665
+ }
666
+ return route;
667
+ });
668
+ return routes;
669
+ };
670
+
671
+ const RunMethods = {
672
+ register: (server) => {
673
+ const { flow: state } = server.app;
674
+ const { configs: config } = server.plugins.flow;
675
+ const registerRoute = (route) => {
676
+ const _routes = server.table();
677
+ const _route = _routes.find((v) => v.path === route.path);
678
+ if (_route)
679
+ logger$1.warn(`Duplicate route, the route ${route.path} already exists`);
680
+ else
681
+ server.route(route);
682
+ };
683
+ const addPage = (_page, extra) => {
684
+ const registerPages = (_pages2) => {
685
+ for (const _route of _pages2) {
686
+ if (state.routes[_route.path])
687
+ logger$1.warn("The route %s alredy exist", _route.path);
688
+ else
689
+ state.routes[_route.path] = _route;
690
+ }
691
+ };
692
+ let _pages;
693
+ if (typeof _page === "function") {
694
+ _pages = _page(config);
695
+ registerPages(_pages);
696
+ } else {
697
+ _pages = definePage(_page)()(config);
698
+ registerPages(_pages);
699
+ }
700
+ };
701
+ const init = async () => {
702
+ const pages = server.app.flow.routes;
703
+ for (const key in pages) {
704
+ const page = pages[key];
705
+ registerRoute(page);
706
+ }
707
+ for (const key in state.extensions) {
708
+ const ext = state.extensions[key];
709
+ if (ext.initMethod)
710
+ await ext.initMethod();
711
+ }
712
+ logger$1.debug("Register %s pages", `${Object.values(pages).length}`);
713
+ };
714
+ return { init, addPage };
715
+ }
716
+ };
717
+
718
+ const plugin = {
719
+ pkg,
720
+ dependencies,
721
+ register: async (server, _options) => {
722
+ const config = getConfigs(server, _options);
723
+ server.expose("configs", config);
724
+ const state = {
725
+ pages: { statics: {}, redirects: {}, dynamics: {}, all: {} },
726
+ truncates: {},
727
+ extensions: {},
728
+ generate: {
729
+ folders: {},
730
+ staticFolders: {},
731
+ postGenerate: {},
732
+ beforePackage: {}
733
+ },
734
+ engines: {},
735
+ routes: {},
736
+ plugins: {}
737
+ };
738
+ server.app.flow = state;
739
+ const { serverInfo } = ServerInfo.register(server);
740
+ const serverDecorate = {
741
+ serverInfo
742
+ };
743
+ const helpers = {
744
+ getPath: (...paths) => path.join(config.relativeTo, ...paths),
745
+ addRedirect: (key, urlPath) => {
746
+ state.pages.redirects[key] = urlPath;
747
+ },
748
+ addStaticPage: (key, url) => {
749
+ if (isDinamyc(url.url))
750
+ return void 0;
751
+ if (state.pages.statics[key]) {
752
+ logger$1.warn("The namePage %s already exist", state.pages.statics[key]);
753
+ } else {
754
+ state.pages.statics[key] = url;
755
+ }
756
+ },
757
+ addDynamicPages: (key, method) => {
758
+ if (state.pages.dynamics[key])
759
+ logger$1.warn("The dynamdcPages %s already exist", key);
760
+ else
761
+ state.pages.dynamics[key] = method;
762
+ }
763
+ };
764
+ server.expose("helpers", helpers);
765
+ server.method("flow.pages", async () => {
766
+ const pages = {
767
+ ...state.pages.statics
768
+ };
769
+ for (const key in state.pages.dynamics) {
770
+ if (Object.prototype.hasOwnProperty.call(state.pages.dynamics, key)) {
771
+ const element = state.pages.dynamics[key];
772
+ const dynamicPages = await element();
773
+ for (const keyPage in dynamicPages) {
774
+ if (Object.prototype.hasOwnProperty.call(dynamicPages, keyPage)) {
775
+ const page = dynamicPages[keyPage];
776
+ if (!pages[keyPage])
777
+ pages[keyPage] = page;
778
+ else
779
+ logger$1.warn("Duplicate page %s", keyPage);
780
+ }
781
+ }
782
+ }
783
+ }
784
+ state.pages.all = pages;
785
+ return pages;
786
+ }, {
787
+ cache: { expiresIn: 3e3, generateTimeout: 8e3 }
788
+ });
789
+ server.expose("pages", () => {
790
+ return state.pages.all;
791
+ });
792
+ server.decorate("server", "flow", serverDecorate);
793
+ Decorators.register(server);
794
+ LifeCircle.register(server);
795
+ const { runGenerate } = useGenerator.register(server);
796
+ serverDecorate.runGenerate = runGenerate;
797
+ const { addPage, init } = RunMethods.register(server);
798
+ serverDecorate.addPage = addPage;
799
+ serverDecorate.init = init;
800
+ serverDecorate.prepagePackage = async () => {
801
+ for (const key in state.generate.beforePackage)
802
+ await state.generate.beforePackage[key]();
803
+ };
804
+ serverDecorate.assignPluginOptions = (realm, options = {}, d = {}, _context) => {
805
+ const key = realm.plugin.replace("@monkeyplus/", "").replace("flow-", "");
806
+ const baseOptions = applyToDefaults(d, options);
807
+ const { plugins } = config;
808
+ if (plugins[key] && Object.values(plugins[key])) {
809
+ if (typeof plugins[key] === "boolean") {
810
+ try {
811
+ const module = require(path.resolve("./extensions", key))?.default;
812
+ if (typeof module === "function") {
813
+ const promise = module({ server, ..._context });
814
+ const isPromise = !!promise && typeof promise.then === "function";
815
+ if (isPromise) {
816
+ return promise.then((opts2) => {
817
+ const _opts = applyToDefaults(baseOptions, opts2 || {});
818
+ Object.assign(plugins, { [key]: _opts });
819
+ return _opts;
820
+ });
821
+ }
822
+ Object.assign(plugins, { [key]: promise });
823
+ return promise;
824
+ }
825
+ const opts = applyToDefaults(baseOptions, module || {});
826
+ Object.assign(plugins, { [key]: opts });
827
+ return opts;
828
+ } catch (error) {
829
+ logger$1.error("Error in load extension: %s", key);
830
+ return baseOptions;
831
+ }
832
+ } else {
833
+ const opts = applyToDefaults(baseOptions, plugins[key]);
834
+ Object.assign(plugins, { [key]: opts });
835
+ return opts;
836
+ }
837
+ } else {
838
+ Object.assign(plugins, { [key]: baseOptions });
839
+ return baseOptions;
840
+ }
841
+ };
842
+ }
843
+ };
844
+
845
+ export { definePage, plugin };