meno-core 1.0.48 → 1.0.49
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/build-static.js +7 -7
- package/dist/chunks/{chunk-UUA5LEWF.js → chunk-6IVUG7FY.js} +138 -7
- package/dist/chunks/chunk-6IVUG7FY.js.map +7 -0
- package/dist/chunks/{chunk-B2RTLDXY.js → chunk-AZQYF6KE.js} +132 -1
- package/dist/chunks/chunk-AZQYF6KE.js.map +7 -0
- package/dist/chunks/{chunk-NKUV77SR.js → chunk-CHD5UCFF.js} +21 -9
- package/dist/chunks/{chunk-NKUV77SR.js.map → chunk-CHD5UCFF.js.map} +2 -2
- package/dist/chunks/{chunk-TPQ7APVQ.js → chunk-EQYDSPBB.js} +418 -62
- package/dist/chunks/chunk-EQYDSPBB.js.map +7 -0
- package/dist/chunks/{chunk-RQSTH2BS.js → chunk-H4JSCDNW.js} +2 -2
- package/dist/chunks/{chunk-EK4KESLU.js → chunk-J23ZX5AP.js} +8 -2
- package/dist/chunks/{chunk-EK4KESLU.js.map → chunk-J23ZX5AP.js.map} +2 -2
- package/dist/chunks/{chunk-D5E3OKSL.js → chunk-JER5NQVM.js} +5 -5
- package/dist/chunks/{chunk-BJRKEPMP.js → chunk-KPU2XHOS.js} +5 -2
- package/dist/chunks/{chunk-BJRKEPMP.js.map → chunk-KPU2XHOS.js.map} +2 -2
- package/dist/chunks/{chunk-NP76N4HQ.js → chunk-LKAGAQ3M.js} +2 -2
- package/dist/chunks/{chunk-3FHJUHAS.js → chunk-S2CX6HFM.js} +260 -25
- package/dist/chunks/chunk-S2CX6HFM.js.map +7 -0
- package/dist/chunks/{configService-IGJEC3MC.js → configService-CCA6AIDI.js} +3 -3
- package/dist/entries/server-router.js +9 -9
- package/dist/entries/server-router.js.map +2 -2
- package/dist/lib/client/index.js +54 -20
- package/dist/lib/client/index.js.map +3 -3
- package/dist/lib/server/index.js +9 -9
- package/dist/lib/shared/index.js +46 -10
- package/dist/lib/shared/index.js.map +3 -3
- package/entries/server-router.tsx +6 -2
- package/lib/client/core/ComponentBuilder.ts +8 -1
- package/lib/client/core/builders/embedBuilder.ts +15 -2
- package/lib/client/core/builders/linkNodeBuilder.ts +15 -2
- package/lib/client/core/builders/localeListBuilder.ts +17 -6
- package/lib/client/styles/StyleInjector.ts +3 -2
- package/lib/client/theme.ts +4 -4
- package/lib/server/cssGenerator.test.ts +64 -1
- package/lib/server/cssGenerator.ts +48 -9
- package/lib/server/providers/fileSystemCMSProvider.test.ts +163 -0
- package/lib/server/providers/fileSystemCMSProvider.ts +200 -11
- package/lib/server/routes/index.ts +1 -1
- package/lib/server/routes/pages.ts +23 -1
- package/lib/server/services/cmsService.test.ts +246 -0
- package/lib/server/services/cmsService.ts +122 -5
- package/lib/server/services/configService.ts +5 -0
- package/lib/server/ssr/attributeBuilder.ts +41 -0
- package/lib/server/ssr/htmlGenerator.test.ts +113 -0
- package/lib/server/ssr/htmlGenerator.ts +51 -4
- package/lib/server/ssr/liveReloadIntegration.test.ts +209 -0
- package/lib/server/ssr/ssrRenderer.test.ts +306 -0
- package/lib/server/ssr/ssrRenderer.ts +182 -44
- package/lib/shared/cssGeneration.test.ts +267 -1
- package/lib/shared/cssGeneration.ts +240 -18
- package/lib/shared/cssProperties.test.ts +247 -1
- package/lib/shared/cssProperties.ts +196 -6
- package/lib/shared/interfaces/contentProvider.ts +39 -6
- package/lib/shared/pathSecurity.ts +16 -0
- package/lib/shared/responsiveScaling.test.ts +143 -0
- package/lib/shared/responsiveScaling.ts +253 -2
- package/lib/shared/themeDefaults.test.ts +3 -3
- package/lib/shared/themeDefaults.ts +3 -3
- package/lib/shared/types/cms.ts +28 -3
- package/lib/shared/types/index.ts +1 -0
- package/lib/shared/utilityClassConfig.ts +3 -0
- package/lib/shared/utilityClassMapper.test.ts +123 -0
- package/lib/shared/utilityClassMapper.ts +179 -8
- package/lib/shared/validation/schemas.ts +15 -1
- package/lib/shared/validation/validators.ts +26 -1
- package/package.json +1 -1
- package/dist/chunks/chunk-3FHJUHAS.js.map +0 -7
- package/dist/chunks/chunk-B2RTLDXY.js.map +0 -7
- package/dist/chunks/chunk-TPQ7APVQ.js.map +0 -7
- package/dist/chunks/chunk-UUA5LEWF.js.map +0 -7
- /package/dist/chunks/{chunk-RQSTH2BS.js.map → chunk-H4JSCDNW.js.map} +0 -0
- /package/dist/chunks/{chunk-D5E3OKSL.js.map → chunk-JER5NQVM.js.map} +0 -0
- /package/dist/chunks/{chunk-NP76N4HQ.js.map → chunk-LKAGAQ3M.js.map} +0 -0
- /package/dist/chunks/{configService-IGJEC3MC.js.map → configService-CCA6AIDI.js.map} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
configService
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-KPU2XHOS.js";
|
|
4
4
|
import {
|
|
5
5
|
projectPaths,
|
|
6
6
|
resolveProjectPath,
|
|
@@ -13,10 +13,12 @@ import {
|
|
|
13
13
|
writeFile
|
|
14
14
|
} from "./chunk-WQFG7PAH.js";
|
|
15
15
|
import {
|
|
16
|
+
CMS_DRAFT_SUFFIX,
|
|
17
|
+
isReservedDraftFilename,
|
|
16
18
|
isSafePathSegment,
|
|
17
19
|
isValidIdentifier,
|
|
18
20
|
resolvePaletteColor
|
|
19
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-J23ZX5AP.js";
|
|
20
22
|
import {
|
|
21
23
|
extractAttributesFromNode,
|
|
22
24
|
isHtmlMapping,
|
|
@@ -24,7 +26,7 @@ import {
|
|
|
24
26
|
processStructure,
|
|
25
27
|
resolveHtmlMapping,
|
|
26
28
|
skipEmptyTemplateAttributes
|
|
27
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-LKAGAQ3M.js";
|
|
28
30
|
import {
|
|
29
31
|
DEFAULT_PREFETCH_CONFIG,
|
|
30
32
|
SSRRegistry,
|
|
@@ -61,20 +63,26 @@ import {
|
|
|
61
63
|
resolveTemplateRawValue,
|
|
62
64
|
responsiveStylesToClasses,
|
|
63
65
|
singularize,
|
|
66
|
+
validateCMSDraftItem,
|
|
64
67
|
validateCMSItem,
|
|
65
68
|
validateComponentDefinition
|
|
66
|
-
} from "./chunk-
|
|
69
|
+
} from "./chunk-S2CX6HFM.js";
|
|
67
70
|
import {
|
|
68
71
|
DEFAULT_BREAKPOINTS,
|
|
72
|
+
DEFAULT_FLUID_RANGE,
|
|
69
73
|
DEFAULT_I18N_CONFIG,
|
|
74
|
+
DEFAULT_SITE_MARGIN,
|
|
75
|
+
buildFluidPropertyValue,
|
|
70
76
|
buildLocalizedPath,
|
|
77
|
+
buildSiteMarginClamp,
|
|
71
78
|
extractLocaleFromPath,
|
|
79
|
+
getSmallestBreakpointName,
|
|
72
80
|
isI18nValue,
|
|
73
81
|
migrateI18nConfig,
|
|
74
82
|
normalizeBreakpointConfig,
|
|
75
83
|
resolveI18nValue,
|
|
76
84
|
scalePropertyValue
|
|
77
|
-
} from "./chunk-
|
|
85
|
+
} from "./chunk-AZQYF6KE.js";
|
|
78
86
|
import {
|
|
79
87
|
isTiptapDocument,
|
|
80
88
|
tiptapToHtml
|
|
@@ -717,6 +725,7 @@ var CMSService = class {
|
|
|
717
725
|
schemaCache = /* @__PURE__ */ new Map();
|
|
718
726
|
routePatterns = [];
|
|
719
727
|
provider;
|
|
728
|
+
previewMode;
|
|
720
729
|
/** Item cache with TTL-based expiration */
|
|
721
730
|
itemsCache = /* @__PURE__ */ new Map();
|
|
722
731
|
/** Cache TTL in milliseconds (5 seconds) */
|
|
@@ -724,9 +733,11 @@ var CMSService = class {
|
|
|
724
733
|
/**
|
|
725
734
|
* Creates a new CMSService instance
|
|
726
735
|
* @param provider - Optional CMSProvider for loading data (enables DI for testing)
|
|
736
|
+
* @param options - Service-level flags (preview mode for dev server)
|
|
727
737
|
*/
|
|
728
|
-
constructor(provider) {
|
|
738
|
+
constructor(provider, options = {}) {
|
|
729
739
|
this.provider = provider;
|
|
740
|
+
this.previewMode = options.previewMode === true;
|
|
730
741
|
}
|
|
731
742
|
/**
|
|
732
743
|
* Set the CMS provider
|
|
@@ -737,9 +748,14 @@ var CMSService = class {
|
|
|
737
748
|
this.provider = provider;
|
|
738
749
|
}
|
|
739
750
|
/**
|
|
740
|
-
* Get items with caching
|
|
741
|
-
*
|
|
742
|
-
*
|
|
751
|
+
* Get items with caching, used by SSR-facing read methods.
|
|
752
|
+
*
|
|
753
|
+
* In preview mode (dev server) drafts are merged over published — drafts
|
|
754
|
+
* win on a per-`_filename` basis and draft-only items are included — so
|
|
755
|
+
* the editor preview reflects unpublished edits. Returns published-only
|
|
756
|
+
* otherwise. Rich-text fields are preprocessed to HTML for template
|
|
757
|
+
* interpolation either way.
|
|
758
|
+
*
|
|
743
759
|
* @param collection - Collection ID to fetch items for
|
|
744
760
|
* @returns Array of CMSItems with rich-text fields converted to HTML markers
|
|
745
761
|
*/
|
|
@@ -749,7 +765,20 @@ var CMSService = class {
|
|
|
749
765
|
if (cached && now - cached.timestamp < this.ITEMS_CACHE_TTL) {
|
|
750
766
|
return cached.items;
|
|
751
767
|
}
|
|
752
|
-
|
|
768
|
+
let rawItems = await this.provider.getItems(collection);
|
|
769
|
+
if (this.previewMode) {
|
|
770
|
+
const drafts = await this.provider.getAllDrafts(collection);
|
|
771
|
+
if (drafts.length > 0) {
|
|
772
|
+
const byFilename = /* @__PURE__ */ new Map();
|
|
773
|
+
for (const item of rawItems) {
|
|
774
|
+
if (item._filename) byFilename.set(item._filename, item);
|
|
775
|
+
}
|
|
776
|
+
for (const draft of drafts) {
|
|
777
|
+
if (draft._filename) byFilename.set(draft._filename, draft);
|
|
778
|
+
}
|
|
779
|
+
rawItems = Array.from(byFilename.values());
|
|
780
|
+
}
|
|
781
|
+
}
|
|
753
782
|
const items = this.preprocessRichTextFields(collection, rawItems);
|
|
754
783
|
this.itemsCache.set(collection, { items, timestamp: now });
|
|
755
784
|
return items;
|
|
@@ -987,6 +1016,71 @@ var CMSService = class {
|
|
|
987
1016
|
* Only clears items cache and provider cache before re-initializing.
|
|
988
1017
|
* Schema/route caches are swapped atomically inside initialize().
|
|
989
1018
|
*/
|
|
1019
|
+
// ----------------------------------------------------------------------
|
|
1020
|
+
// Draft-version methods (Studio-only — never used by SSR / static export)
|
|
1021
|
+
// ----------------------------------------------------------------------
|
|
1022
|
+
/**
|
|
1023
|
+
* Load both published and draft versions of an item, used by the editor.
|
|
1024
|
+
* Returns `{}` when neither version exists.
|
|
1025
|
+
*/
|
|
1026
|
+
async getItemVersions(collection, filename) {
|
|
1027
|
+
if (!this.provider) return {};
|
|
1028
|
+
const [published, draft] = await Promise.all([
|
|
1029
|
+
this.provider.getItemByFilename(collection, filename),
|
|
1030
|
+
this.provider.getDraft(collection, filename)
|
|
1031
|
+
]);
|
|
1032
|
+
const result = {};
|
|
1033
|
+
if (published) result.published = published;
|
|
1034
|
+
if (draft) result.draft = draft;
|
|
1035
|
+
return result;
|
|
1036
|
+
}
|
|
1037
|
+
/**
|
|
1038
|
+
* List all items in a collection for the Studio item list:
|
|
1039
|
+
* - Published items annotated with `_hasDraft: true` when a draft sibling exists.
|
|
1040
|
+
* - Draft-only items (no published file yet) returned with `_isDraft: true`.
|
|
1041
|
+
*/
|
|
1042
|
+
async listItemsWithDraftFlag(collection) {
|
|
1043
|
+
if (!this.provider) return [];
|
|
1044
|
+
const [published, drafts] = await Promise.all([
|
|
1045
|
+
this.provider.getItems(collection),
|
|
1046
|
+
this.provider.getAllDrafts(collection)
|
|
1047
|
+
]);
|
|
1048
|
+
const draftFilenames = new Set(drafts.map((d) => d._filename).filter(Boolean));
|
|
1049
|
+
const annotatedPublished = published.map((item) => {
|
|
1050
|
+
if (item._filename && draftFilenames.has(item._filename)) {
|
|
1051
|
+
return { ...item, _hasDraft: true };
|
|
1052
|
+
}
|
|
1053
|
+
return item;
|
|
1054
|
+
});
|
|
1055
|
+
const publishedFilenames = new Set(published.map((i) => i._filename).filter(Boolean));
|
|
1056
|
+
const draftOnly = drafts.filter((d) => d._filename && !publishedFilenames.has(d._filename));
|
|
1057
|
+
return [...annotatedPublished, ...draftOnly];
|
|
1058
|
+
}
|
|
1059
|
+
/** Pass-through to provider; no caching (drafts are read fresh in the editor). */
|
|
1060
|
+
async getDraft(collection, filename) {
|
|
1061
|
+
if (!this.provider) return null;
|
|
1062
|
+
return this.provider.getDraft(collection, filename);
|
|
1063
|
+
}
|
|
1064
|
+
async hasDraft(collection, filename) {
|
|
1065
|
+
if (!this.provider) return false;
|
|
1066
|
+
return this.provider.hasDraft(collection, filename);
|
|
1067
|
+
}
|
|
1068
|
+
async saveDraft(collection, item) {
|
|
1069
|
+
if (!this.provider) throw new Error("CMS provider not configured");
|
|
1070
|
+
await this.provider.saveDraft(collection, item);
|
|
1071
|
+
this.itemsCache.delete(collection);
|
|
1072
|
+
}
|
|
1073
|
+
async discardDraft(collection, filename) {
|
|
1074
|
+
if (!this.provider) throw new Error("CMS provider not configured");
|
|
1075
|
+
await this.provider.discardDraft(collection, filename);
|
|
1076
|
+
this.itemsCache.delete(collection);
|
|
1077
|
+
}
|
|
1078
|
+
async publishDraft(collection, filename) {
|
|
1079
|
+
if (!this.provider) throw new Error("CMS provider not configured");
|
|
1080
|
+
const item = await this.provider.publishDraft(collection, filename);
|
|
1081
|
+
this.itemsCache.delete(collection);
|
|
1082
|
+
return item;
|
|
1083
|
+
}
|
|
990
1084
|
async refreshSchemas() {
|
|
991
1085
|
if (!this.provider) {
|
|
992
1086
|
return;
|
|
@@ -1295,20 +1389,45 @@ ${cssVars.join("\n")}
|
|
|
1295
1389
|
return cssBlocks.join("\n\n");
|
|
1296
1390
|
}
|
|
1297
1391
|
function generateVariablesCSS(config, breakpoints, responsiveScales) {
|
|
1298
|
-
|
|
1392
|
+
const cssBlocks = [];
|
|
1393
|
+
const fluidActive = responsiveScales?.enabled === true && responsiveScales?.mode === "fluid";
|
|
1394
|
+
const fluidRange = responsiveScales?.fluidRange ?? DEFAULT_FLUID_RANGE;
|
|
1395
|
+
const fluidBaseRef = responsiveScales?.baseReference || 16;
|
|
1396
|
+
const smallestBp = fluidActive ? getSmallestBreakpointName(breakpoints) : null;
|
|
1397
|
+
const baseVars = (config.variables ?? []).map((v) => {
|
|
1398
|
+
if (fluidActive && smallestBp && v.type !== "none") {
|
|
1399
|
+
const categoryScales = responsiveScales?.[v.type];
|
|
1400
|
+
const scale = categoryScales?.[smallestBp];
|
|
1401
|
+
if (scale != null && scale !== 1) {
|
|
1402
|
+
const fluid = buildFluidPropertyValue(
|
|
1403
|
+
v.value,
|
|
1404
|
+
scale,
|
|
1405
|
+
fluidRange.min,
|
|
1406
|
+
fluidRange.max,
|
|
1407
|
+
fluidBaseRef
|
|
1408
|
+
);
|
|
1409
|
+
if (fluid) return ` ${v.cssVar}: ${fluid};`;
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
return ` ${v.cssVar}: ${v.value};`;
|
|
1413
|
+
});
|
|
1414
|
+
if (fluidActive) {
|
|
1415
|
+
const siteMargin = responsiveScales?.siteMargin ?? DEFAULT_SITE_MARGIN;
|
|
1416
|
+
baseVars.push(` --site-margin: ${buildSiteMarginClamp(siteMargin, fluidRange)};`);
|
|
1417
|
+
}
|
|
1418
|
+
if (baseVars.length === 0) {
|
|
1299
1419
|
return "";
|
|
1300
1420
|
}
|
|
1301
|
-
const cssBlocks = [];
|
|
1302
|
-
const baseVars = config.variables.map((v) => ` ${v.cssVar}: ${v.value};`);
|
|
1303
1421
|
cssBlocks.push(`:root {
|
|
1304
1422
|
${baseVars.join("\n")}
|
|
1305
1423
|
}`);
|
|
1306
|
-
|
|
1424
|
+
const userVariables = config.variables ?? [];
|
|
1425
|
+
if (breakpoints && responsiveScales?.enabled && !fluidActive && userVariables.length > 0) {
|
|
1307
1426
|
const baseRef = responsiveScales.baseReference || 16;
|
|
1308
1427
|
const sortedBreakpoints = Object.entries(breakpoints).sort((a, b) => b[1].breakpoint - a[1].breakpoint);
|
|
1309
1428
|
for (const [bpName, bpEntry] of sortedBreakpoints) {
|
|
1310
1429
|
const scaledVars = [];
|
|
1311
|
-
for (const variable of
|
|
1430
|
+
for (const variable of userVariables) {
|
|
1312
1431
|
if (variable.scales && variable.scales[bpName]) {
|
|
1313
1432
|
const overrideValue = variable.scales[bpName];
|
|
1314
1433
|
if (overrideValue !== variable.value) {
|
|
@@ -1389,6 +1508,27 @@ function buildAttributes(props, exclude = []) {
|
|
|
1389
1508
|
}
|
|
1390
1509
|
return attrs.length > 0 ? " " + attrs.join(" ") : "";
|
|
1391
1510
|
}
|
|
1511
|
+
function buildEditorAttrs(opts) {
|
|
1512
|
+
const { elementPath } = opts;
|
|
1513
|
+
if (!elementPath) return "";
|
|
1514
|
+
const parts = [`data-element-path="${escapeHtml(elementPath.join(","))}"`];
|
|
1515
|
+
if (opts.cmsItemIndexPath && opts.cmsItemIndexPath.length > 0) {
|
|
1516
|
+
parts.push(`data-cms-item-index="${escapeHtml(opts.cmsItemIndexPath.join("."))}"`);
|
|
1517
|
+
if (opts.cmsListPaths && opts.cmsListPaths.length === opts.cmsItemIndexPath.length) {
|
|
1518
|
+
const ctx = JSON.stringify({ itemIndexPath: opts.cmsItemIndexPath, listPaths: opts.cmsListPaths });
|
|
1519
|
+
parts.push(`data-cms-context="${escapeHtml(ctx)}"`);
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
if (opts.isCMSListContainer) parts.push(`data-cms-list="true"`);
|
|
1523
|
+
if (opts.isComponentRoot) parts.push(`data-component-root="true"`);
|
|
1524
|
+
if (opts.parentComponentName) {
|
|
1525
|
+
parts.push(`data-parent-component="${escapeHtml(opts.parentComponentName)}"`);
|
|
1526
|
+
}
|
|
1527
|
+
if (opts.componentContext && !opts.isSlotContent) {
|
|
1528
|
+
parts.push(`data-component-context="${escapeHtml(opts.componentContext)}"`);
|
|
1529
|
+
}
|
|
1530
|
+
return " " + parts.join(" ");
|
|
1531
|
+
}
|
|
1392
1532
|
function styleToString(style) {
|
|
1393
1533
|
if (!style || Object.keys(style).length === 0) return "";
|
|
1394
1534
|
const declarations = [];
|
|
@@ -1901,6 +2041,12 @@ function getDOMPurify() {
|
|
|
1901
2041
|
function getTemplateContext(ctx) {
|
|
1902
2042
|
return ctx.templateContext || null;
|
|
1903
2043
|
}
|
|
2044
|
+
function buildListResolutionScope(ctx) {
|
|
2045
|
+
const tplCtx = ctx.templateContext;
|
|
2046
|
+
const props = ctx.componentResolvedProps;
|
|
2047
|
+
if (!tplCtx && !props) return void 0;
|
|
2048
|
+
return { ...props ?? {}, ...tplCtx ?? {} };
|
|
2049
|
+
}
|
|
1904
2050
|
function getI18nResolver(ctx) {
|
|
1905
2051
|
return createI18nResolver(ctx.locale, ctx.i18nConfig);
|
|
1906
2052
|
}
|
|
@@ -1939,7 +2085,11 @@ function processStyleToClasses(style, ctx) {
|
|
|
1939
2085
|
getI18nResolver(ctx)
|
|
1940
2086
|
);
|
|
1941
2087
|
}
|
|
1942
|
-
|
|
2088
|
+
const fluidActive = ctx.responsiveScales?.enabled === true && ctx.responsiveScales?.mode === "fluid";
|
|
2089
|
+
return responsiveStylesToClasses(
|
|
2090
|
+
processedStyle,
|
|
2091
|
+
{ fluidActive, responsiveScales: ctx.responsiveScales }
|
|
2092
|
+
);
|
|
1943
2093
|
}
|
|
1944
2094
|
function evaluateIfCondition(node, ctx) {
|
|
1945
2095
|
const ifValue = hasIf(node) ? node.if : void 0;
|
|
@@ -1971,31 +2121,31 @@ function evaluateIfCondition(node, ctx) {
|
|
|
1971
2121
|
}
|
|
1972
2122
|
return true;
|
|
1973
2123
|
}
|
|
1974
|
-
function resolveFilterValue(value,
|
|
1975
|
-
if (!
|
|
2124
|
+
function resolveFilterValue(value, scope) {
|
|
2125
|
+
if (!scope || typeof value !== "string" || !value.startsWith("{{") || !value.endsWith("}}")) {
|
|
1976
2126
|
return value;
|
|
1977
2127
|
}
|
|
1978
2128
|
const path2 = value.slice(2, -2).trim();
|
|
1979
|
-
const resolved = getNestedValue(
|
|
2129
|
+
const resolved = getNestedValue(scope, path2);
|
|
1980
2130
|
return resolved !== void 0 ? resolved : value;
|
|
1981
2131
|
}
|
|
1982
|
-
function resolveFilterTemplates(filter,
|
|
1983
|
-
if (!filter || !
|
|
2132
|
+
function resolveFilterTemplates(filter, scope) {
|
|
2133
|
+
if (!filter || !scope) return filter;
|
|
1984
2134
|
if (Array.isArray(filter)) {
|
|
1985
2135
|
return filter.map((cond) => ({
|
|
1986
2136
|
...cond,
|
|
1987
|
-
value: resolveFilterValue(cond.value,
|
|
2137
|
+
value: resolveFilterValue(cond.value, scope)
|
|
1988
2138
|
}));
|
|
1989
2139
|
}
|
|
1990
2140
|
if ("field" in filter && "value" in filter) {
|
|
1991
2141
|
return {
|
|
1992
2142
|
...filter,
|
|
1993
|
-
value: resolveFilterValue(filter.value,
|
|
2143
|
+
value: resolveFilterValue(filter.value, scope)
|
|
1994
2144
|
};
|
|
1995
2145
|
}
|
|
1996
2146
|
const resolved = {};
|
|
1997
2147
|
for (const [key, value] of Object.entries(filter)) {
|
|
1998
|
-
resolved[key] = resolveFilterValue(value,
|
|
2148
|
+
resolved[key] = resolveFilterValue(value, scope);
|
|
1999
2149
|
}
|
|
2000
2150
|
return resolved;
|
|
2001
2151
|
}
|
|
@@ -2032,7 +2182,7 @@ async function expandRichTextComponents(html, ctx) {
|
|
|
2032
2182
|
return resolved.join("");
|
|
2033
2183
|
}
|
|
2034
2184
|
var ssrComponentRegistry = new SSRRegistry();
|
|
2035
|
-
async function buildComponentHTML(node, globalComponents = {}, pageComponents = {}, locale, i18nConfig, slugMappings, pagePath, cmsContext, cmsService, isProductionBuild) {
|
|
2185
|
+
async function buildComponentHTML(node, globalComponents = {}, pageComponents = {}, locale, i18nConfig, slugMappings, pagePath, cmsContext, cmsService, isProductionBuild, injectEditorAttrs) {
|
|
2036
2186
|
const interactiveStylesMap = /* @__PURE__ */ new Map();
|
|
2037
2187
|
const preloadImages = [];
|
|
2038
2188
|
const neededCollections = /* @__PURE__ */ new Set();
|
|
@@ -2067,7 +2217,9 @@ async function buildComponentHTML(node, globalComponents = {}, pageComponents =
|
|
|
2067
2217
|
// Collect SSR fallback HTML for complex nodes
|
|
2068
2218
|
processedRawHtmlCollector,
|
|
2069
2219
|
// Collect raw→processed HTML for Astro exporter
|
|
2070
|
-
imageFormat: configService.getImageFormat()
|
|
2220
|
+
imageFormat: configService.getImageFormat(),
|
|
2221
|
+
injectEditorAttrs,
|
|
2222
|
+
responsiveScales: configService.getResponsiveScales()
|
|
2071
2223
|
};
|
|
2072
2224
|
const html = await renderNode(node, ctx);
|
|
2073
2225
|
return { html, interactiveStylesMap, preloadImages, neededCollections, ssrFallbackCollector, processedRawHtmlCollector };
|
|
@@ -2098,7 +2250,7 @@ async function renderNestedListPlaceholder(node, ctx) {
|
|
|
2098
2250
|
};
|
|
2099
2251
|
const templateContent = node.children ? await renderChildrenAsync(node.children, childTemplateCtx) : "";
|
|
2100
2252
|
const configJson = escapeHtml(JSON.stringify(config));
|
|
2101
|
-
return `<div data-cms-list-nested="true" data-collection="${escapeHtml(sourceStr)}" data-cms-config="${configJson}"><template data-nested-template>${templateContent}</template></div>`;
|
|
2253
|
+
return `<div data-cms-list-nested="true" data-collection="${escapeHtml(sourceStr)}" data-cms-config="${configJson}"${editorAttrs(ctx, { isCMSListContainer: true })}><template data-nested-template>${templateContent}</template></div>`;
|
|
2102
2254
|
}
|
|
2103
2255
|
async function processList(node, ctx) {
|
|
2104
2256
|
const nodeType = node.type;
|
|
@@ -2157,7 +2309,9 @@ async function processList(node, ctx) {
|
|
|
2157
2309
|
);
|
|
2158
2310
|
const itemCtx = {
|
|
2159
2311
|
...ctx,
|
|
2160
|
-
templateContext
|
|
2312
|
+
templateContext,
|
|
2313
|
+
cmsItemIndexPath: [...ctx.cmsItemIndexPath ?? [], i],
|
|
2314
|
+
cmsListPaths: [...ctx.cmsListPaths ?? [], ctx.elementPath ?? []]
|
|
2161
2315
|
};
|
|
2162
2316
|
const childrenHtml = await renderChildrenAsync(node.children || [], itemCtx);
|
|
2163
2317
|
renderedItems.push(childrenHtml);
|
|
@@ -2201,7 +2355,8 @@ async function getCollectionItems(node, source, ctx) {
|
|
|
2201
2355
|
resolvedIds = Array.isArray(value) ? value.map((v) => String(v)) : String(value);
|
|
2202
2356
|
}
|
|
2203
2357
|
} else {
|
|
2204
|
-
const
|
|
2358
|
+
const mergedScope = buildListResolutionScope(ctx) ?? {};
|
|
2359
|
+
const parentContext = { _type: "template", ...mergedScope };
|
|
2205
2360
|
resolvedIds = resolveItemsTemplate(node.items, parentContext);
|
|
2206
2361
|
}
|
|
2207
2362
|
if (!resolvedIds) {
|
|
@@ -2219,7 +2374,7 @@ async function getCollectionItems(node, source, ctx) {
|
|
|
2219
2374
|
} else {
|
|
2220
2375
|
const query = {
|
|
2221
2376
|
collection: source,
|
|
2222
|
-
filter: resolveFilterTemplates(node.filter, ctx
|
|
2377
|
+
filter: resolveFilterTemplates(node.filter, buildListResolutionScope(ctx)),
|
|
2223
2378
|
sort: node.sort,
|
|
2224
2379
|
limit: node.limit,
|
|
2225
2380
|
offset: node.offset,
|
|
@@ -2274,11 +2429,13 @@ function buildNodeElementClass(ctx, label, isSlotContent2) {
|
|
|
2274
2429
|
const useComponentContext = !isSlotContent2 && Boolean(ctx.componentContext);
|
|
2275
2430
|
const effectiveFileType = useComponentContext ? "component" : "page";
|
|
2276
2431
|
const effectiveFileName = useComponentContext ? ctx.componentContext : pagePath ? pagePath.replace(/^\//, "").replace(/\//g, "_") || "index" : "page";
|
|
2432
|
+
const rawPath = ctx.elementPath || [];
|
|
2433
|
+
const path2 = useComponentContext && ctx.componentRootPath ? rawPath.slice(ctx.componentRootPath.length) : rawPath;
|
|
2277
2434
|
const elementClassCtx = {
|
|
2278
2435
|
fileType: effectiveFileType,
|
|
2279
2436
|
fileName: effectiveFileName || "page",
|
|
2280
2437
|
label,
|
|
2281
|
-
path:
|
|
2438
|
+
path: path2
|
|
2282
2439
|
};
|
|
2283
2440
|
return generateElementClassName(elementClassCtx);
|
|
2284
2441
|
}
|
|
@@ -2298,6 +2455,26 @@ function buildCssVariableStyleAttr(cssVariables) {
|
|
|
2298
2455
|
const styleString = Object.entries(cssVariables).map(([k, v]) => `${k}: ${v}`).join("; ");
|
|
2299
2456
|
return ` style="${escapeHtml(styleString)}"`;
|
|
2300
2457
|
}
|
|
2458
|
+
function arraysEqual(a, b) {
|
|
2459
|
+
if (!a || !b || a.length !== b.length) return false;
|
|
2460
|
+
for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
|
|
2461
|
+
return true;
|
|
2462
|
+
}
|
|
2463
|
+
function editorAttrs(ctx, opts = {}) {
|
|
2464
|
+
if (!ctx.injectEditorAttrs) return "";
|
|
2465
|
+
const isComponentRoot = !!ctx.componentContext && !opts.isSlotContent && arraysEqual(ctx.elementPath, ctx.componentRootPath);
|
|
2466
|
+
const effectiveParent = opts.isSlotContent ? ctx.parentComponentName : isComponentRoot ? ctx.parentComponentName : ctx.componentContext ?? ctx.parentComponentName;
|
|
2467
|
+
return buildEditorAttrs({
|
|
2468
|
+
elementPath: ctx.elementPath,
|
|
2469
|
+
cmsItemIndexPath: ctx.cmsItemIndexPath,
|
|
2470
|
+
cmsListPaths: ctx.cmsListPaths,
|
|
2471
|
+
componentContext: ctx.componentContext,
|
|
2472
|
+
parentComponentName: effectiveParent,
|
|
2473
|
+
isComponentRoot,
|
|
2474
|
+
isSlotContent: opts.isSlotContent,
|
|
2475
|
+
isCMSListContainer: opts.isCMSListContainer
|
|
2476
|
+
});
|
|
2477
|
+
}
|
|
2301
2478
|
async function renderNode(node, ctx) {
|
|
2302
2479
|
const { breakpoints, viewportWidth, locale, i18nConfig, slugMappings, pagePath } = ctx;
|
|
2303
2480
|
if (node === null || node === void 0) return "";
|
|
@@ -2406,7 +2583,7 @@ async function renderNode(node, ctx) {
|
|
|
2406
2583
|
delete nodeAttributes2.class;
|
|
2407
2584
|
const attrs2 = buildAttributes(nodeAttributes2);
|
|
2408
2585
|
const classAttr2 = classNames.length > 0 ? ` class="${escapeHtml(classNames.filter(Boolean).join(" "))}"` : "";
|
|
2409
|
-
return `<span${classAttr2}${embedStyleAttr}${attrs2}>${optimizedHtml}</span>`;
|
|
2586
|
+
return `<span${classAttr2}${embedStyleAttr}${attrs2}${editorAttrs(ctx, { isSlotContent: isSlotContent(node) })}>${optimizedHtml}</span>`;
|
|
2410
2587
|
}
|
|
2411
2588
|
const attrClassName2 = nodeAttributes2.className || nodeAttributes2.class || "";
|
|
2412
2589
|
if (attrClassName2) {
|
|
@@ -2416,7 +2593,7 @@ async function renderNode(node, ctx) {
|
|
|
2416
2593
|
delete nodeAttributes2.class;
|
|
2417
2594
|
const attrs = buildAttributes(nodeAttributes2);
|
|
2418
2595
|
const classAttr = classNames.length > 0 ? ` class="${escapeHtml(classNames.filter(Boolean).join(" "))}"` : "";
|
|
2419
|
-
return `<span${classAttr}${attrs}>${optimizedHtml}</span>`;
|
|
2596
|
+
return `<span${classAttr}${attrs}${editorAttrs(ctx, { isSlotContent: isSlotContent(node) })}>${optimizedHtml}</span>`;
|
|
2420
2597
|
}
|
|
2421
2598
|
if (isLinkNode(node)) {
|
|
2422
2599
|
let href = typeof node.href === "string" ? node.href : "#";
|
|
@@ -2477,7 +2654,7 @@ async function renderNode(node, ctx) {
|
|
|
2477
2654
|
const childPath = ctx.elementPath ? [...ctx.elementPath, index] : [index];
|
|
2478
2655
|
return renderNode(child, { ...ctx, elementPath: childPath });
|
|
2479
2656
|
}))).join("") : await renderNode(children, { ...ctx, elementPath: ctx.elementPath ? [...ctx.elementPath, 0] : [0] });
|
|
2480
|
-
return `<a href="${escapeHtml(String(href))}"${classAttr}${olinkStyleAttr}${attrs}>${childrenHTML}</a>`;
|
|
2657
|
+
return `<a href="${escapeHtml(String(href))}"${classAttr}${olinkStyleAttr}${attrs}${editorAttrs(ctx, { isSlotContent: isSlotContent(node) })}>${childrenHTML}</a>`;
|
|
2481
2658
|
}
|
|
2482
2659
|
if (isLocaleListNode(node)) {
|
|
2483
2660
|
return renderLocaleList(node, ctx);
|
|
@@ -2651,8 +2828,10 @@ async function renderComponent(componentName, propsWithStyleAndAttrs, children,
|
|
|
2651
2828
|
}
|
|
2652
2829
|
return await renderNode(processedStructure, {
|
|
2653
2830
|
...ctx,
|
|
2831
|
+
// The previously-active component (if any) becomes the parent for editor attrs
|
|
2832
|
+
parentComponentName: ctx.componentContext,
|
|
2654
2833
|
componentContext: componentName,
|
|
2655
|
-
|
|
2834
|
+
componentRootPath: ctx.elementPath,
|
|
2656
2835
|
componentResolvedProps: resolvedProps
|
|
2657
2836
|
});
|
|
2658
2837
|
} catch (error) {
|
|
@@ -2672,7 +2851,7 @@ async function renderLinkNode(propsWithStyleAndAttrs, children, ctx) {
|
|
|
2672
2851
|
const childPath = ctx.elementPath ? [...ctx.elementPath, index] : [index];
|
|
2673
2852
|
return renderNode(child, { ...ctx, elementPath: childPath });
|
|
2674
2853
|
}))).join("") : await renderNode(children, { ...ctx, elementPath: ctx.elementPath ? [...ctx.elementPath, 0] : [0] });
|
|
2675
|
-
return `<a href="${escapeHtml(String(href))}"${linkClassAttr}${attrs}>${childrenHTML}</a>`;
|
|
2854
|
+
return `<a href="${escapeHtml(String(href))}"${linkClassAttr}${attrs}${editorAttrs(ctx)}>${childrenHTML}</a>`;
|
|
2676
2855
|
}
|
|
2677
2856
|
async function renderHtmlElement(tag, propsWithStyleAndAttrs, children, ctx) {
|
|
2678
2857
|
let classValue = propsWithStyleAndAttrs.className ? String(propsWithStyleAndAttrs.className) : "";
|
|
@@ -2694,18 +2873,32 @@ async function renderHtmlElement(tag, propsWithStyleAndAttrs, children, ctx) {
|
|
|
2694
2873
|
const imageProps = ["src", "alt", "loading", "width", "height", "sizes", "srcset", "fetchpriority"];
|
|
2695
2874
|
const excludeProps = tag.toLowerCase() === "img" ? ["style", "className", ...imageProps] : ["style", "className"];
|
|
2696
2875
|
const attrs = buildAttributes(propsWithStyleAndAttrs, excludeProps);
|
|
2697
|
-
const
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2876
|
+
const isRawTextElement = tag.toLowerCase() === "style" || tag.toLowerCase() === "script";
|
|
2877
|
+
let childrenHTML;
|
|
2878
|
+
if (isRawTextElement) {
|
|
2879
|
+
const flatten = (node) => {
|
|
2880
|
+
if (node == null) return "";
|
|
2881
|
+
if (typeof node === "string") return node;
|
|
2882
|
+
if (typeof node === "number") return String(node);
|
|
2883
|
+
if (Array.isArray(node)) return node.map(flatten).join("");
|
|
2884
|
+
return "";
|
|
2885
|
+
};
|
|
2886
|
+
childrenHTML = flatten(children);
|
|
2887
|
+
} else {
|
|
2888
|
+
childrenHTML = Array.isArray(children) ? (await Promise.all(children.map((child, index) => {
|
|
2889
|
+
const childPath = ctx.elementPath ? [...ctx.elementPath, index] : [index];
|
|
2890
|
+
return renderNode(child, { ...ctx, elementPath: childPath });
|
|
2891
|
+
}))).join("") : await renderNode(children, { ...ctx, elementPath: ctx.elementPath ? [...ctx.elementPath, 0] : [0] });
|
|
2892
|
+
}
|
|
2893
|
+
const ea = editorAttrs(ctx);
|
|
2701
2894
|
const voidElements = ["img", "input", "br", "hr", "meta", "link", "area", "base", "col", "embed", "source", "track", "wbr"];
|
|
2702
2895
|
if (voidElements.includes(tag.toLowerCase())) {
|
|
2703
2896
|
if (tag.toLowerCase() === "img") {
|
|
2704
|
-
return renderImageElement(propsWithStyleAndAttrs, classAttr, styleAttr, attrs, ctx);
|
|
2897
|
+
return renderImageElement(propsWithStyleAndAttrs, classAttr, styleAttr, attrs + ea, ctx);
|
|
2705
2898
|
}
|
|
2706
|
-
return `<${tag}${classAttr}${styleAttr}${attrs} />`;
|
|
2899
|
+
return `<${tag}${classAttr}${styleAttr}${attrs}${ea} />`;
|
|
2707
2900
|
}
|
|
2708
|
-
return `<${tag}${classAttr}${styleAttr}${attrs}>${childrenHTML}</${tag}>`;
|
|
2901
|
+
return `<${tag}${classAttr}${styleAttr}${attrs}${ea}>${childrenHTML}</${tag}>`;
|
|
2709
2902
|
}
|
|
2710
2903
|
function renderImageElement(propsWithStyleAndAttrs, classAttr, styleAttr, attrs, ctx) {
|
|
2711
2904
|
const imgProps = propsWithStyleAndAttrs;
|
|
@@ -2793,9 +2986,13 @@ function renderLocaleList(node, ctx) {
|
|
|
2793
2986
|
localeIconMap.set(localeConfig.code, localeConfig.icon);
|
|
2794
2987
|
}
|
|
2795
2988
|
}
|
|
2989
|
+
const localeStyleOpts = {
|
|
2990
|
+
fluidActive: ctx.responsiveScales?.enabled === true && ctx.responsiveScales?.mode === "fluid",
|
|
2991
|
+
responsiveScales: ctx.responsiveScales
|
|
2992
|
+
};
|
|
2796
2993
|
let containerClasses = [];
|
|
2797
2994
|
if (nodeStyle) {
|
|
2798
|
-
containerClasses = responsiveStylesToClasses(nodeStyle);
|
|
2995
|
+
containerClasses = responsiveStylesToClasses(nodeStyle, localeStyleOpts);
|
|
2799
2996
|
}
|
|
2800
2997
|
const localeListInteractiveStyles = node.interactiveStyles;
|
|
2801
2998
|
const localeListGenerateElementClass = node.generateElementClass;
|
|
@@ -2812,21 +3009,21 @@ function renderLocaleList(node, ctx) {
|
|
|
2812
3009
|
const containerClassAttr = containerClasses.length > 0 ? ` class="${escapeHtml(containerClasses.join(" "))}"` : "";
|
|
2813
3010
|
let itemClasses = [];
|
|
2814
3011
|
if (node.itemStyle) {
|
|
2815
|
-
itemClasses = responsiveStylesToClasses(node.itemStyle);
|
|
3012
|
+
itemClasses = responsiveStylesToClasses(node.itemStyle, localeStyleOpts);
|
|
2816
3013
|
}
|
|
2817
3014
|
const itemClassAttr = itemClasses.length > 0 ? ` class="${escapeHtml(itemClasses.join(" "))}"` : "";
|
|
2818
3015
|
let activeItemClasses = [];
|
|
2819
3016
|
if (node.activeItemStyle) {
|
|
2820
|
-
activeItemClasses = responsiveStylesToClasses(node.activeItemStyle);
|
|
3017
|
+
activeItemClasses = responsiveStylesToClasses(node.activeItemStyle, localeStyleOpts);
|
|
2821
3018
|
}
|
|
2822
3019
|
let separatorClasses = [];
|
|
2823
3020
|
if (node.separatorStyle) {
|
|
2824
|
-
separatorClasses = responsiveStylesToClasses(node.separatorStyle);
|
|
3021
|
+
separatorClasses = responsiveStylesToClasses(node.separatorStyle, localeStyleOpts);
|
|
2825
3022
|
}
|
|
2826
3023
|
const separatorClassAttr = separatorClasses.length > 0 ? ` class="${escapeHtml(separatorClasses.join(" "))}"` : "";
|
|
2827
3024
|
let flagClasses = [];
|
|
2828
3025
|
if (node.flagStyle) {
|
|
2829
|
-
flagClasses = responsiveStylesToClasses(node.flagStyle);
|
|
3026
|
+
flagClasses = responsiveStylesToClasses(node.flagStyle, localeStyleOpts);
|
|
2830
3027
|
}
|
|
2831
3028
|
const flagClassAttr = flagClasses.length > 0 ? ` class="${escapeHtml(flagClasses.join(" "))}"` : "";
|
|
2832
3029
|
const currentItemClasses = [...itemClasses, ...activeItemClasses];
|
|
@@ -2861,7 +3058,7 @@ function renderLocaleList(node, ctx) {
|
|
|
2861
3058
|
const linksHTML = showSeparator ? links.join(`<span${separatorClassAttr}></span>`) : links.join("");
|
|
2862
3059
|
const nodeAttributes = extractAttributesFromNode(node);
|
|
2863
3060
|
const attrsStr = buildAttributes(nodeAttributes);
|
|
2864
|
-
const localeListResult = `<div data-locale-list="true"${containerClassAttr}${localeListStyleAttr}${attrsStr}>${linksHTML}</div>`;
|
|
3061
|
+
const localeListResult = `<div data-locale-list="true"${containerClassAttr}${localeListStyleAttr}${attrsStr}${editorAttrs(ctx)}>${linksHTML}</div>`;
|
|
2865
3062
|
if (ctx.ssrFallbackCollector && ctx.elementPath) {
|
|
2866
3063
|
ctx.ssrFallbackCollector.set(ctx.elementPath.join("."), localeListResult);
|
|
2867
3064
|
}
|
|
@@ -2869,7 +3066,7 @@ function renderLocaleList(node, ctx) {
|
|
|
2869
3066
|
}
|
|
2870
3067
|
return '<div data-locale-list="true"></div>';
|
|
2871
3068
|
}
|
|
2872
|
-
async function renderPageSSR(pageData, globalComponents = {}, pagePath = "/", baseUrl = "", locale, i18nConfig, slugMappings, cmsContext, cmsService, isProductionBuild) {
|
|
3069
|
+
async function renderPageSSR(pageData, globalComponents = {}, pagePath = "/", baseUrl = "", locale, i18nConfig, slugMappings, cmsContext, cmsService, isProductionBuild, injectEditorAttrs) {
|
|
2873
3070
|
const rootNode = pageData?.root || void 0;
|
|
2874
3071
|
if (!rootNode) {
|
|
2875
3072
|
throw new Error("Page data must have a root node");
|
|
@@ -2893,7 +3090,7 @@ async function renderPageSSR(pageData, globalComponents = {}, pagePath = "/", ba
|
|
|
2893
3090
|
}
|
|
2894
3091
|
}
|
|
2895
3092
|
const pageComponents = pageData?.components || {};
|
|
2896
|
-
const { html: contentHTML, interactiveStylesMap, preloadImages, neededCollections, ssrFallbackCollector, processedRawHtmlCollector } = rootNode ? await buildComponentHTML(rootNode, globalComponents, pageComponents, effectiveLocale, config, slugMappings, pagePath, cmsContext, cmsService, isProductionBuild) : { html: "", interactiveStylesMap: /* @__PURE__ */ new Map(), preloadImages: [], neededCollections: /* @__PURE__ */ new Set(), ssrFallbackCollector: /* @__PURE__ */ new Map(), processedRawHtmlCollector: /* @__PURE__ */ new Map() };
|
|
3093
|
+
const { html: contentHTML, interactiveStylesMap, preloadImages, neededCollections, ssrFallbackCollector, processedRawHtmlCollector } = rootNode ? await buildComponentHTML(rootNode, globalComponents, pageComponents, effectiveLocale, config, slugMappings, pagePath, cmsContext, cmsService, isProductionBuild, injectEditorAttrs) : { html: "", interactiveStylesMap: /* @__PURE__ */ new Map(), preloadImages: [], neededCollections: /* @__PURE__ */ new Set(), ssrFallbackCollector: /* @__PURE__ */ new Map(), processedRawHtmlCollector: /* @__PURE__ */ new Map() };
|
|
2897
3094
|
const javascript = await collectComponentJavaScript(globalComponents, pageComponents);
|
|
2898
3095
|
const componentCSS = collectComponentCSS(globalComponents, pageComponents);
|
|
2899
3096
|
const fullUrl = baseUrl ? `${baseUrl}${pagePath}` : pagePath;
|
|
@@ -3217,7 +3414,7 @@ function generateImagePreloadTags(preloadImages) {
|
|
|
3217
3414
|
}
|
|
3218
3415
|
function minifyCSS(code) {
|
|
3219
3416
|
if (!code.trim()) return code;
|
|
3220
|
-
return code.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\s*([{}
|
|
3417
|
+
return code.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\s*([{};:,>~])\s*/g, "$1").replace(/\s+/g, " ").replace(/\{\s+/g, "{").replace(/\s+\}/g, "}").replace(/;}/g, "}").trim();
|
|
3221
3418
|
}
|
|
3222
3419
|
async function generateSSRHTML(pageDataOrOptions, globalComponents = {}, pagePath = "/", baseUrl = "", useBuiltBundle = false, locale, slugMappings, cmsContext, cmsService, externalScriptPath) {
|
|
3223
3420
|
let options;
|
|
@@ -3254,11 +3451,12 @@ async function generateSSRHTML(pageDataOrOptions, globalComponents = {}, pagePat
|
|
|
3254
3451
|
pageCustomCode,
|
|
3255
3452
|
clientDataCollections,
|
|
3256
3453
|
injectLiveReload = false,
|
|
3454
|
+
injectEditorAttrs = false,
|
|
3257
3455
|
isEditor = false,
|
|
3258
3456
|
isProductionBuild = false,
|
|
3259
3457
|
serverPort
|
|
3260
3458
|
} = options;
|
|
3261
|
-
const rendered = await renderPageSSR(pageData, components, path2, base, loc, void 0, slugs, cms, cmsServ, isProductionBuild);
|
|
3459
|
+
const rendered = await renderPageSSR(pageData, components, path2, base, loc, void 0, slugs, cms, cmsServ, isProductionBuild, injectEditorAttrs);
|
|
3262
3460
|
let finalClientDataCollections = clientDataCollections;
|
|
3263
3461
|
if (rendered.neededCollections.size > 0 && cmsServ) {
|
|
3264
3462
|
finalClientDataCollections = clientDataCollections ? new Map(clientDataCollections) : /* @__PURE__ */ new Map();
|
|
@@ -3440,7 +3638,7 @@ picture {
|
|
|
3440
3638
|
const scriptPreloadTag = extScriptPath ? `<link rel="preload" href="${extScriptPath}" as="script">` : "";
|
|
3441
3639
|
const imagePreloadTags = generateImagePreloadTags(rendered.preloadImages);
|
|
3442
3640
|
const wsUrl = serverPort ? `'ws://localhost:${serverPort}/hmr'` : `location.origin.replace('http','ws')+'/hmr'`;
|
|
3443
|
-
const liveReloadScript = injectLiveReload ? `<script>(function(){var ws,timer,gen=0;function connect(){ws=new WebSocket(${wsUrl});ws.onmessage=function(e){var d=JSON.parse(e.data);if(d.type==='hmr:libraries-update'){location.reload()}else if(d.type==='hmr:update'||d.type==='hmr:cms-update'||d.type==='hmr:colors-update'||d.type==='hmr:variables-update')hotReload()};ws.onclose=function(){clearTimeout(timer);timer=setTimeout(connect,1000)}}function hotReload(){var g=++gen;var sx=window.scrollX,sy=window.scrollY;fetch(location.href,{cache:'no-store'}).then(function(r){return r.text()}).then(function(html){if(g!==gen)return;var p=new DOMParser();var d=p.parseFromString(html,'text/html');var or=document.getElementById('root')
|
|
3641
|
+
const liveReloadScript = injectLiveReload ? `<script>(function(){var ws,timer,gen=0,lastSrvRoot=null;function strip(s){return s?s.replace(/[?&]_r=\\d+/,''):''}function classList(el){return (el.getAttribute('class')||'').split(/\\s+/).filter(Boolean)}function syncEl(cur,srv,old){var cc=classList(cur),sc=classList(srv),oc=old?new Set(classList(old)):new Set();var rt=cc.filter(function(c){return !oc.has(c)});var seen=new Set(),fin=[];sc.concat(rt).forEach(function(c){if(!seen.has(c)){seen.add(c);fin.push(c)}});var fs=fin.join(' ');if((cur.getAttribute('class')||'')!==fs){if(fs)cur.setAttribute('class',fs);else cur.removeAttribute('class')}for(var i=0;i<srv.attributes.length;i++){var a=srv.attributes[i];if(a.name==='class')continue;if(cur.getAttribute(a.name)!==a.value)cur.setAttribute(a.name,a.value)}if(old){for(var i=0;i<old.attributes.length;i++){var a=old.attributes[i];if(a.name==='class')continue;if(!srv.hasAttribute(a.name)&&cur.hasAttribute(a.name))cur.removeAttribute(a.name)}}}function syncText(cur,srv){var cc=cur.childNodes,sc=srv.childNodes;for(var i=0;i<sc.length;i++){var s=sc[i],c=cc[i];if(s.nodeType===3&&c&&c.nodeType===3){if(c.textContent!==s.textContent)c.textContent=s.textContent}}}function smartUpdate(curR,srvR,oldR){var ce=curR.querySelectorAll('[data-element-path]'),se=srvR.querySelectorAll('[data-element-path]');if(ce.length!==se.length){if(curR.innerHTML!==srvR.innerHTML)curR.innerHTML=srvR.innerHTML;return}var sbp={};for(var i=0;i<se.length;i++)sbp[se[i].getAttribute('data-element-path')]=se[i];var obp={};if(oldR){var oe=oldR.querySelectorAll('[data-element-path]');for(var i=0;i<oe.length;i++)obp[oe[i].getAttribute('data-element-path')]=oe[i]}for(var i=0;i<ce.length;i++){var c=ce[i],p=c.getAttribute('data-element-path'),s=sbp[p];if(!s){if(curR.innerHTML!==srvR.innerHTML)curR.innerHTML=srvR.innerHTML;return}syncEl(c,s,obp[p]);syncText(c,s)}syncText(curR,srvR)}function connect(){ws=new WebSocket(${wsUrl});ws.onmessage=function(e){var d=JSON.parse(e.data);if(d.type==='hmr:libraries-update'){location.reload()}else if(d.type==='hmr:update'||d.type==='hmr:cms-update'||d.type==='hmr:colors-update'||d.type==='hmr:variables-update')hotReload()};ws.onclose=function(){clearTimeout(timer);timer=setTimeout(connect,1000)}}function hotReload(){var g=++gen;var sx=window.scrollX,sy=window.scrollY;fetch(location.href,{cache:'no-store'}).then(function(r){return r.text()}).then(function(html){if(g!==gen)return;var p=new DOMParser();var d=p.parseFromString(html,'text/html');var or=document.getElementById('root'),nr=d.getElementById('root');if(or&&nr)smartUpdate(or,nr,lastSrvRoot);if(nr)lastSrvRoot=nr.cloneNode(true);var os=document.getElementById('meno-styles'),ns=d.getElementById('meno-styles');if(os&&ns&&os.textContent!==ns.textContent)os.parentNode.replaceChild(ns.cloneNode(true),os);var nh=d.documentElement;if(nh){var nl=nh.getAttribute('lang')||'en',nt=nh.getAttribute('theme')||'light';if(document.documentElement.getAttribute('lang')!==nl)document.documentElement.setAttribute('lang',nl);if(document.documentElement.getAttribute('theme')!==nt)document.documentElement.setAttribute('theme',nt)}var ocms=document.querySelectorAll('script[id^="meno-cms-"]'),ncms=d.querySelectorAll('script[id^="meno-cms-"]');var ock=JSON.stringify(Array.prototype.map.call(ocms,function(s){return [s.id,s.textContent]}));var nck=JSON.stringify(Array.prototype.map.call(ncms,function(s){return [s.id,s.textContent]}));if(ock!==nck){ocms.forEach(function(s){s.remove()});ncms.forEach(function(s){var c=document.createElement('script');c.type=s.type;c.id=s.id;c.textContent=s.textContent;document.head.appendChild(c)})}window.__menoHotReload=true;var olib=document.querySelectorAll('body > script[src^="/libraries/"]'),nlib=d.querySelectorAll('body > script[src^="/libraries/"]');var olk=JSON.stringify(Array.prototype.map.call(olib,function(s){return strip(s.getAttribute('src'))}).sort());var nlk=JSON.stringify(Array.prototype.map.call(nlib,function(s){return strip(s.getAttribute('src'))}).sort());if(olk!==nlk){olib.forEach(function(o){o.remove()});nlib.forEach(function(n){var src=n.getAttribute('src');var ls=document.createElement('script');ls.src=src+(src.indexOf('?')>-1?'&':'?')+'_r='+Date.now();document.body.appendChild(ls)})}var oscr=document.querySelector('script[src^="/_scripts/"]'),nscr=d.querySelector('script[src^="/_scripts/"]');var oss=oscr?strip(oscr.getAttribute('src')):'',nss=nscr?strip(nscr.getAttribute('src')):'';if(oss===nss){window.scrollTo(sx,sy)}else{if(oscr)oscr.remove();if(nscr){var src=nscr.getAttribute('src');var s=document.createElement('script');s.src=src+(src.indexOf('?')>-1?'&':'?')+'_r='+Date.now();s.onload=function(){document.dispatchEvent(new Event('DOMContentLoaded'));window.scrollTo(sx,sy)};s.onerror=function(){window.scrollTo(sx,sy)};document.body.appendChild(s)}else{document.dispatchEvent(new Event('DOMContentLoaded'));window.scrollTo(sx,sy)}}}).catch(function(){location.reload()})}var iR=document.getElementById('root');if(iR)lastSrvRoot=iR.cloneNode(true);connect()})()</script>` : "";
|
|
3444
3642
|
const scrollHandlerScript = injectLiveReload ? `<script>(function(){window.addEventListener('message',function(e){if(e.data.type==='GET_SCROLL_POSITION'){window.parent.postMessage({type:'SCROLL_POSITION_RESPONSE',scrollX:window.scrollX,scrollY:window.scrollY},'*')}else if(e.data.type==='SET_SCROLL_POSITION'){window.scrollTo(e.data.scrollX,e.data.scrollY)}})})()</script>` : "";
|
|
3445
3643
|
const styleContent = useBundled ? finalCSS : `
|
|
3446
3644
|
${combinedCSS.split("\n").join("\n ")}
|
|
@@ -5622,6 +5820,7 @@ autoInit();
|
|
|
5622
5820
|
// lib/server/providers/fileSystemCMSProvider.ts
|
|
5623
5821
|
import { existsSync as existsSync2, readdirSync as readdirSync2, mkdirSync } from "fs";
|
|
5624
5822
|
import { join as join2 } from "path";
|
|
5823
|
+
var DRAFT_FILE_SUFFIX = `${CMS_DRAFT_SUFFIX}.json`;
|
|
5625
5824
|
async function loadJSONFile2(filePath) {
|
|
5626
5825
|
try {
|
|
5627
5826
|
if (await fileExists(filePath)) {
|
|
@@ -5633,12 +5832,18 @@ async function loadJSONFile2(filePath) {
|
|
|
5633
5832
|
return null;
|
|
5634
5833
|
}
|
|
5635
5834
|
}
|
|
5636
|
-
function normalizeItem(content, filename) {
|
|
5637
|
-
|
|
5835
|
+
function normalizeItem(content, filename, isDraft = false) {
|
|
5836
|
+
const base = {
|
|
5638
5837
|
...content,
|
|
5639
5838
|
_slug: filename,
|
|
5640
5839
|
_filename: filename
|
|
5641
5840
|
};
|
|
5841
|
+
if (isDraft) base._isDraft = true;
|
|
5842
|
+
return base;
|
|
5843
|
+
}
|
|
5844
|
+
function stripTransient(item) {
|
|
5845
|
+
const { _slug, _isDraft, _hasDraft, _url, ...rest } = item;
|
|
5846
|
+
return rest;
|
|
5642
5847
|
}
|
|
5643
5848
|
var FileSystemCMSProvider = class {
|
|
5644
5849
|
constructor(templatesDir, cmsDir) {
|
|
@@ -5656,13 +5861,16 @@ var FileSystemCMSProvider = class {
|
|
|
5656
5861
|
}
|
|
5657
5862
|
}
|
|
5658
5863
|
/**
|
|
5659
|
-
* Validate filename to prevent path traversal attacks
|
|
5864
|
+
* Validate filename to prevent path traversal attacks and reserved-suffix collisions.
|
|
5660
5865
|
* @throws Error if filename is invalid
|
|
5661
5866
|
*/
|
|
5662
5867
|
validateFilename(filename) {
|
|
5663
5868
|
if (!isSafePathSegment(filename)) {
|
|
5664
5869
|
throw new Error(`Invalid filename: "${filename}". Filenames cannot contain path separators or traversal sequences.`);
|
|
5665
5870
|
}
|
|
5871
|
+
if (isReservedDraftFilename(filename)) {
|
|
5872
|
+
throw new Error(`Invalid filename: "${filename}". The "${CMS_DRAFT_SUFFIX}" suffix is reserved for draft files.`);
|
|
5873
|
+
}
|
|
5666
5874
|
}
|
|
5667
5875
|
/**
|
|
5668
5876
|
* Load all CMS schemas from page files with source: 'cms' in templates/
|
|
@@ -5709,7 +5917,7 @@ var FileSystemCMSProvider = class {
|
|
|
5709
5917
|
return [];
|
|
5710
5918
|
}
|
|
5711
5919
|
const files = readdirSync2(collectionDir);
|
|
5712
|
-
const jsonFiles = files.filter((f) => f.endsWith(".json"));
|
|
5920
|
+
const jsonFiles = files.filter((f) => f.endsWith(".json") && !f.endsWith(DRAFT_FILE_SUFFIX));
|
|
5713
5921
|
const results = await Promise.all(
|
|
5714
5922
|
jsonFiles.map(async (file) => {
|
|
5715
5923
|
const filePath = join2(collectionDir, file);
|
|
@@ -5793,22 +6001,170 @@ var FileSystemCMSProvider = class {
|
|
|
5793
6001
|
if (!existsSync2(collectionDir)) {
|
|
5794
6002
|
mkdirSync(collectionDir, { recursive: true });
|
|
5795
6003
|
}
|
|
5796
|
-
const
|
|
6004
|
+
const itemData = stripTransient(item);
|
|
5797
6005
|
const filePath = join2(collectionDir, `${filename}.json`);
|
|
5798
6006
|
await writeFile2(filePath, JSON.stringify(itemData, null, 2), "utf-8");
|
|
5799
6007
|
}
|
|
5800
6008
|
/**
|
|
5801
|
-
* Delete item by filename
|
|
6009
|
+
* Delete item by filename. Removes the published file AND any draft sibling.
|
|
5802
6010
|
*/
|
|
5803
6011
|
async deleteItem(collection, filename) {
|
|
5804
6012
|
this.validateCollection(collection);
|
|
5805
6013
|
this.validateFilename(filename);
|
|
5806
6014
|
const { unlink } = await import("fs/promises");
|
|
5807
|
-
const
|
|
6015
|
+
const publishedPath = join2(this.cmsDir, collection, `${filename}.json`);
|
|
6016
|
+
if (existsSync2(publishedPath)) {
|
|
6017
|
+
await unlink(publishedPath);
|
|
6018
|
+
}
|
|
6019
|
+
const draftPath = this.draftPath(collection, filename);
|
|
6020
|
+
if (existsSync2(draftPath)) {
|
|
6021
|
+
await unlink(draftPath);
|
|
6022
|
+
}
|
|
6023
|
+
}
|
|
6024
|
+
// ---- Draft helpers ----------------------------------------------------
|
|
6025
|
+
draftPath(collection, filename) {
|
|
6026
|
+
return join2(this.cmsDir, collection, `${filename}${DRAFT_FILE_SUFFIX}`);
|
|
6027
|
+
}
|
|
6028
|
+
/**
|
|
6029
|
+
* Get the draft version of an item, or null if no draft file exists.
|
|
6030
|
+
* Drafts skip strict validation — they may be partial / WIP.
|
|
6031
|
+
*/
|
|
6032
|
+
async getDraft(collection, filename) {
|
|
6033
|
+
this.validateCollection(collection);
|
|
6034
|
+
this.validateFilename(filename);
|
|
6035
|
+
const filePath = this.draftPath(collection, filename);
|
|
6036
|
+
const content = await loadJSONFile2(filePath);
|
|
6037
|
+
if (!content || typeof content !== "object") {
|
|
6038
|
+
return null;
|
|
6039
|
+
}
|
|
6040
|
+
return normalizeItem(
|
|
6041
|
+
content,
|
|
6042
|
+
filename,
|
|
6043
|
+
/*isDraft*/
|
|
6044
|
+
true
|
|
6045
|
+
);
|
|
6046
|
+
}
|
|
6047
|
+
/**
|
|
6048
|
+
* List all drafts in a collection. Used by the Studio item list to mark
|
|
6049
|
+
* items that have an outstanding draft sibling (or are draft-only).
|
|
6050
|
+
*/
|
|
6051
|
+
async getAllDrafts(collection) {
|
|
6052
|
+
this.validateCollection(collection);
|
|
6053
|
+
const collectionDir = join2(this.cmsDir, collection);
|
|
6054
|
+
if (!existsSync2(collectionDir)) return [];
|
|
6055
|
+
const files = readdirSync2(collectionDir).filter((f) => f.endsWith(DRAFT_FILE_SUFFIX));
|
|
6056
|
+
const results = await Promise.all(
|
|
6057
|
+
files.map(async (file) => {
|
|
6058
|
+
const filePath = join2(collectionDir, file);
|
|
6059
|
+
const content = await loadJSONFile2(filePath);
|
|
6060
|
+
return { file, content };
|
|
6061
|
+
})
|
|
6062
|
+
);
|
|
6063
|
+
const drafts = [];
|
|
6064
|
+
for (const { file, content } of results) {
|
|
6065
|
+
if (content && typeof content === "object") {
|
|
6066
|
+
const filename = file.slice(0, -DRAFT_FILE_SUFFIX.length);
|
|
6067
|
+
drafts.push(normalizeItem(
|
|
6068
|
+
content,
|
|
6069
|
+
filename,
|
|
6070
|
+
/*isDraft*/
|
|
6071
|
+
true
|
|
6072
|
+
));
|
|
6073
|
+
}
|
|
6074
|
+
}
|
|
6075
|
+
return drafts;
|
|
6076
|
+
}
|
|
6077
|
+
async hasDraft(collection, filename) {
|
|
6078
|
+
this.validateCollection(collection);
|
|
6079
|
+
this.validateFilename(filename);
|
|
6080
|
+
return existsSync2(this.draftPath(collection, filename));
|
|
6081
|
+
}
|
|
6082
|
+
/**
|
|
6083
|
+
* Save the draft version of an item. Loose validation — drafts may have
|
|
6084
|
+
* missing required fields or partial data. Strict validation only runs at
|
|
6085
|
+
* publish time. The item's `_filename` determines the target file.
|
|
6086
|
+
*/
|
|
6087
|
+
async saveDraft(collection, item) {
|
|
6088
|
+
this.validateCollection(collection);
|
|
6089
|
+
const { writeFile: writeFile2 } = await import("fs/promises");
|
|
6090
|
+
const schemas = await this.getAllSchemas();
|
|
6091
|
+
const schemaInfo = schemas.get(collection);
|
|
6092
|
+
if (!schemaInfo) {
|
|
6093
|
+
throw new Error(`Unknown collection: ${collection}`);
|
|
6094
|
+
}
|
|
6095
|
+
let filename;
|
|
6096
|
+
if (item._filename) {
|
|
6097
|
+
filename = item._filename;
|
|
6098
|
+
} else {
|
|
6099
|
+
const slugField = schemaInfo.schema.slugField;
|
|
6100
|
+
const slugValue = item[slugField];
|
|
6101
|
+
filename = typeof slugValue === "string" ? slugValue : String(slugValue);
|
|
6102
|
+
}
|
|
6103
|
+
if (!filename || filename === "[object Object]") {
|
|
6104
|
+
throw new Error("Missing _filename field. Drafts must have _filename set on creation.");
|
|
6105
|
+
}
|
|
6106
|
+
this.validateFilename(filename);
|
|
6107
|
+
const collectionDir = join2(this.cmsDir, collection);
|
|
6108
|
+
if (!existsSync2(collectionDir)) {
|
|
6109
|
+
mkdirSync(collectionDir, { recursive: true });
|
|
6110
|
+
}
|
|
6111
|
+
const itemData = stripTransient(item);
|
|
6112
|
+
const validation = validateCMSDraftItem(itemData);
|
|
6113
|
+
if (!validation.valid) {
|
|
6114
|
+
const messages = validation.errors.map((e) => `${e.path}: ${e.message}`).join(", ");
|
|
6115
|
+
throw new Error(`Invalid draft: ${messages}`);
|
|
6116
|
+
}
|
|
6117
|
+
const filePath = this.draftPath(collection, filename);
|
|
6118
|
+
await writeFile2(filePath, JSON.stringify(itemData, null, 2), "utf-8");
|
|
6119
|
+
}
|
|
6120
|
+
/**
|
|
6121
|
+
* Discard the draft version of an item. No-op if no draft exists.
|
|
6122
|
+
*/
|
|
6123
|
+
async discardDraft(collection, filename) {
|
|
6124
|
+
this.validateCollection(collection);
|
|
6125
|
+
this.validateFilename(filename);
|
|
6126
|
+
const { unlink } = await import("fs/promises");
|
|
6127
|
+
const filePath = this.draftPath(collection, filename);
|
|
5808
6128
|
if (existsSync2(filePath)) {
|
|
5809
6129
|
await unlink(filePath);
|
|
5810
6130
|
}
|
|
5811
6131
|
}
|
|
6132
|
+
/**
|
|
6133
|
+
* Promote a draft to published. Reads `{filename}.draft.json`, writes the
|
|
6134
|
+
* content to `{filename}.json`, then unlinks the draft. The published write
|
|
6135
|
+
* happens first so a crash mid-operation leaves a valid published file plus
|
|
6136
|
+
* an orphan draft (recoverable via the editor's Discard button) — never a
|
|
6137
|
+
* gap with no published content.
|
|
6138
|
+
*
|
|
6139
|
+
* Throws if no draft exists.
|
|
6140
|
+
*/
|
|
6141
|
+
async publishDraft(collection, filename) {
|
|
6142
|
+
this.validateCollection(collection);
|
|
6143
|
+
this.validateFilename(filename);
|
|
6144
|
+
const { writeFile: writeFile2, unlink } = await import("fs/promises");
|
|
6145
|
+
const draftFilePath = this.draftPath(collection, filename);
|
|
6146
|
+
const content = await loadJSONFile2(draftFilePath);
|
|
6147
|
+
if (!content || typeof content !== "object") {
|
|
6148
|
+
throw new Error(`No draft to publish for ${collection}/${filename}`);
|
|
6149
|
+
}
|
|
6150
|
+
const item = normalizeItem(content, filename);
|
|
6151
|
+
const validation = validateCMSItem(item);
|
|
6152
|
+
if (!validation.valid) {
|
|
6153
|
+
const messages = validation.errors.map((e) => `${e.path}: ${e.message}`).join(", ");
|
|
6154
|
+
throw new Error(`Cannot publish invalid draft: ${messages}`);
|
|
6155
|
+
}
|
|
6156
|
+
const collectionDir = join2(this.cmsDir, collection);
|
|
6157
|
+
if (!existsSync2(collectionDir)) {
|
|
6158
|
+
mkdirSync(collectionDir, { recursive: true });
|
|
6159
|
+
}
|
|
6160
|
+
const itemData = stripTransient(validation.data);
|
|
6161
|
+
const publishedPath = join2(collectionDir, `${filename}.json`);
|
|
6162
|
+
await writeFile2(publishedPath, JSON.stringify(itemData, null, 2), "utf-8");
|
|
6163
|
+
if (existsSync2(draftFilePath)) {
|
|
6164
|
+
await unlink(draftFilePath);
|
|
6165
|
+
}
|
|
6166
|
+
return normalizeItem(itemData, filename);
|
|
6167
|
+
}
|
|
5812
6168
|
/**
|
|
5813
6169
|
* Clear schema cache (useful when pages are modified)
|
|
5814
6170
|
*/
|
|
@@ -5932,4 +6288,4 @@ export {
|
|
|
5932
6288
|
FileSystemCMSProvider,
|
|
5933
6289
|
migrateTemplatesDirectory
|
|
5934
6290
|
};
|
|
5935
|
-
//# sourceMappingURL=chunk-
|
|
6291
|
+
//# sourceMappingURL=chunk-EQYDSPBB.js.map
|