webspresso 0.0.97 → 0.0.98
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/.agents/ADMIN.md +153 -5
- package/.agents/ARCHITECTURE.md +10 -0
- package/.agents/CLI.md +195 -1
- package/.agents/PLUGINS.md +153 -9
- package/.agents/QUEUE.md +43 -0
- package/bin/commands/add-esbuild.js +80 -0
- package/bin/commands/add-tailwind.js +76 -31
- package/bin/commands/analytics-prune.js +57 -0
- package/bin/commands/api.js +207 -15
- package/bin/commands/build.js +99 -0
- package/bin/commands/dev.js +64 -35
- package/bin/commands/doctor.js +26 -0
- package/bin/commands/email-prune.js +47 -0
- package/bin/commands/make-job.js +86 -0
- package/bin/commands/make-model.js +250 -0
- package/bin/commands/new.js +45 -28
- package/bin/commands/openapi.js +102 -0
- package/bin/commands/page.js +6 -3
- package/bin/commands/queue-prune.js +59 -0
- package/bin/commands/routes.js +52 -0
- package/bin/commands/service.js +6 -3
- package/bin/commands/typegen.js +106 -0
- package/bin/commands/upload-prune.js +79 -0
- package/bin/webspresso.js +22 -0
- package/core/applySchema.js +13 -6
- package/core/auth/hash.js +21 -12
- package/core/compression/middleware.js +23 -17
- package/core/openapi/build-from-api-routes.js +160 -29
- package/core/orm/cache/fingerprint.js +15 -1
- package/core/orm/cache/memory-provider.js +27 -2
- package/core/orm/index.js +12 -7
- package/core/orm/model.js +49 -7
- package/core/queue/adapters/database.js +94 -0
- package/core/queue/adapters/memory.js +62 -0
- package/core/queue/adapters/redis.js +80 -0
- package/core/queue/manager.js +123 -0
- package/core/ssr/stream.js +11 -0
- package/index.d.ts +660 -10
- package/index.js +44 -96
- package/package.json +1 -1
- package/plugins/admin-panel/api.js +279 -17
- package/plugins/admin-panel/app.js +114 -22
- package/plugins/admin-panel/client/parts/01-state-api-breadcrumb.js +314 -9
- package/plugins/admin-panel/client/parts/04-field-renderers.js +264 -67
- package/plugins/admin-panel/client/parts/05-rich-text-file-helpers.js +10 -5
- package/plugins/admin-panel/client/parts/06-login-setup-forms.js +119 -28
- package/plugins/admin-panel/client/parts/07-model-list.js +49 -8
- package/plugins/admin-panel/client/parts/08-record-list.js +243 -23
- package/plugins/admin-panel/client/parts/09-record-form.js +59 -12
- package/plugins/admin-panel/client/parts/10-export-registry.js +23 -0
- package/plugins/admin-panel/core/admin-module.js +13 -7
- package/plugins/admin-panel/core/api-extensions.js +30 -2
- package/plugins/admin-panel/core/registry.js +96 -16
- package/plugins/admin-panel/index.js +96 -0
- package/plugins/admin-panel/lib/login-rate-limiter.js +119 -0
- package/plugins/admin-panel/modules/admin-users.js +16 -1
- package/plugins/admin-panel/modules/bulk-actions.js +13 -5
- package/plugins/admin-panel/modules/custom-pages.js +492 -3
- package/plugins/admin-panel/modules/dashboard.js +283 -0
- package/plugins/admin-panel/modules/menu.js +56 -18
- package/plugins/admin-panel/modules/user-management.js +24 -6
- package/plugins/audit-log/admin-component.js +11 -0
- package/plugins/audit-log/api-handlers.js +34 -0
- package/plugins/audit-log/index.js +129 -3
- package/plugins/audit-log/purge.js +60 -10
- package/plugins/dashboard/app.js +152 -27
- package/plugins/dashboard/index.js +143 -39
- package/plugins/dashboard/styles.js +78 -0
- package/plugins/data-exchange/export-xlsx.js +9 -2
- package/plugins/data-exchange/import.js +9 -2
- package/plugins/data-exchange/index.js +6 -3
- package/plugins/email/index.js +132 -2
- package/plugins/email/purge.js +60 -10
- package/plugins/index.js +4 -0
- package/plugins/orm-cache-admin/index.js +4 -2
- package/plugins/queue/index.js +11 -0
- package/plugins/schema-explorer/admin-component.js +714 -0
- package/plugins/schema-explorer/relations-graph.js +205 -0
- package/plugins/schema-explorer.js +248 -69
- package/plugins/site-analytics/admin-component.js +23 -0
- package/plugins/site-analytics/api-handlers.js +42 -0
- package/plugins/site-analytics/index.js +147 -2
- package/plugins/site-analytics/purge.js +168 -0
- package/plugins/swagger.js +261 -13
- package/plugins/upload/index.js +26 -0
- package/plugins/upload/orphan-pruner.js +292 -0
- package/src/api/api-loader.js +3 -1
- package/src/bundle/index.js +517 -0
- package/src/bundle/manifest.js +165 -0
- package/src/dev/banner.js +296 -0
- package/src/dev/colors.js +70 -0
- package/src/dev/index.js +62 -0
- package/src/dev/profiler.js +354 -0
- package/src/dev/typegen.js +497 -0
- package/src/dev-reload/index.js +611 -0
- package/src/file-router.js +210 -62
- package/src/helpers.js +958 -98
- package/src/njk-frontmatter.js +12 -0
- package/src/pages/page-loader.js +61 -11
- package/src/server.js +629 -63
package/src/file-router.js
CHANGED
|
@@ -8,12 +8,13 @@ const path = require('path');
|
|
|
8
8
|
const { ZodError } = require('zod');
|
|
9
9
|
const { compileSchema, invalidateSchema } = require('../core/compileSchema');
|
|
10
10
|
const { applySchema } = require('../core/applySchema');
|
|
11
|
-
const { createHelpers } = require('./helpers');
|
|
11
|
+
const { createHelpers, getAssetManager } = require('./helpers');
|
|
12
12
|
const {
|
|
13
13
|
loadNjkRouteTemplate,
|
|
14
14
|
parseNjkFrontmatter,
|
|
15
15
|
frontmatterToPatches,
|
|
16
16
|
clearNjkFrontmatterCaches,
|
|
17
|
+
registerClearHook,
|
|
17
18
|
} = require('./njk-frontmatter');
|
|
18
19
|
const { renderStream } = require('../core/ssr/stream');
|
|
19
20
|
|
|
@@ -27,12 +28,30 @@ const i18nCache = new Map();
|
|
|
27
28
|
// Cache for merged i18n (key: `${pagesDir}::${routeDir}::${locale}`, value: { globalMtime, routeMtime, data })
|
|
28
29
|
const mergedI18nCache = new Map();
|
|
29
30
|
|
|
31
|
+
// Cache for translator instances in production
|
|
32
|
+
const translatorCache = new Map();
|
|
33
|
+
|
|
34
|
+
function clearTranslatorCache() {
|
|
35
|
+
translatorCache.clear();
|
|
36
|
+
}
|
|
37
|
+
|
|
30
38
|
// Cache for route configs in production
|
|
31
39
|
const configCache = new Map();
|
|
32
40
|
|
|
33
41
|
// Dev-only: avoid require() on every SSR request when the .js file is unchanged (mtime)
|
|
34
42
|
const routeConfigDevCache = new Map();
|
|
35
43
|
|
|
44
|
+
// Cache for compiled frontmatter string templates (key: fullPath, value: compiled Template)
|
|
45
|
+
const compiledStringTemplateCache = new Map();
|
|
46
|
+
|
|
47
|
+
function clearCompiledStringTemplateCache() {
|
|
48
|
+
compiledStringTemplateCache.clear();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (typeof registerClearHook === 'function') {
|
|
52
|
+
registerClearHook(clearCompiledStringTemplateCache);
|
|
53
|
+
}
|
|
54
|
+
|
|
36
55
|
// Cache for API filename -> { method, baseName } (basename keys; stable per process)
|
|
37
56
|
const methodFromFilenameCache = new Map();
|
|
38
57
|
|
|
@@ -43,6 +62,8 @@ const MAX_LOCALE_LEN = 16;
|
|
|
43
62
|
|
|
44
63
|
let cachedSupportedLocalesRaw = null;
|
|
45
64
|
let cachedSupportedLocaleSet = null;
|
|
65
|
+
let cachedDefaultLocaleKey = null;
|
|
66
|
+
let cachedDefaultLocale = 'en';
|
|
46
67
|
|
|
47
68
|
/** @returns {Set<string>} */
|
|
48
69
|
function parseSupportedLocaleSet() {
|
|
@@ -57,6 +78,27 @@ function parseSupportedLocaleSet() {
|
|
|
57
78
|
return cachedSupportedLocaleSet;
|
|
58
79
|
}
|
|
59
80
|
|
|
81
|
+
/**
|
|
82
|
+
* Get default locale matching supported set
|
|
83
|
+
* @param {Set<string>} [supported]
|
|
84
|
+
* @returns {string}
|
|
85
|
+
*/
|
|
86
|
+
function getDefaultLocale(supported = parseSupportedLocaleSet()) {
|
|
87
|
+
const rawDef = process.env.DEFAULT_LOCALE || 'en';
|
|
88
|
+
const rawSupp = process.env.SUPPORTED_LOCALES || 'en';
|
|
89
|
+
const key = rawDef + '|' + rawSupp;
|
|
90
|
+
if (key === cachedDefaultLocaleKey && cachedDefaultLocale) {
|
|
91
|
+
return cachedDefaultLocale;
|
|
92
|
+
}
|
|
93
|
+
cachedDefaultLocaleKey = key;
|
|
94
|
+
const defaultCand = normalizeLocaleCandidate(rawDef);
|
|
95
|
+
cachedDefaultLocale =
|
|
96
|
+
pickMatchingLocale(defaultCand, supported)
|
|
97
|
+
?? (supported.has('en') ? 'en' : [...supported][0])
|
|
98
|
+
?? 'en';
|
|
99
|
+
return cachedDefaultLocale;
|
|
100
|
+
}
|
|
101
|
+
|
|
60
102
|
/**
|
|
61
103
|
* @param {string} raw
|
|
62
104
|
* @returns {string|null}
|
|
@@ -271,29 +313,31 @@ function extractMethodFromFilename(filename) {
|
|
|
271
313
|
|
|
272
314
|
/**
|
|
273
315
|
* Whether `load()` return values for `stylesheets` and `scripts` are promoted to
|
|
274
|
-
* `pageHead` in Nunjucks (see `createApp({ pageAssets })`)
|
|
275
|
-
*
|
|
276
|
-
* @
|
|
316
|
+
* `pageHead` in Nunjucks (see `createApp({ pageAssets })`), with optional automatic
|
|
317
|
+
* per-route script/stylesheet discovery.
|
|
318
|
+
* @param {boolean|{enabled?: boolean, stylesheets?: boolean, scripts?: boolean, auto?: boolean}|null|undefined} raw
|
|
319
|
+
* @returns {{ enabled: boolean, stylesheets: boolean, scripts: boolean, auto: boolean }}
|
|
277
320
|
*/
|
|
278
321
|
function resolvePageAssets(raw) {
|
|
279
322
|
if (raw === true) {
|
|
280
|
-
return { enabled: true, stylesheets: true, scripts: true };
|
|
323
|
+
return { enabled: true, stylesheets: true, scripts: true, auto: true };
|
|
281
324
|
}
|
|
282
325
|
if (raw == null || raw === false) {
|
|
283
|
-
return { enabled: false, stylesheets: false, scripts: false };
|
|
326
|
+
return { enabled: false, stylesheets: false, scripts: false, auto: false };
|
|
284
327
|
}
|
|
285
328
|
if (typeof raw === 'object') {
|
|
286
329
|
const on = raw.enabled !== false;
|
|
287
330
|
if (!on) {
|
|
288
|
-
return { enabled: false, stylesheets: false, scripts: false };
|
|
331
|
+
return { enabled: false, stylesheets: false, scripts: false, auto: false };
|
|
289
332
|
}
|
|
290
333
|
return {
|
|
291
334
|
enabled: true,
|
|
292
335
|
stylesheets: raw.stylesheets !== false,
|
|
293
336
|
scripts: raw.scripts !== false,
|
|
337
|
+
auto: raw.auto !== false,
|
|
294
338
|
};
|
|
295
339
|
}
|
|
296
|
-
return { enabled: false, stylesheets: false, scripts: false };
|
|
340
|
+
return { enabled: false, stylesheets: false, scripts: false, auto: false };
|
|
297
341
|
}
|
|
298
342
|
|
|
299
343
|
/**
|
|
@@ -306,11 +350,13 @@ function toList(v) {
|
|
|
306
350
|
}
|
|
307
351
|
|
|
308
352
|
/**
|
|
309
|
-
* @param {{ enabled: boolean, stylesheets: boolean, scripts: boolean }} cfg
|
|
353
|
+
* @param {{ enabled: boolean, stylesheets: boolean, scripts: boolean, auto?: boolean }} cfg
|
|
310
354
|
* @param {Object} data
|
|
355
|
+
* @param {{ file?: string, routePath?: string }} [routeInfo={}]
|
|
356
|
+
* @param {Object|null} [assetManager=null]
|
|
311
357
|
* @returns {{ data: Object, pageHead: { stylesheets: unknown[], scripts: unknown[] }|null, pageAssets: boolean }}
|
|
312
358
|
*/
|
|
313
|
-
function applyPageAssetsToTemplateData(cfg, data) {
|
|
359
|
+
function applyPageAssetsToTemplateData(cfg, data, routeInfo = {}, assetManager = null) {
|
|
314
360
|
if (!cfg || !cfg.enabled) {
|
|
315
361
|
return { data, pageHead: null, pageAssets: false };
|
|
316
362
|
}
|
|
@@ -325,6 +371,40 @@ function applyPageAssetsToTemplateData(cfg, data) {
|
|
|
325
371
|
scriptItems = toList(out.scripts);
|
|
326
372
|
delete out.scripts;
|
|
327
373
|
}
|
|
374
|
+
|
|
375
|
+
// Auto-discover page assets from route if enabled
|
|
376
|
+
if (cfg.auto && assetManager && (routeInfo.file || routeInfo.routePath)) {
|
|
377
|
+
const candidates = typeof assetManager.getRouteAssetCandidates === 'function'
|
|
378
|
+
? assetManager.getRouteAssetCandidates(routeInfo.file, routeInfo.routePath)
|
|
379
|
+
: [];
|
|
380
|
+
|
|
381
|
+
if (cfg.stylesheets) {
|
|
382
|
+
for (const cand of candidates) {
|
|
383
|
+
if (typeof assetManager.hasPageCss === 'function' && assetManager.hasPageCss(cand)) {
|
|
384
|
+
const cssUrl = assetManager.pageCssUrl(cand);
|
|
385
|
+
const already = styles.some((s) => (typeof s === 'string' ? s === cssUrl : s?.href === cssUrl));
|
|
386
|
+
if (!already) {
|
|
387
|
+
styles.push(cssUrl);
|
|
388
|
+
}
|
|
389
|
+
break;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
if (cfg.scripts) {
|
|
395
|
+
for (const cand of candidates) {
|
|
396
|
+
if (typeof assetManager.hasPageJs === 'function' && assetManager.hasPageJs(cand)) {
|
|
397
|
+
const jsUrl = assetManager.pageJsUrl(cand);
|
|
398
|
+
const already = scriptItems.some((s) => (typeof s === 'string' ? s === jsUrl : s?.src === jsUrl));
|
|
399
|
+
if (!already) {
|
|
400
|
+
scriptItems.push({ src: jsUrl, defer: true });
|
|
401
|
+
}
|
|
402
|
+
break;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
328
408
|
return {
|
|
329
409
|
data: out,
|
|
330
410
|
pageHead: { stylesheets: styles, scripts: scriptItems },
|
|
@@ -492,15 +572,25 @@ function getPluralRules(locale) {
|
|
|
492
572
|
return pr;
|
|
493
573
|
}
|
|
494
574
|
|
|
575
|
+
const KEY_PARTS_CACHE = new Map();
|
|
576
|
+
|
|
495
577
|
function resolveTranslationValue(dict, key) {
|
|
496
|
-
if (!dict || typeof dict !== 'object') return undefined;
|
|
578
|
+
if (!dict || typeof dict !== 'object' || typeof key !== 'string') return undefined;
|
|
497
579
|
if (dict[key] !== undefined) return dict[key];
|
|
580
|
+
if (!key.includes('.')) return undefined;
|
|
581
|
+
|
|
582
|
+
let parts = KEY_PARTS_CACHE.get(key);
|
|
583
|
+
if (!parts) {
|
|
584
|
+
parts = key.split('.');
|
|
585
|
+
if (KEY_PARTS_CACHE.size < 1000) {
|
|
586
|
+
KEY_PARTS_CACHE.set(key, parts);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
498
589
|
|
|
499
|
-
const parts = key.split('.');
|
|
500
590
|
let curr = dict;
|
|
501
|
-
for (
|
|
591
|
+
for (let i = 0; i < parts.length; i++) {
|
|
502
592
|
if (curr && typeof curr === 'object') {
|
|
503
|
-
curr = curr[
|
|
593
|
+
curr = curr[parts[i]];
|
|
504
594
|
} else {
|
|
505
595
|
return undefined;
|
|
506
596
|
}
|
|
@@ -726,25 +816,37 @@ function loadGlobalHooks(pagesDir, isDev) {
|
|
|
726
816
|
* @param {string} hookName - Hook name
|
|
727
817
|
* @param {Object} ctx - Context object
|
|
728
818
|
*/
|
|
729
|
-
|
|
730
|
-
if (hooks
|
|
731
|
-
|
|
819
|
+
function executeHook(hooks, hookName, ctx, ...extra) {
|
|
820
|
+
if (!hooks || typeof hooks[hookName] !== 'function') {
|
|
821
|
+
return;
|
|
732
822
|
}
|
|
823
|
+
return hooks[hookName](ctx, ...extra);
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
function runHooks(globalHooks, routeHooks, hookName, ctx, ...extra) {
|
|
827
|
+
const gFn = globalHooks ? globalHooks[hookName] : undefined;
|
|
828
|
+
const rFn = routeHooks ? routeHooks[hookName] : undefined;
|
|
829
|
+
if (typeof gFn !== 'function' && typeof rFn !== 'function') return;
|
|
830
|
+
return (async () => {
|
|
831
|
+
if (typeof gFn === 'function') {
|
|
832
|
+
const gRes = gFn(ctx, ...extra);
|
|
833
|
+
if (gRes && typeof gRes.then === 'function') await gRes;
|
|
834
|
+
}
|
|
835
|
+
if (typeof rFn === 'function') {
|
|
836
|
+
const rRes = rFn(ctx, ...extra);
|
|
837
|
+
if (rRes && typeof rRes.then === 'function') await rRes;
|
|
838
|
+
}
|
|
839
|
+
})();
|
|
733
840
|
}
|
|
734
841
|
|
|
735
842
|
/**
|
|
736
843
|
* Detect locale from request
|
|
737
844
|
* @param {Object} req - Express request
|
|
845
|
+
* @param {Set<string>} [supported] - Optional precomputed supported locales set
|
|
846
|
+
* @param {string} [def] - Optional precomputed default locale
|
|
738
847
|
* @returns {string} Locale code
|
|
739
848
|
*/
|
|
740
|
-
function detectLocale(req) {
|
|
741
|
-
const supported = parseSupportedLocaleSet();
|
|
742
|
-
const defaultCand = normalizeLocaleCandidate(process.env.DEFAULT_LOCALE || 'en');
|
|
743
|
-
const def =
|
|
744
|
-
pickMatchingLocale(defaultCand, supported)
|
|
745
|
-
?? (supported.has('en') ? 'en' : [...supported][0])
|
|
746
|
-
?? 'en';
|
|
747
|
-
|
|
849
|
+
function detectLocale(req, supported = parseSupportedLocaleSet(), def = getDefaultLocale(supported)) {
|
|
748
850
|
if (req.query && req.query.lang != null && req.query.lang !== '') {
|
|
749
851
|
const q = normalizeLocaleCandidate(String(req.query.lang));
|
|
750
852
|
const hit = pickMatchingLocale(q, supported);
|
|
@@ -1064,10 +1166,10 @@ function mountPages(app, options) {
|
|
|
1064
1166
|
}
|
|
1065
1167
|
}
|
|
1066
1168
|
// Same instance as createApp({ db }) / getAppContext().db — available to handler & route middleware
|
|
1067
|
-
if (db != null) {
|
|
1169
|
+
if (db != null && !req.db) {
|
|
1068
1170
|
req.db = db;
|
|
1069
1171
|
}
|
|
1070
|
-
if (serviceRegistry != null) {
|
|
1172
|
+
if (serviceRegistry != null && !req.service) {
|
|
1071
1173
|
req.service = (name, input, opts) =>
|
|
1072
1174
|
serviceRegistry.call(name, input, { req, res, db }, opts);
|
|
1073
1175
|
}
|
|
@@ -1159,20 +1261,38 @@ function mountPages(app, options) {
|
|
|
1159
1261
|
}
|
|
1160
1262
|
}
|
|
1161
1263
|
// Detect locale
|
|
1162
|
-
const
|
|
1163
|
-
const defaultLocale =
|
|
1264
|
+
const supported = parseSupportedLocaleSet();
|
|
1265
|
+
const defaultLocale = getDefaultLocale(supported);
|
|
1266
|
+
const locale = detectLocale(req, supported, defaultLocale);
|
|
1164
1267
|
|
|
1165
1268
|
// Load translations (primary + fallback)
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1269
|
+
let t;
|
|
1270
|
+
if (!isDev) {
|
|
1271
|
+
const transKey = `${absolutePagesDir}::${route.routeDir}::${locale}::${defaultLocale}`;
|
|
1272
|
+
t = translatorCache.get(transKey);
|
|
1273
|
+
if (!t) {
|
|
1274
|
+
const translations = loadI18n(absolutePagesDir, route.routeDir, locale, false);
|
|
1275
|
+
const fallbackTranslations = (locale !== defaultLocale)
|
|
1276
|
+
? loadI18n(absolutePagesDir, route.routeDir, defaultLocale, false)
|
|
1277
|
+
: EMPTY_OBJECT;
|
|
1278
|
+
t = createTranslator(translations, {
|
|
1279
|
+
locale,
|
|
1280
|
+
fallbackTranslations,
|
|
1281
|
+
fallbackLocale: defaultLocale,
|
|
1282
|
+
});
|
|
1283
|
+
translatorCache.set(transKey, t);
|
|
1284
|
+
}
|
|
1285
|
+
} else {
|
|
1286
|
+
const translations = loadI18n(absolutePagesDir, route.routeDir, locale, true);
|
|
1287
|
+
const fallbackTranslations = (locale !== defaultLocale)
|
|
1288
|
+
? loadI18n(absolutePagesDir, route.routeDir, defaultLocale, true)
|
|
1289
|
+
: EMPTY_OBJECT;
|
|
1290
|
+
t = createTranslator(translations, {
|
|
1291
|
+
locale,
|
|
1292
|
+
fallbackTranslations,
|
|
1293
|
+
fallbackLocale: defaultLocale,
|
|
1294
|
+
});
|
|
1295
|
+
}
|
|
1176
1296
|
|
|
1177
1297
|
// Load route config
|
|
1178
1298
|
const config = isDev ? loadRouteConfig(route.configPath, true) : mountConfig;
|
|
@@ -1190,6 +1310,8 @@ function mountPages(app, options) {
|
|
|
1190
1310
|
}
|
|
1191
1311
|
|
|
1192
1312
|
const njkTpl = loadNjkRouteTemplate(route.fullPath, isDev);
|
|
1313
|
+
const metaTitle = t('meta.title');
|
|
1314
|
+
const metaDesc = t('meta.description');
|
|
1193
1315
|
|
|
1194
1316
|
const ctx = {
|
|
1195
1317
|
req,
|
|
@@ -1208,8 +1330,8 @@ function mountPages(app, options) {
|
|
|
1208
1330
|
},
|
|
1209
1331
|
data: { ...njkTpl.dataPatch },
|
|
1210
1332
|
meta: {
|
|
1211
|
-
title:
|
|
1212
|
-
description:
|
|
1333
|
+
title: metaTitle !== 'meta.title' ? metaTitle : null,
|
|
1334
|
+
description: metaDesc !== 'meta.description' ? metaDesc : null,
|
|
1213
1335
|
indexable: true,
|
|
1214
1336
|
canonical: null,
|
|
1215
1337
|
...njkTpl.metaPatch,
|
|
@@ -1221,16 +1343,16 @@ function mountPages(app, options) {
|
|
|
1221
1343
|
req.context = ctx;
|
|
1222
1344
|
|
|
1223
1345
|
// Execute hooks: onRequest
|
|
1224
|
-
|
|
1225
|
-
|
|
1346
|
+
let hookPromise = runHooks(globalHooks, routeHooks, 'onRequest', ctx);
|
|
1347
|
+
if (hookPromise) await hookPromise;
|
|
1226
1348
|
|
|
1227
1349
|
// Execute hooks: onRoute
|
|
1228
|
-
|
|
1229
|
-
|
|
1350
|
+
hookPromise = runHooks(globalHooks, routeHooks, 'onRoute', ctx);
|
|
1351
|
+
if (hookPromise) await hookPromise;
|
|
1230
1352
|
|
|
1231
1353
|
// Execute hooks: beforeMiddleware
|
|
1232
|
-
|
|
1233
|
-
|
|
1354
|
+
hookPromise = runHooks(globalHooks, routeHooks, 'beforeMiddleware', ctx);
|
|
1355
|
+
if (hookPromise) await hookPromise;
|
|
1234
1356
|
|
|
1235
1357
|
// Run route middleware (chain fixed at route registration; edit middleware in dev → restart)
|
|
1236
1358
|
if (preResolvedPageMw.length) {
|
|
@@ -1245,8 +1367,8 @@ function mountPages(app, options) {
|
|
|
1245
1367
|
}
|
|
1246
1368
|
|
|
1247
1369
|
// Execute hooks: afterMiddleware
|
|
1248
|
-
|
|
1249
|
-
|
|
1370
|
+
hookPromise = runHooks(globalHooks, routeHooks, 'afterMiddleware', ctx);
|
|
1371
|
+
if (hookPromise) await hookPromise;
|
|
1250
1372
|
|
|
1251
1373
|
// Execute hooks: beforeLoad
|
|
1252
1374
|
if (pluginManager && db) {
|
|
@@ -1255,8 +1377,8 @@ function mountPages(app, options) {
|
|
|
1255
1377
|
ctx.content = contentApi.getContentService(db);
|
|
1256
1378
|
}
|
|
1257
1379
|
}
|
|
1258
|
-
|
|
1259
|
-
|
|
1380
|
+
hookPromise = runHooks(globalHooks, routeHooks, 'beforeLoad', ctx);
|
|
1381
|
+
if (hookPromise) await hookPromise;
|
|
1260
1382
|
|
|
1261
1383
|
// Run load function
|
|
1262
1384
|
if (config?.load && typeof config.load === 'function') {
|
|
@@ -1267,8 +1389,8 @@ function mountPages(app, options) {
|
|
|
1267
1389
|
}
|
|
1268
1390
|
|
|
1269
1391
|
// Execute hooks: afterLoad
|
|
1270
|
-
|
|
1271
|
-
|
|
1392
|
+
hookPromise = runHooks(globalHooks, routeHooks, 'afterLoad', ctx);
|
|
1393
|
+
if (hookPromise) await hookPromise;
|
|
1272
1394
|
|
|
1273
1395
|
// Run meta function
|
|
1274
1396
|
if (config?.meta && typeof config.meta === 'function') {
|
|
@@ -1279,10 +1401,18 @@ function mountPages(app, options) {
|
|
|
1279
1401
|
}
|
|
1280
1402
|
|
|
1281
1403
|
// Execute hooks: beforeRender
|
|
1282
|
-
|
|
1283
|
-
|
|
1404
|
+
hookPromise = runHooks(globalHooks, routeHooks, 'beforeRender', ctx);
|
|
1405
|
+
if (hookPromise) await hookPromise;
|
|
1284
1406
|
|
|
1285
|
-
const pageAssetBundle = applyPageAssetsToTemplateData(
|
|
1407
|
+
const pageAssetBundle = applyPageAssetsToTemplateData(
|
|
1408
|
+
pageAssetsResolved,
|
|
1409
|
+
ctx.data,
|
|
1410
|
+
{
|
|
1411
|
+
file: route.file,
|
|
1412
|
+
routePath: route.routePath,
|
|
1413
|
+
},
|
|
1414
|
+
ctx.fsy?.assetManager || getAssetManager()
|
|
1415
|
+
);
|
|
1286
1416
|
ctx.data = pageAssetBundle.data;
|
|
1287
1417
|
const renderContext = {
|
|
1288
1418
|
...ctx.data,
|
|
@@ -1316,10 +1446,24 @@ function mountPages(app, options) {
|
|
|
1316
1446
|
});
|
|
1317
1447
|
}
|
|
1318
1448
|
|
|
1319
|
-
let html
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
:
|
|
1449
|
+
let html;
|
|
1450
|
+
if (njkTpl.useStringRender && njkTpl.templateBody != null) {
|
|
1451
|
+
if (typeof nunjucks.Template === 'function') {
|
|
1452
|
+
let compiled = isDev ? null : compiledStringTemplateCache.get(route.fullPath);
|
|
1453
|
+
if (!compiled || (isDev && compiled._templateBody !== njkTpl.templateBody)) {
|
|
1454
|
+
compiled = new nunjucks.Template(njkTpl.templateBody, nunjucks, route.fullPath);
|
|
1455
|
+
compiled._templateBody = njkTpl.templateBody;
|
|
1456
|
+
compiledStringTemplateCache.set(route.fullPath, compiled);
|
|
1457
|
+
}
|
|
1458
|
+
html = compiled.render(renderContext);
|
|
1459
|
+
} else if (typeof nunjucks.renderString === 'function') {
|
|
1460
|
+
html = nunjucks.renderString(njkTpl.templateBody, renderContext, { path: route.fullPath });
|
|
1461
|
+
} else {
|
|
1462
|
+
html = nunjucks.render(templatePath, renderContext);
|
|
1463
|
+
}
|
|
1464
|
+
} else {
|
|
1465
|
+
html = nunjucks.render(templatePath, renderContext);
|
|
1466
|
+
}
|
|
1323
1467
|
|
|
1324
1468
|
if (pluginManager) {
|
|
1325
1469
|
const contentApi = pluginManager.getPluginAPI('content');
|
|
@@ -1330,8 +1474,8 @@ function mountPages(app, options) {
|
|
|
1330
1474
|
|
|
1331
1475
|
// Execute hooks: afterRender
|
|
1332
1476
|
ctx.html = html;
|
|
1333
|
-
|
|
1334
|
-
|
|
1477
|
+
hookPromise = runHooks(globalHooks, routeHooks, 'afterRender', ctx);
|
|
1478
|
+
if (hookPromise) await hookPromise;
|
|
1335
1479
|
|
|
1336
1480
|
if (route.routePath === '/404' || route.file === '404.njk') {
|
|
1337
1481
|
res.status(404);
|
|
@@ -1404,5 +1548,9 @@ module.exports = {
|
|
|
1404
1548
|
frontmatterToPatches,
|
|
1405
1549
|
loadNjkRouteTemplate,
|
|
1406
1550
|
clearNjkFrontmatterCaches,
|
|
1551
|
+
compiledStringTemplateCache,
|
|
1552
|
+
clearCompiledStringTemplateCache,
|
|
1553
|
+
translatorCache,
|
|
1554
|
+
clearTranslatorCache,
|
|
1407
1555
|
};
|
|
1408
1556
|
|