@momentumcms/core 0.5.3 → 0.5.5
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 +23 -1
- package/src/index.js +21 -1
- package/src/lib/collections/collection.types.d.ts +35 -0
- package/src/lib/config.d.ts +64 -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,
|
|
@@ -70,6 +71,7 @@ __export(src_exports, {
|
|
|
70
71
|
richText: () => richText,
|
|
71
72
|
row: () => row,
|
|
72
73
|
select: () => select,
|
|
74
|
+
shouldSyncSchema: () => shouldSyncSchema,
|
|
73
75
|
slug: () => slug,
|
|
74
76
|
tabs: () => tabs,
|
|
75
77
|
text: () => text,
|
|
@@ -648,7 +650,8 @@ function defineMomentumConfig(config) {
|
|
|
648
650
|
admin: {
|
|
649
651
|
basePath: config.admin?.basePath ?? "/admin",
|
|
650
652
|
branding: config.admin?.branding ?? {},
|
|
651
|
-
toasts: config.admin?.toasts ?? true
|
|
653
|
+
toasts: config.admin?.toasts ?? true,
|
|
654
|
+
components: config.admin?.components
|
|
652
655
|
},
|
|
653
656
|
server: {
|
|
654
657
|
port: config.server?.port ?? 3e3,
|
|
@@ -674,6 +677,15 @@ function defineMomentumConfig(config) {
|
|
|
674
677
|
migrations: resolveMigrationConfig(config.migrations)
|
|
675
678
|
};
|
|
676
679
|
}
|
|
680
|
+
function shouldSyncSchema(config) {
|
|
681
|
+
const explicit = config.db.syncSchema ?? "auto";
|
|
682
|
+
if (typeof explicit === "boolean")
|
|
683
|
+
return explicit;
|
|
684
|
+
if (!config.migrations)
|
|
685
|
+
return true;
|
|
686
|
+
const mode = resolveMigrationMode(config.migrations.mode);
|
|
687
|
+
return mode !== "migrate";
|
|
688
|
+
}
|
|
677
689
|
function getDbAdapter(config) {
|
|
678
690
|
return config.db.adapter;
|
|
679
691
|
}
|
|
@@ -769,6 +781,14 @@ function createSeedHelpers() {
|
|
|
769
781
|
}
|
|
770
782
|
};
|
|
771
783
|
}
|
|
784
|
+
|
|
785
|
+
// libs/core/src/lib/versions/version.types.ts
|
|
786
|
+
function hasVersionDrafts(collection) {
|
|
787
|
+
const v = collection.versions;
|
|
788
|
+
if (!v || typeof v === "boolean")
|
|
789
|
+
return false;
|
|
790
|
+
return !!v.drafts;
|
|
791
|
+
}
|
|
772
792
|
// Annotate the CommonJS export names for ESM import in node:
|
|
773
793
|
0 && (module.exports = {
|
|
774
794
|
LAYOUT_FIELD_TYPES,
|
|
@@ -802,6 +822,7 @@ function createSeedHelpers() {
|
|
|
802
822
|
hasAllRoles,
|
|
803
823
|
hasAnyRole,
|
|
804
824
|
hasRole,
|
|
825
|
+
hasVersionDrafts,
|
|
805
826
|
humanizeFieldName,
|
|
806
827
|
isAuthenticated,
|
|
807
828
|
isLayoutField,
|
|
@@ -821,6 +842,7 @@ function createSeedHelpers() {
|
|
|
821
842
|
richText,
|
|
822
843
|
row,
|
|
823
844
|
select,
|
|
845
|
+
shouldSyncSchema,
|
|
824
846
|
slug,
|
|
825
847
|
tabs,
|
|
826
848
|
text,
|
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,
|
|
@@ -593,6 +594,15 @@ function defineMomentumConfig(config) {
|
|
|
593
594
|
migrations: resolveMigrationConfig(config.migrations)
|
|
594
595
|
};
|
|
595
596
|
}
|
|
597
|
+
function shouldSyncSchema(config) {
|
|
598
|
+
const explicit = config.db.syncSchema ?? "auto";
|
|
599
|
+
if (typeof explicit === "boolean")
|
|
600
|
+
return explicit;
|
|
601
|
+
if (!config.migrations)
|
|
602
|
+
return true;
|
|
603
|
+
const mode = resolveMigrationMode(config.migrations.mode);
|
|
604
|
+
return mode !== "migrate";
|
|
605
|
+
}
|
|
596
606
|
function getDbAdapter(config) {
|
|
597
607
|
return config.db.adapter;
|
|
598
608
|
}
|
|
@@ -688,6 +698,14 @@ function createSeedHelpers() {
|
|
|
688
698
|
}
|
|
689
699
|
};
|
|
690
700
|
}
|
|
701
|
+
|
|
702
|
+
// libs/core/src/lib/versions/version.types.ts
|
|
703
|
+
function hasVersionDrafts(collection) {
|
|
704
|
+
const v = collection.versions;
|
|
705
|
+
if (!v || typeof v === "boolean")
|
|
706
|
+
return false;
|
|
707
|
+
return !!v.drafts;
|
|
708
|
+
}
|
|
691
709
|
export {
|
|
692
710
|
LAYOUT_FIELD_TYPES,
|
|
693
711
|
MIN_PASSWORD_LENGTH,
|
|
@@ -720,6 +738,7 @@ export {
|
|
|
720
738
|
hasAllRoles,
|
|
721
739
|
hasAnyRole,
|
|
722
740
|
hasRole,
|
|
741
|
+
hasVersionDrafts,
|
|
723
742
|
humanizeFieldName,
|
|
724
743
|
isAuthenticated,
|
|
725
744
|
isLayoutField,
|
|
@@ -739,6 +758,7 @@ export {
|
|
|
739
758
|
richText,
|
|
740
759
|
row,
|
|
741
760
|
select,
|
|
761
|
+
shouldSyncSchema,
|
|
742
762
|
slug,
|
|
743
763
|
tabs,
|
|
744
764
|
text,
|
|
@@ -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
|
@@ -193,6 +193,50 @@ export interface DatabaseConfig {
|
|
|
193
193
|
* Use @momentumcms/db-drizzle for Drizzle ORM support.
|
|
194
194
|
*/
|
|
195
195
|
adapter: DatabaseAdapter;
|
|
196
|
+
/**
|
|
197
|
+
* Whether to auto-sync the database schema on server start.
|
|
198
|
+
*
|
|
199
|
+
* - `true`: Always run `adapter.initialize()` (CREATE TABLE IF NOT EXISTS).
|
|
200
|
+
* - `false`: Never auto-sync — expect migrations to be run separately.
|
|
201
|
+
* - `'auto'`: Sync when no migration config or in `push` mode;
|
|
202
|
+
* skip when migration mode is `migrate`.
|
|
203
|
+
*
|
|
204
|
+
* @default 'auto'
|
|
205
|
+
*/
|
|
206
|
+
syncSchema?: boolean | 'auto';
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Admin component overrides and layout slot registrations.
|
|
210
|
+
* Used in `AdminPanelConfig.components` for global overrides
|
|
211
|
+
* and in `MomentumPlugin.adminComponents` for plugin-level overrides.
|
|
212
|
+
*
|
|
213
|
+
* Loaders use `() => Promise<unknown>` so this interface stays
|
|
214
|
+
* framework-agnostic (core is `env:universal`). The admin package
|
|
215
|
+
* casts to `Type<unknown>` at registration time.
|
|
216
|
+
*/
|
|
217
|
+
export interface AdminComponentsConfig {
|
|
218
|
+
/** Replace the dashboard page */
|
|
219
|
+
dashboard?: () => Promise<unknown>;
|
|
220
|
+
/** Replace the login page */
|
|
221
|
+
login?: () => Promise<unknown>;
|
|
222
|
+
/** Replace the media library page */
|
|
223
|
+
media?: () => Promise<unknown>;
|
|
224
|
+
/** Slot: before navigation links in sidebar */
|
|
225
|
+
beforeNavigation?: () => Promise<unknown>;
|
|
226
|
+
/** Slot: after navigation links in sidebar */
|
|
227
|
+
afterNavigation?: () => Promise<unknown>;
|
|
228
|
+
/** Slot: global header (between mobile header and main content) */
|
|
229
|
+
header?: () => Promise<unknown>;
|
|
230
|
+
/** Slot: global footer (after main content) */
|
|
231
|
+
footer?: () => Promise<unknown>;
|
|
232
|
+
/** Slot: before dashboard content */
|
|
233
|
+
beforeDashboard?: () => Promise<unknown>;
|
|
234
|
+
/** Slot: after dashboard content */
|
|
235
|
+
afterDashboard?: () => Promise<unknown>;
|
|
236
|
+
/** Slot: before login form */
|
|
237
|
+
beforeLogin?: () => Promise<unknown>;
|
|
238
|
+
/** Slot: after login form */
|
|
239
|
+
afterLogin?: () => Promise<unknown>;
|
|
196
240
|
}
|
|
197
241
|
/**
|
|
198
242
|
* Global admin panel configuration.
|
|
@@ -219,6 +263,12 @@ export interface AdminPanelConfig {
|
|
|
219
263
|
* @default true
|
|
220
264
|
*/
|
|
221
265
|
toasts?: boolean;
|
|
266
|
+
/**
|
|
267
|
+
* Custom admin component overrides and layout slots.
|
|
268
|
+
* Register page replacements (dashboard, login, media) and
|
|
269
|
+
* slot components (header, footer, beforeDashboard, etc.).
|
|
270
|
+
*/
|
|
271
|
+
components?: AdminComponentsConfig;
|
|
222
272
|
}
|
|
223
273
|
/**
|
|
224
274
|
* Server configuration.
|
|
@@ -378,11 +428,16 @@ export type ResolvedSeedingOptions = Required<SeedingOptions>;
|
|
|
378
428
|
export interface ResolvedSeedingConfig extends SeedingConfig {
|
|
379
429
|
options: ResolvedSeedingOptions;
|
|
380
430
|
}
|
|
431
|
+
/**
|
|
432
|
+
* AdminPanelConfig with primitive defaults resolved (basePath, branding, toasts).
|
|
433
|
+
* `components` remains optional since it has no default.
|
|
434
|
+
*/
|
|
435
|
+
export type ResolvedAdminPanelConfig = Required<Omit<AdminPanelConfig, 'components'>> & Pick<AdminPanelConfig, 'components'>;
|
|
381
436
|
/**
|
|
382
437
|
* Internal config with resolved defaults.
|
|
383
438
|
*/
|
|
384
439
|
export interface ResolvedMomentumConfig extends MomentumConfig {
|
|
385
|
-
admin:
|
|
440
|
+
admin: ResolvedAdminPanelConfig;
|
|
386
441
|
server: Required<ServerConfig>;
|
|
387
442
|
seeding?: ResolvedSeedingConfig;
|
|
388
443
|
logging: ResolvedLoggingConfig;
|
|
@@ -415,6 +470,14 @@ export interface ResolvedMomentumConfig extends MomentumConfig {
|
|
|
415
470
|
* ```
|
|
416
471
|
*/
|
|
417
472
|
export declare function defineMomentumConfig(config: MomentumConfig): ResolvedMomentumConfig;
|
|
473
|
+
/**
|
|
474
|
+
* Determine whether the server should auto-sync the database schema on boot.
|
|
475
|
+
*
|
|
476
|
+
* Resolution order:
|
|
477
|
+
* 1. Explicit `db.syncSchema` (true / false) — always wins.
|
|
478
|
+
* 2. `'auto'` (or omitted) — sync unless migration mode is `'migrate'`.
|
|
479
|
+
*/
|
|
480
|
+
export declare function shouldSyncSchema(config: MomentumConfig | ResolvedMomentumConfig): boolean;
|
|
418
481
|
/**
|
|
419
482
|
* Gets the database adapter from the config.
|
|
420
483
|
*/
|
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
|
*/
|