Skip to content

Commit 75461cd

Browse files
committed
feat: auto-register widgets and components
1 parent 7ec1b31 commit 75461cd

File tree

2 files changed

+117
-103
lines changed

2 files changed

+117
-103
lines changed

packages/decap-cms-app/src/init/core-widgets.ts

Lines changed: 0 additions & 29 deletions
This file was deleted.

packages/decap-cms-app/src/init/index.ts

Lines changed: 117 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,133 @@
11
export type * from 'decap-cms-core';
22
import { DecapCmsCore as cms } from 'decap-cms-core';
33

4-
import type { CMS, CmsBackendType, InitOptions } from 'decap-cms-core';
4+
import type { CMS, CmsBackendType, CmsConfig, CmsField, InitOptions } from 'decap-cms-core';
55
export { CMS };
66

7-
// Backend registration functions
8-
export async function registerBackend(type: CmsBackendType) {
9-
switch (type) {
10-
case 'aws-cognito-github-proxy':
11-
cms.registerBackend(
12-
type,
13-
(await import('decap-cms-backend-aws-cognito-github-proxy')).AwsCognitoGitHubProxyBackend,
14-
);
15-
break;
16-
case 'azure':
17-
cms.registerBackend(type, (await import('decap-cms-backend-azure')).AzureBackend);
18-
break;
19-
case 'bitbucket':
20-
cms.registerBackend(type, (await import('decap-cms-backend-bitbucket')).BitbucketBackend);
21-
break;
22-
case 'git-gateway':
23-
cms.registerBackend(type, (await import('decap-cms-backend-git-gateway')).GitGatewayBackend);
24-
break;
25-
case 'github':
26-
cms.registerBackend(type, (await import('decap-cms-backend-github')).GitHubBackend);
27-
break;
28-
case 'gitlab':
29-
cms.registerBackend(type, (await import('decap-cms-backend-gitlab')).GitLabBackend);
30-
break;
31-
case 'gitea':
32-
cms.registerBackend(type, (await import('decap-cms-backend-gitea')).GiteaBackend);
33-
break;
34-
case 'test-repo':
35-
cms.registerBackend(type, (await import('decap-cms-backend-test')).TestBackend);
36-
break;
37-
case 'proxy':
38-
cms.registerBackend(type, (await import('decap-cms-backend-proxy')).ProxyBackend);
39-
break;
40-
default:
41-
throw new Error(`Backend type '${type}' not supported`);
7+
// List of known backends, will be auto-loaded based on config
8+
const backends = {
9+
'aws-cognito-github-proxy': async () =>
10+
(await import('decap-cms-backend-aws-cognito-github-proxy')).AwsCognitoGitHubProxyBackend,
11+
azure: async () => (await import('decap-cms-backend-azure')).AzureBackend,
12+
bitbucket: async () => (await import('decap-cms-backend-bitbucket')).BitbucketBackend,
13+
'git-gateway': async () => (await import('decap-cms-backend-git-gateway')).GitGatewayBackend,
14+
github: async () => (await import('decap-cms-backend-github')).GitHubBackend,
15+
gitlab: async () => (await import('decap-cms-backend-gitlab')).GitLabBackend,
16+
gitea: async () => (await import('decap-cms-backend-gitea')).GiteaBackend,
17+
'test-repo': async () => (await import('decap-cms-backend-test')).TestBackend,
18+
proxy: async () => (await import('decap-cms-backend-proxy')).ProxyBackend,
19+
};
20+
21+
async function registerBackend(type: CmsBackendType) {
22+
const loader = backends[type];
23+
if (!loader) {
24+
throw new Error(`Backend type '${type}' not supported`);
4225
}
26+
cms.registerBackend(type, await loader());
4327
}
4428

45-
// Widget registration functions
46-
export async function registerCoreWidgets() {
47-
const { widgets } = await import('./core-widgets');
48-
cms.registerWidget(widgets);
49-
}
29+
// List of known widgets, will be auto-loaded based on config
30+
const widgets = {
31+
map: async () => (await import('decap-cms-widget-map')).default.Widget(),
32+
code: async () => (await import('decap-cms-widget-code')).default.Widget(),
33+
string: async () => (await import('decap-cms-widget-string')).default.Widget(),
34+
number: async () => (await import('decap-cms-widget-number')).default.Widget(),
35+
text: async () => (await import('decap-cms-widget-text')).default.Widget(),
36+
image: async () => (await import('decap-cms-widget-image')).default.Widget(),
37+
file: async () => (await import('decap-cms-widget-file')).default.Widget(),
38+
select: async () => (await import('decap-cms-widget-select')).default.Widget(),
39+
markdown: async () => (await import('decap-cms-widget-markdown')).default.Widget(),
40+
list: async () => (await import('decap-cms-widget-list')).default.Widget(),
41+
object: async () => (await import('decap-cms-widget-object')).default.Widget(),
42+
relation: async () => (await import('decap-cms-widget-relation')).default.Widget(),
43+
boolean: async () => (await import('decap-cms-widget-boolean')).default.Widget(),
44+
datetime: async () => (await import('decap-cms-widget-datetime')).default.Widget(),
45+
color: async () => (await import('decap-cms-widget-colorstring')).default.Widget(),
46+
};
5047

51-
export async function registerMapWidget() {
52-
const m = await import('decap-cms-widget-map');
53-
cms.registerWidget(m.default.Widget() as any);
54-
}
48+
// List of known editor components, will be auto-loaded based on config
49+
const editorComponents = {
50+
image: async () => (await import('decap-cms-editor-component-image')).default,
51+
'code-block': () => ({
52+
id: 'code-block',
53+
label: 'Code Block',
54+
widget: 'code',
55+
type: 'code-block',
56+
}),
57+
};
58+
59+
function registerWidgetsAndEditorComponents(config: CmsConfig) {
60+
const usedWidgets = new Set<string>();
61+
const usedEditorComponents = new Set<string>();
62+
63+
// Collect widgets and editor components used in collection fields
64+
for (const c of config.collections) {
65+
if (c.files) {
66+
for (const file of c.files) {
67+
if (file.fields) collectWidgets(file.fields, usedWidgets, usedEditorComponents);
68+
}
69+
}
70+
if (c.fields) collectWidgets(c.fields, usedWidgets, usedEditorComponents);
71+
}
72+
73+
// Load and register all used editor components
74+
usedEditorComponents.values().map(async type => {
75+
const loader = editorComponents[type as keyof typeof editorComponents];
76+
if (loader) {
77+
// Don't override manually registered components...
78+
if (!cms.getEditorComponents().has(type)) {
79+
cms.registerEditorComponent(await loader());
80+
}
81+
} else {
82+
// Check if the component has been manually registered...
83+
if (!cms.getEditorComponents().has(type)) {
84+
throw new Error(
85+
`Unknown editor component "${type}". If this is a custom component, make sure to register it.`,
86+
);
87+
}
88+
}
89+
});
5590

56-
export async function registerCodeWidget() {
57-
const m = await import('decap-cms-widget-code');
58-
cms.registerWidget(m.default.Widget() as any);
91+
// Collect widgets from editor components (like the "code" widget from the code-block)
92+
cms
93+
.getEditorComponents()
94+
.valueSeq()
95+
.forEach(e => {
96+
if (e?.widget) usedWidgets.add(e.widget);
97+
});
98+
99+
// Load and register all used widgets
100+
return Promise.all(
101+
usedWidgets.values().map(async type => {
102+
const loader = widgets[type as keyof typeof widgets];
103+
if (loader) {
104+
cms.registerWidget(await loader());
105+
} else {
106+
if (!cms.getWidget(type)) {
107+
throw new Error(
108+
`Unknown widget type "${type}". If this is a custom widget, make sure to register it.`,
109+
);
110+
}
111+
}
112+
}),
113+
);
59114
}
60115

61-
// Editor component registration functions
62-
export async function registerImageComponent() {
63-
const m = await import('decap-cms-editor-component-image');
64-
cms.registerEditorComponent(m.default);
116+
function collectWidgets(fields: CmsField[], widgets: Set<string>, editorComponents: Set<string>) {
117+
for (const f of fields) {
118+
widgets.add(f.widget);
119+
if (f.widget === 'list') {
120+
collectWidgets(f.field ? [f.field] : f.fields ?? [], widgets, editorComponents);
121+
}
122+
if (f.widget === 'object') {
123+
collectWidgets(f.fields, widgets, editorComponents);
124+
}
125+
if (f.widget === 'markdown' && f.editor_components) {
126+
f.editor_components.forEach(c => editorComponents.add(c));
127+
}
128+
}
65129
}
66130

67-
// Locale registration functions
68131
export async function registerLocale(locale: string) {
69132
const m = await import('decap-cms-locales');
70133
if (locale in m) {
@@ -111,33 +174,13 @@ export const availableLocales = [
111174
'zh_Hant', // Traditional Chinese
112175
] as const;
113176

114-
// Convenience function to register everything
115-
export async function registerAll() {
116-
await Promise.all([
117-
registerBackend('git-gateway'),
118-
registerBackend('azure'),
119-
registerBackend('aws-cognito-github-proxy'),
120-
registerBackend('github'),
121-
registerBackend('gitlab'),
122-
registerBackend('gitea'),
123-
registerBackend('bitbucket'),
124-
registerBackend('test-repo'),
125-
registerBackend('proxy'),
126-
registerCoreWidgets(),
127-
registerMapWidget(),
128-
registerCodeWidget(),
129-
registerImageComponent(),
130-
registerLocale('en'), // Register English by default
131-
]);
132-
}
133-
134177
type Options = InitOptions & { setup?: (cms: CMS) => void | Promise<void> };
135178

136179
export async function init(options: Options) {
137180
const { config, setup } = options;
138181
await Promise.all([
139182
setup && setup(cms),
140-
registerCoreWidgets(),
183+
registerWidgetsAndEditorComponents(config),
141184
registerLocale(config.locale || 'en'),
142185
registerBackend(config.local_backend ? 'proxy' : config.backend.name),
143186
]);

0 commit comments

Comments
 (0)