@momentumcms/core 0.5.3 → 0.5.4
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/package.json +1 -1
- package/src/generators/generator.cjs +102 -21
- package/src/generators/generator.d.ts +3 -2
- package/src/generators/generator.js +102 -21
- package/src/index.cjs +12 -1
- package/src/index.js +11 -1
- package/src/lib/collections/collection.types.d.ts +35 -0
- package/src/lib/config.d.ts +45 -1
- package/src/lib/plugins.d.ts +6 -1
- package/src/lib/versions/version.types.d.ts +10 -0
package/package.json
CHANGED
|
@@ -465,6 +465,35 @@ ${indent}}`;
|
|
|
465
465
|
}
|
|
466
466
|
return "undefined";
|
|
467
467
|
}
|
|
468
|
+
function serializeComponentLoaders(components, configPath, outputPath, indent) {
|
|
469
|
+
const entries = [];
|
|
470
|
+
for (const [key, value] of Object.entries(components)) {
|
|
471
|
+
if (typeof value !== "function")
|
|
472
|
+
continue;
|
|
473
|
+
const source = value.toString();
|
|
474
|
+
const importMatch = /(?:import|__vite_ssr_dynamic_import__)\(\s*['"]([^'"]+)['"]\s*\)/.exec(
|
|
475
|
+
source
|
|
476
|
+
);
|
|
477
|
+
if (!importMatch)
|
|
478
|
+
continue;
|
|
479
|
+
const importPath = importMatch[1];
|
|
480
|
+
const abs = (0, import_node_path.resolve)((0, import_node_path.dirname)(configPath), importPath);
|
|
481
|
+
const rel = computeRelativeImport(outputPath, abs + ".ts");
|
|
482
|
+
const memberMatch = /\.then\(\s*\(?\s*(\w+)\s*\)?\s*=>\s*\1(?:\.(\w+)|\[["'](\w+)["']\])\s*\)/.exec(source);
|
|
483
|
+
if (!memberMatch)
|
|
484
|
+
continue;
|
|
485
|
+
const memberName = memberMatch[2] || memberMatch[3];
|
|
486
|
+
const safeKey = needsQuoting2(key) ? safeQuote(key) : key;
|
|
487
|
+
entries.push(
|
|
488
|
+
`${indent} ${safeKey}: () => import(${JSON.stringify(rel)}).then((m) => m.${memberName})`
|
|
489
|
+
);
|
|
490
|
+
}
|
|
491
|
+
if (entries.length === 0)
|
|
492
|
+
return null;
|
|
493
|
+
return `{
|
|
494
|
+
${entries.join(",\n")},
|
|
495
|
+
${indent}}`;
|
|
496
|
+
}
|
|
468
497
|
function serializeField(field, indent = " ") {
|
|
469
498
|
const props = [];
|
|
470
499
|
props.push(`${indent}name: ${JSON.stringify(field.name)}`);
|
|
@@ -614,7 +643,7 @@ ${indent} }`;
|
|
|
614
643
|
${items},
|
|
615
644
|
${indent}]`;
|
|
616
645
|
}
|
|
617
|
-
function serializeCollection(collection, indent = " ") {
|
|
646
|
+
function serializeCollection(collection, indent = " ", configPath, outputPath) {
|
|
618
647
|
const parts = [];
|
|
619
648
|
parts.push(`${indent} slug: ${JSON.stringify(collection.slug)}`);
|
|
620
649
|
if (collection.labels) {
|
|
@@ -622,16 +651,37 @@ function serializeCollection(collection, indent = " ") {
|
|
|
622
651
|
}
|
|
623
652
|
parts.push(`${indent} fields: ${serializeFieldsArray(collection.fields, indent + " ")}`);
|
|
624
653
|
if (collection.admin) {
|
|
625
|
-
const
|
|
654
|
+
const componentsObj = collection.admin["components"];
|
|
655
|
+
const adminEntries = Object.entries(collection.admin).filter(([k, v]) => v !== void 0 && k !== "components").map(([k, v]) => {
|
|
626
656
|
if (k === "preview" && typeof v === "function") {
|
|
627
657
|
const fn = v;
|
|
628
658
|
return [k, previewFunctionToTemplate(fn, collection.fields)];
|
|
629
659
|
}
|
|
630
660
|
return [k, v];
|
|
631
661
|
}).filter(([, v]) => typeof v !== "function");
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
662
|
+
let componentsStr = null;
|
|
663
|
+
if (componentsObj && typeof componentsObj === "object" && configPath && outputPath) {
|
|
664
|
+
const loaders = {};
|
|
665
|
+
for (const [k, v] of Object.entries(componentsObj)) {
|
|
666
|
+
loaders[k] = v;
|
|
667
|
+
}
|
|
668
|
+
componentsStr = serializeComponentLoaders(loaders, configPath, outputPath, indent + " ");
|
|
669
|
+
}
|
|
670
|
+
if (adminEntries.length > 0 || componentsStr) {
|
|
671
|
+
const adminProps = [];
|
|
672
|
+
if (adminEntries.length > 0) {
|
|
673
|
+
const adminObj = Object.fromEntries(adminEntries);
|
|
674
|
+
for (const [k, v] of Object.entries(adminObj)) {
|
|
675
|
+
const key = needsQuoting2(k) ? safeQuote(k) : k;
|
|
676
|
+
adminProps.push(`${indent} ${key}: ${serializeValue(v, indent + " ")}`);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
if (componentsStr) {
|
|
680
|
+
adminProps.push(`${indent} components: ${componentsStr}`);
|
|
681
|
+
}
|
|
682
|
+
parts.push(`${indent} admin: {
|
|
683
|
+
${adminProps.join(",\n")},
|
|
684
|
+
${indent} }`);
|
|
635
685
|
}
|
|
636
686
|
}
|
|
637
687
|
if (collection.auth) {
|
|
@@ -691,14 +741,24 @@ function computeRelativeImport(fromFile, toFile) {
|
|
|
691
741
|
}
|
|
692
742
|
return rel;
|
|
693
743
|
}
|
|
694
|
-
function generateAdminConfig(config, typesRelPath) {
|
|
744
|
+
function generateAdminConfig(config, typesRelPath, configPath, outputPath) {
|
|
695
745
|
const lines = [];
|
|
696
746
|
const allCollections = resolveAllCollections(config);
|
|
697
747
|
const globals = config.globals ?? [];
|
|
698
748
|
const plugins = config.plugins ?? [];
|
|
699
|
-
const
|
|
700
|
-
|
|
701
|
-
|
|
749
|
+
const SAFE_IDENTIFIER = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
|
|
750
|
+
const pluginsWithAdminRoutes = plugins.filter((p) => {
|
|
751
|
+
if (!p.browserImports?.adminRoutes || !p.adminRoutes || p.adminRoutes.length === 0)
|
|
752
|
+
return false;
|
|
753
|
+
const exportName = p.browserImports.adminRoutes.exportName;
|
|
754
|
+
if (!SAFE_IDENTIFIER.test(exportName)) {
|
|
755
|
+
console.warn(
|
|
756
|
+
`[generateAdminConfig] Skipping plugin "${p.name}": exportName "${exportName}" is not a valid identifier`
|
|
757
|
+
);
|
|
758
|
+
return false;
|
|
759
|
+
}
|
|
760
|
+
return true;
|
|
761
|
+
});
|
|
702
762
|
lines.push("/**");
|
|
703
763
|
lines.push(" * AUTO-GENERATED by @momentumcms/core");
|
|
704
764
|
lines.push(" * DO NOT EDIT - regenerate with: nx run <app>:generate");
|
|
@@ -714,13 +774,13 @@ function generateAdminConfig(config, typesRelPath) {
|
|
|
714
774
|
const imp = plugin.browserImports?.adminRoutes;
|
|
715
775
|
if (!imp)
|
|
716
776
|
continue;
|
|
717
|
-
lines.push(`import { ${imp.exportName} } from
|
|
777
|
+
lines.push(`import { ${imp.exportName} } from ${JSON.stringify(imp.path)};`);
|
|
718
778
|
}
|
|
719
779
|
lines.push("");
|
|
720
780
|
const genericParams = globals.length > 0 ? "<CollectionSlug, GlobalSlug>" : "<CollectionSlug>";
|
|
721
781
|
lines.push(`export const adminConfig: MomentumAdminConfig${genericParams} = {`);
|
|
722
782
|
if (allCollections.length > 0) {
|
|
723
|
-
const collectionItems = allCollections.map((c) => ` ${serializeCollection(c, " ")}`).join(",\n");
|
|
783
|
+
const collectionItems = allCollections.map((c) => ` ${serializeCollection(c, " ", configPath, outputPath)}`).join(",\n");
|
|
724
784
|
lines.push(` collections: [
|
|
725
785
|
${collectionItems},
|
|
726
786
|
],`);
|
|
@@ -734,15 +794,31 @@ ${globalItems},
|
|
|
734
794
|
],`);
|
|
735
795
|
}
|
|
736
796
|
if (config.admin) {
|
|
737
|
-
const
|
|
738
|
-
if (config.admin.basePath)
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
if (
|
|
745
|
-
|
|
797
|
+
const adminParts = [];
|
|
798
|
+
if (config.admin.basePath) {
|
|
799
|
+
adminParts.push(` basePath: ${JSON.stringify(config.admin.basePath)}`);
|
|
800
|
+
}
|
|
801
|
+
if (config.admin.branding) {
|
|
802
|
+
adminParts.push(` branding: ${serializeValue(config.admin.branding, " ")}`);
|
|
803
|
+
}
|
|
804
|
+
if (config.admin.toasts !== void 0) {
|
|
805
|
+
adminParts.push(` toasts: ${String(config.admin.toasts)}`);
|
|
806
|
+
}
|
|
807
|
+
if (config.admin.components && configPath && outputPath) {
|
|
808
|
+
const componentsStr = serializeComponentLoaders(
|
|
809
|
+
config.admin.components,
|
|
810
|
+
configPath,
|
|
811
|
+
outputPath,
|
|
812
|
+
" "
|
|
813
|
+
);
|
|
814
|
+
if (componentsStr) {
|
|
815
|
+
adminParts.push(` components: ${componentsStr}`);
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
if (adminParts.length > 0) {
|
|
819
|
+
lines.push(` admin: {
|
|
820
|
+
${adminParts.join(",\n")},
|
|
821
|
+
},`);
|
|
746
822
|
}
|
|
747
823
|
}
|
|
748
824
|
if (pluginsWithAdminRoutes.length > 0) {
|
|
@@ -812,7 +888,12 @@ async function runGenerator(options) {
|
|
|
812
888
|
(0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(typesOutputPath), { recursive: true });
|
|
813
889
|
(0, import_node_fs.writeFileSync)(typesOutputPath, typesContent, "utf-8");
|
|
814
890
|
console.info(`Types generated: ${typesOutputPath}`);
|
|
815
|
-
const adminConfigContent = generateAdminConfig(
|
|
891
|
+
const adminConfigContent = generateAdminConfig(
|
|
892
|
+
config,
|
|
893
|
+
typesRelPath,
|
|
894
|
+
configPath,
|
|
895
|
+
configOutputPath
|
|
896
|
+
);
|
|
816
897
|
(0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(configOutputPath), { recursive: true });
|
|
817
898
|
(0, import_node_fs.writeFileSync)(configOutputPath, adminConfigContent, "utf-8");
|
|
818
899
|
console.info(`Admin config generated: ${configOutputPath}`);
|
|
@@ -149,6 +149,7 @@ interface MomentumConfig {
|
|
|
149
149
|
title?: string;
|
|
150
150
|
};
|
|
151
151
|
toasts?: boolean;
|
|
152
|
+
components?: Record<string, unknown>;
|
|
152
153
|
};
|
|
153
154
|
plugins?: PluginDescriptor[];
|
|
154
155
|
}
|
|
@@ -175,7 +176,7 @@ export declare function serializeField(field: FieldDefinition, indent?: string):
|
|
|
175
176
|
/**
|
|
176
177
|
* Serialize a collection definition, stripping server-only properties.
|
|
177
178
|
*/
|
|
178
|
-
export declare function serializeCollection(collection: CollectionDefinition, indent?: string): string;
|
|
179
|
+
export declare function serializeCollection(collection: CollectionDefinition, indent?: string, configPath?: string, outputPath?: string): string;
|
|
179
180
|
/**
|
|
180
181
|
* Serialize a global definition, stripping server-only properties.
|
|
181
182
|
*/
|
|
@@ -189,7 +190,7 @@ export declare function computeRelativeImport(fromFile: string, toFile: string):
|
|
|
189
190
|
* Collections and globals are inlined with server-only properties stripped.
|
|
190
191
|
* Only plugin admin routes are still imported (they have loadComponent functions).
|
|
191
192
|
*/
|
|
192
|
-
export declare function generateAdminConfig(config: MomentumConfig, typesRelPath: string): string;
|
|
193
|
+
export declare function generateAdminConfig(config: MomentumConfig, typesRelPath: string, configPath?: string, outputPath?: string): string;
|
|
193
194
|
export default function runGenerator(options: GeneratorOptions): Promise<{
|
|
194
195
|
success: boolean;
|
|
195
196
|
}>;
|
|
@@ -434,6 +434,35 @@ ${indent}}`;
|
|
|
434
434
|
}
|
|
435
435
|
return "undefined";
|
|
436
436
|
}
|
|
437
|
+
function serializeComponentLoaders(components, configPath, outputPath, indent) {
|
|
438
|
+
const entries = [];
|
|
439
|
+
for (const [key, value] of Object.entries(components)) {
|
|
440
|
+
if (typeof value !== "function")
|
|
441
|
+
continue;
|
|
442
|
+
const source = value.toString();
|
|
443
|
+
const importMatch = /(?:import|__vite_ssr_dynamic_import__)\(\s*['"]([^'"]+)['"]\s*\)/.exec(
|
|
444
|
+
source
|
|
445
|
+
);
|
|
446
|
+
if (!importMatch)
|
|
447
|
+
continue;
|
|
448
|
+
const importPath = importMatch[1];
|
|
449
|
+
const abs = resolve(dirname(configPath), importPath);
|
|
450
|
+
const rel = computeRelativeImport(outputPath, abs + ".ts");
|
|
451
|
+
const memberMatch = /\.then\(\s*\(?\s*(\w+)\s*\)?\s*=>\s*\1(?:\.(\w+)|\[["'](\w+)["']\])\s*\)/.exec(source);
|
|
452
|
+
if (!memberMatch)
|
|
453
|
+
continue;
|
|
454
|
+
const memberName = memberMatch[2] || memberMatch[3];
|
|
455
|
+
const safeKey = needsQuoting2(key) ? safeQuote(key) : key;
|
|
456
|
+
entries.push(
|
|
457
|
+
`${indent} ${safeKey}: () => import(${JSON.stringify(rel)}).then((m) => m.${memberName})`
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
if (entries.length === 0)
|
|
461
|
+
return null;
|
|
462
|
+
return `{
|
|
463
|
+
${entries.join(",\n")},
|
|
464
|
+
${indent}}`;
|
|
465
|
+
}
|
|
437
466
|
function serializeField(field, indent = " ") {
|
|
438
467
|
const props = [];
|
|
439
468
|
props.push(`${indent}name: ${JSON.stringify(field.name)}`);
|
|
@@ -583,7 +612,7 @@ ${indent} }`;
|
|
|
583
612
|
${items},
|
|
584
613
|
${indent}]`;
|
|
585
614
|
}
|
|
586
|
-
function serializeCollection(collection, indent = " ") {
|
|
615
|
+
function serializeCollection(collection, indent = " ", configPath, outputPath) {
|
|
587
616
|
const parts = [];
|
|
588
617
|
parts.push(`${indent} slug: ${JSON.stringify(collection.slug)}`);
|
|
589
618
|
if (collection.labels) {
|
|
@@ -591,16 +620,37 @@ function serializeCollection(collection, indent = " ") {
|
|
|
591
620
|
}
|
|
592
621
|
parts.push(`${indent} fields: ${serializeFieldsArray(collection.fields, indent + " ")}`);
|
|
593
622
|
if (collection.admin) {
|
|
594
|
-
const
|
|
623
|
+
const componentsObj = collection.admin["components"];
|
|
624
|
+
const adminEntries = Object.entries(collection.admin).filter(([k, v]) => v !== void 0 && k !== "components").map(([k, v]) => {
|
|
595
625
|
if (k === "preview" && typeof v === "function") {
|
|
596
626
|
const fn = v;
|
|
597
627
|
return [k, previewFunctionToTemplate(fn, collection.fields)];
|
|
598
628
|
}
|
|
599
629
|
return [k, v];
|
|
600
630
|
}).filter(([, v]) => typeof v !== "function");
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
631
|
+
let componentsStr = null;
|
|
632
|
+
if (componentsObj && typeof componentsObj === "object" && configPath && outputPath) {
|
|
633
|
+
const loaders = {};
|
|
634
|
+
for (const [k, v] of Object.entries(componentsObj)) {
|
|
635
|
+
loaders[k] = v;
|
|
636
|
+
}
|
|
637
|
+
componentsStr = serializeComponentLoaders(loaders, configPath, outputPath, indent + " ");
|
|
638
|
+
}
|
|
639
|
+
if (adminEntries.length > 0 || componentsStr) {
|
|
640
|
+
const adminProps = [];
|
|
641
|
+
if (adminEntries.length > 0) {
|
|
642
|
+
const adminObj = Object.fromEntries(adminEntries);
|
|
643
|
+
for (const [k, v] of Object.entries(adminObj)) {
|
|
644
|
+
const key = needsQuoting2(k) ? safeQuote(k) : k;
|
|
645
|
+
adminProps.push(`${indent} ${key}: ${serializeValue(v, indent + " ")}`);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
if (componentsStr) {
|
|
649
|
+
adminProps.push(`${indent} components: ${componentsStr}`);
|
|
650
|
+
}
|
|
651
|
+
parts.push(`${indent} admin: {
|
|
652
|
+
${adminProps.join(",\n")},
|
|
653
|
+
${indent} }`);
|
|
604
654
|
}
|
|
605
655
|
}
|
|
606
656
|
if (collection.auth) {
|
|
@@ -660,14 +710,24 @@ function computeRelativeImport(fromFile, toFile) {
|
|
|
660
710
|
}
|
|
661
711
|
return rel;
|
|
662
712
|
}
|
|
663
|
-
function generateAdminConfig(config, typesRelPath) {
|
|
713
|
+
function generateAdminConfig(config, typesRelPath, configPath, outputPath) {
|
|
664
714
|
const lines = [];
|
|
665
715
|
const allCollections = resolveAllCollections(config);
|
|
666
716
|
const globals = config.globals ?? [];
|
|
667
717
|
const plugins = config.plugins ?? [];
|
|
668
|
-
const
|
|
669
|
-
|
|
670
|
-
|
|
718
|
+
const SAFE_IDENTIFIER = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
|
|
719
|
+
const pluginsWithAdminRoutes = plugins.filter((p) => {
|
|
720
|
+
if (!p.browserImports?.adminRoutes || !p.adminRoutes || p.adminRoutes.length === 0)
|
|
721
|
+
return false;
|
|
722
|
+
const exportName = p.browserImports.adminRoutes.exportName;
|
|
723
|
+
if (!SAFE_IDENTIFIER.test(exportName)) {
|
|
724
|
+
console.warn(
|
|
725
|
+
`[generateAdminConfig] Skipping plugin "${p.name}": exportName "${exportName}" is not a valid identifier`
|
|
726
|
+
);
|
|
727
|
+
return false;
|
|
728
|
+
}
|
|
729
|
+
return true;
|
|
730
|
+
});
|
|
671
731
|
lines.push("/**");
|
|
672
732
|
lines.push(" * AUTO-GENERATED by @momentumcms/core");
|
|
673
733
|
lines.push(" * DO NOT EDIT - regenerate with: nx run <app>:generate");
|
|
@@ -683,13 +743,13 @@ function generateAdminConfig(config, typesRelPath) {
|
|
|
683
743
|
const imp = plugin.browserImports?.adminRoutes;
|
|
684
744
|
if (!imp)
|
|
685
745
|
continue;
|
|
686
|
-
lines.push(`import { ${imp.exportName} } from
|
|
746
|
+
lines.push(`import { ${imp.exportName} } from ${JSON.stringify(imp.path)};`);
|
|
687
747
|
}
|
|
688
748
|
lines.push("");
|
|
689
749
|
const genericParams = globals.length > 0 ? "<CollectionSlug, GlobalSlug>" : "<CollectionSlug>";
|
|
690
750
|
lines.push(`export const adminConfig: MomentumAdminConfig${genericParams} = {`);
|
|
691
751
|
if (allCollections.length > 0) {
|
|
692
|
-
const collectionItems = allCollections.map((c) => ` ${serializeCollection(c, " ")}`).join(",\n");
|
|
752
|
+
const collectionItems = allCollections.map((c) => ` ${serializeCollection(c, " ", configPath, outputPath)}`).join(",\n");
|
|
693
753
|
lines.push(` collections: [
|
|
694
754
|
${collectionItems},
|
|
695
755
|
],`);
|
|
@@ -703,15 +763,31 @@ ${globalItems},
|
|
|
703
763
|
],`);
|
|
704
764
|
}
|
|
705
765
|
if (config.admin) {
|
|
706
|
-
const
|
|
707
|
-
if (config.admin.basePath)
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
if (
|
|
714
|
-
|
|
766
|
+
const adminParts = [];
|
|
767
|
+
if (config.admin.basePath) {
|
|
768
|
+
adminParts.push(` basePath: ${JSON.stringify(config.admin.basePath)}`);
|
|
769
|
+
}
|
|
770
|
+
if (config.admin.branding) {
|
|
771
|
+
adminParts.push(` branding: ${serializeValue(config.admin.branding, " ")}`);
|
|
772
|
+
}
|
|
773
|
+
if (config.admin.toasts !== void 0) {
|
|
774
|
+
adminParts.push(` toasts: ${String(config.admin.toasts)}`);
|
|
775
|
+
}
|
|
776
|
+
if (config.admin.components && configPath && outputPath) {
|
|
777
|
+
const componentsStr = serializeComponentLoaders(
|
|
778
|
+
config.admin.components,
|
|
779
|
+
configPath,
|
|
780
|
+
outputPath,
|
|
781
|
+
" "
|
|
782
|
+
);
|
|
783
|
+
if (componentsStr) {
|
|
784
|
+
adminParts.push(` components: ${componentsStr}`);
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
if (adminParts.length > 0) {
|
|
788
|
+
lines.push(` admin: {
|
|
789
|
+
${adminParts.join(",\n")},
|
|
790
|
+
},`);
|
|
715
791
|
}
|
|
716
792
|
}
|
|
717
793
|
if (pluginsWithAdminRoutes.length > 0) {
|
|
@@ -781,7 +857,12 @@ async function runGenerator(options) {
|
|
|
781
857
|
mkdirSync(dirname(typesOutputPath), { recursive: true });
|
|
782
858
|
writeFileSync(typesOutputPath, typesContent, "utf-8");
|
|
783
859
|
console.info(`Types generated: ${typesOutputPath}`);
|
|
784
|
-
const adminConfigContent = generateAdminConfig(
|
|
860
|
+
const adminConfigContent = generateAdminConfig(
|
|
861
|
+
config,
|
|
862
|
+
typesRelPath,
|
|
863
|
+
configPath,
|
|
864
|
+
configOutputPath
|
|
865
|
+
);
|
|
785
866
|
mkdirSync(dirname(configOutputPath), { recursive: true });
|
|
786
867
|
writeFileSync(configOutputPath, adminConfigContent, "utf-8");
|
|
787
868
|
console.info(`Admin config generated: ${configOutputPath}`);
|
package/src/index.cjs
CHANGED
|
@@ -51,6 +51,7 @@ __export(src_exports, {
|
|
|
51
51
|
hasAllRoles: () => hasAllRoles,
|
|
52
52
|
hasAnyRole: () => hasAnyRole,
|
|
53
53
|
hasRole: () => hasRole,
|
|
54
|
+
hasVersionDrafts: () => hasVersionDrafts,
|
|
54
55
|
humanizeFieldName: () => humanizeFieldName,
|
|
55
56
|
isAuthenticated: () => isAuthenticated,
|
|
56
57
|
isLayoutField: () => isLayoutField,
|
|
@@ -648,7 +649,8 @@ function defineMomentumConfig(config) {
|
|
|
648
649
|
admin: {
|
|
649
650
|
basePath: config.admin?.basePath ?? "/admin",
|
|
650
651
|
branding: config.admin?.branding ?? {},
|
|
651
|
-
toasts: config.admin?.toasts ?? true
|
|
652
|
+
toasts: config.admin?.toasts ?? true,
|
|
653
|
+
components: config.admin?.components
|
|
652
654
|
},
|
|
653
655
|
server: {
|
|
654
656
|
port: config.server?.port ?? 3e3,
|
|
@@ -769,6 +771,14 @@ function createSeedHelpers() {
|
|
|
769
771
|
}
|
|
770
772
|
};
|
|
771
773
|
}
|
|
774
|
+
|
|
775
|
+
// libs/core/src/lib/versions/version.types.ts
|
|
776
|
+
function hasVersionDrafts(collection) {
|
|
777
|
+
const v = collection.versions;
|
|
778
|
+
if (!v || typeof v === "boolean")
|
|
779
|
+
return false;
|
|
780
|
+
return !!v.drafts;
|
|
781
|
+
}
|
|
772
782
|
// Annotate the CommonJS export names for ESM import in node:
|
|
773
783
|
0 && (module.exports = {
|
|
774
784
|
LAYOUT_FIELD_TYPES,
|
|
@@ -802,6 +812,7 @@ function createSeedHelpers() {
|
|
|
802
812
|
hasAllRoles,
|
|
803
813
|
hasAnyRole,
|
|
804
814
|
hasRole,
|
|
815
|
+
hasVersionDrafts,
|
|
805
816
|
humanizeFieldName,
|
|
806
817
|
isAuthenticated,
|
|
807
818
|
isLayoutField,
|
package/src/index.js
CHANGED
|
@@ -567,7 +567,8 @@ function defineMomentumConfig(config) {
|
|
|
567
567
|
admin: {
|
|
568
568
|
basePath: config.admin?.basePath ?? "/admin",
|
|
569
569
|
branding: config.admin?.branding ?? {},
|
|
570
|
-
toasts: config.admin?.toasts ?? true
|
|
570
|
+
toasts: config.admin?.toasts ?? true,
|
|
571
|
+
components: config.admin?.components
|
|
571
572
|
},
|
|
572
573
|
server: {
|
|
573
574
|
port: config.server?.port ?? 3e3,
|
|
@@ -688,6 +689,14 @@ function createSeedHelpers() {
|
|
|
688
689
|
}
|
|
689
690
|
};
|
|
690
691
|
}
|
|
692
|
+
|
|
693
|
+
// libs/core/src/lib/versions/version.types.ts
|
|
694
|
+
function hasVersionDrafts(collection) {
|
|
695
|
+
const v = collection.versions;
|
|
696
|
+
if (!v || typeof v === "boolean")
|
|
697
|
+
return false;
|
|
698
|
+
return !!v.drafts;
|
|
699
|
+
}
|
|
691
700
|
export {
|
|
692
701
|
LAYOUT_FIELD_TYPES,
|
|
693
702
|
MIN_PASSWORD_LENGTH,
|
|
@@ -720,6 +729,7 @@ export {
|
|
|
720
729
|
hasAllRoles,
|
|
721
730
|
hasAnyRole,
|
|
722
731
|
hasRole,
|
|
732
|
+
hasVersionDrafts,
|
|
723
733
|
humanizeFieldName,
|
|
724
734
|
isAuthenticated,
|
|
725
735
|
isLayoutField,
|
|
@@ -37,6 +37,8 @@ export interface AccessConfig {
|
|
|
37
37
|
publishVersions?: AccessFunction;
|
|
38
38
|
/** Control who can restore previous versions */
|
|
39
39
|
restoreVersions?: AccessFunction;
|
|
40
|
+
/** Control who can see draft (unpublished) documents. Falls back to `update` access if not set. */
|
|
41
|
+
readDrafts?: AccessFunction;
|
|
40
42
|
}
|
|
41
43
|
export interface HookArgs {
|
|
42
44
|
req: RequestContext;
|
|
@@ -84,6 +86,39 @@ export interface AdminConfig {
|
|
|
84
86
|
/** HTTP endpoint path for the action (e.g., '/api/auth/api-keys') */
|
|
85
87
|
endpoint?: string;
|
|
86
88
|
}>;
|
|
89
|
+
/**
|
|
90
|
+
* Custom components for this collection's admin pages.
|
|
91
|
+
* Register page replacements and per-collection layout slots.
|
|
92
|
+
*/
|
|
93
|
+
components?: CollectionAdminComponentsConfig;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Per-collection admin component overrides and layout slots.
|
|
97
|
+
* Used in `AdminConfig.components` for collection-specific customization.
|
|
98
|
+
*
|
|
99
|
+
* Loaders use `() => Promise<unknown>` to stay framework-agnostic.
|
|
100
|
+
*/
|
|
101
|
+
export interface CollectionAdminComponentsConfig {
|
|
102
|
+
/** Replace the list page for this collection */
|
|
103
|
+
list?: () => Promise<unknown>;
|
|
104
|
+
/** Replace the edit page for this collection */
|
|
105
|
+
edit?: () => Promise<unknown>;
|
|
106
|
+
/** Replace the view page for this collection */
|
|
107
|
+
view?: () => Promise<unknown>;
|
|
108
|
+
/** Slot: before the collection list */
|
|
109
|
+
beforeList?: () => Promise<unknown>;
|
|
110
|
+
/** Slot: after the collection list */
|
|
111
|
+
afterList?: () => Promise<unknown>;
|
|
112
|
+
/** Slot: before the edit form */
|
|
113
|
+
beforeEdit?: () => Promise<unknown>;
|
|
114
|
+
/** Slot: after the edit form */
|
|
115
|
+
afterEdit?: () => Promise<unknown>;
|
|
116
|
+
/** Slot: sidebar panel on edit page */
|
|
117
|
+
editSidebar?: () => Promise<unknown>;
|
|
118
|
+
/** Slot: before the view page */
|
|
119
|
+
beforeView?: () => Promise<unknown>;
|
|
120
|
+
/** Slot: after the view page */
|
|
121
|
+
afterView?: () => Promise<unknown>;
|
|
87
122
|
}
|
|
88
123
|
export interface VersionsConfig {
|
|
89
124
|
/** Enable draft versions */
|
package/src/lib/config.d.ts
CHANGED
|
@@ -194,6 +194,39 @@ export interface DatabaseConfig {
|
|
|
194
194
|
*/
|
|
195
195
|
adapter: DatabaseAdapter;
|
|
196
196
|
}
|
|
197
|
+
/**
|
|
198
|
+
* Admin component overrides and layout slot registrations.
|
|
199
|
+
* Used in `AdminPanelConfig.components` for global overrides
|
|
200
|
+
* and in `MomentumPlugin.adminComponents` for plugin-level overrides.
|
|
201
|
+
*
|
|
202
|
+
* Loaders use `() => Promise<unknown>` so this interface stays
|
|
203
|
+
* framework-agnostic (core is `env:universal`). The admin package
|
|
204
|
+
* casts to `Type<unknown>` at registration time.
|
|
205
|
+
*/
|
|
206
|
+
export interface AdminComponentsConfig {
|
|
207
|
+
/** Replace the dashboard page */
|
|
208
|
+
dashboard?: () => Promise<unknown>;
|
|
209
|
+
/** Replace the login page */
|
|
210
|
+
login?: () => Promise<unknown>;
|
|
211
|
+
/** Replace the media library page */
|
|
212
|
+
media?: () => Promise<unknown>;
|
|
213
|
+
/** Slot: before navigation links in sidebar */
|
|
214
|
+
beforeNavigation?: () => Promise<unknown>;
|
|
215
|
+
/** Slot: after navigation links in sidebar */
|
|
216
|
+
afterNavigation?: () => Promise<unknown>;
|
|
217
|
+
/** Slot: global header (between mobile header and main content) */
|
|
218
|
+
header?: () => Promise<unknown>;
|
|
219
|
+
/** Slot: global footer (after main content) */
|
|
220
|
+
footer?: () => Promise<unknown>;
|
|
221
|
+
/** Slot: before dashboard content */
|
|
222
|
+
beforeDashboard?: () => Promise<unknown>;
|
|
223
|
+
/** Slot: after dashboard content */
|
|
224
|
+
afterDashboard?: () => Promise<unknown>;
|
|
225
|
+
/** Slot: before login form */
|
|
226
|
+
beforeLogin?: () => Promise<unknown>;
|
|
227
|
+
/** Slot: after login form */
|
|
228
|
+
afterLogin?: () => Promise<unknown>;
|
|
229
|
+
}
|
|
197
230
|
/**
|
|
198
231
|
* Global admin panel configuration.
|
|
199
232
|
* (Distinct from collection-level AdminConfig)
|
|
@@ -219,6 +252,12 @@ export interface AdminPanelConfig {
|
|
|
219
252
|
* @default true
|
|
220
253
|
*/
|
|
221
254
|
toasts?: boolean;
|
|
255
|
+
/**
|
|
256
|
+
* Custom admin component overrides and layout slots.
|
|
257
|
+
* Register page replacements (dashboard, login, media) and
|
|
258
|
+
* slot components (header, footer, beforeDashboard, etc.).
|
|
259
|
+
*/
|
|
260
|
+
components?: AdminComponentsConfig;
|
|
222
261
|
}
|
|
223
262
|
/**
|
|
224
263
|
* Server configuration.
|
|
@@ -378,11 +417,16 @@ export type ResolvedSeedingOptions = Required<SeedingOptions>;
|
|
|
378
417
|
export interface ResolvedSeedingConfig extends SeedingConfig {
|
|
379
418
|
options: ResolvedSeedingOptions;
|
|
380
419
|
}
|
|
420
|
+
/**
|
|
421
|
+
* AdminPanelConfig with primitive defaults resolved (basePath, branding, toasts).
|
|
422
|
+
* `components` remains optional since it has no default.
|
|
423
|
+
*/
|
|
424
|
+
export type ResolvedAdminPanelConfig = Required<Omit<AdminPanelConfig, 'components'>> & Pick<AdminPanelConfig, 'components'>;
|
|
381
425
|
/**
|
|
382
426
|
* Internal config with resolved defaults.
|
|
383
427
|
*/
|
|
384
428
|
export interface ResolvedMomentumConfig extends MomentumConfig {
|
|
385
|
-
admin:
|
|
429
|
+
admin: ResolvedAdminPanelConfig;
|
|
386
430
|
server: Required<ServerConfig>;
|
|
387
431
|
seeding?: ResolvedSeedingConfig;
|
|
388
432
|
logging: ResolvedLoggingConfig;
|
package/src/lib/plugins.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* Runtime implementations (PluginRunner, etc.) live in @momentumcms/plugins/core.
|
|
7
7
|
*/
|
|
8
8
|
import type { CollectionConfig, UserContext } from './collections';
|
|
9
|
-
import type { MomentumConfig } from './config';
|
|
9
|
+
import type { AdminComponentsConfig, MomentumConfig } from './config';
|
|
10
10
|
/**
|
|
11
11
|
* Descriptor for Express middleware/routes that a plugin wants auto-mounted.
|
|
12
12
|
* Plugins register these during onInit via context.registerMiddleware().
|
|
@@ -185,6 +185,11 @@ export interface MomentumPlugin {
|
|
|
185
185
|
onReady?(context: PluginReadyContext): void | Promise<void>;
|
|
186
186
|
/** Called on graceful shutdown. Clean up resources. */
|
|
187
187
|
onShutdown?(context: PluginContext): void | Promise<void>;
|
|
188
|
+
/**
|
|
189
|
+
* Admin component overrides and layout slots registered by this plugin.
|
|
190
|
+
* Merged into the global registries at admin init time.
|
|
191
|
+
*/
|
|
192
|
+
adminComponents?: AdminComponentsConfig;
|
|
188
193
|
/** Browser-safe import descriptors for the admin config generator.
|
|
189
194
|
* Tells the generator where to import collections, admin routes, and
|
|
190
195
|
* modifyCollections from browser-safe sub-paths instead of the main
|
|
@@ -134,6 +134,16 @@ export interface SchedulePublishResult {
|
|
|
134
134
|
/** Scheduled publish date (ISO string) */
|
|
135
135
|
scheduledPublishAt: string;
|
|
136
136
|
}
|
|
137
|
+
/**
|
|
138
|
+
* Check if a collection has version drafts enabled.
|
|
139
|
+
* `versions: true` enables versioning but NOT drafts.
|
|
140
|
+
* Only `versions: { drafts: true }` (or `{ drafts: { ... } }`) enables drafts.
|
|
141
|
+
*/
|
|
142
|
+
export declare function hasVersionDrafts(collection: {
|
|
143
|
+
versions?: boolean | {
|
|
144
|
+
drafts?: boolean | object;
|
|
145
|
+
};
|
|
146
|
+
}): boolean;
|
|
137
147
|
/**
|
|
138
148
|
* Event data passed to version-related hooks.
|
|
139
149
|
*/
|