@lupinum/ginko-content 0.3.3 → 0.3.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/dist/cms-contract/build.js +51 -18
- package/dist/module.json +1 -1
- package/dist/module.mjs +1 -1
- package/dist/runtime/app/components/ContentRendererInline.vue +3 -1
- package/dist/runtime/app/components/internal/ContentRendererMarkdown.vue +3 -1
- package/dist/runtime/app/components/internal/MarkdownRenderer.d.ts +9 -0
- package/dist/runtime/app/components/internal/MarkdownRenderer.js +15 -4
- package/dist/runtime/markdown/plugins.d.ts +5 -0
- package/dist/runtime/markdown/plugins.js +4 -3
- package/dist/web-types.json +9 -1
- package/package.json +1 -1
|
@@ -84,20 +84,20 @@ function fieldFromSchema(key, schema, localized) {
|
|
|
84
84
|
const required = !["ZodOptional", "ZodNullable", "ZodDefault"].includes(getSchemaTypeName(schema) ?? "");
|
|
85
85
|
if (metadata) return fieldFromMetadata(key, metadata, schema, unwrapped, localized, required);
|
|
86
86
|
const reference = getReferenceDescriptor(unwrapped);
|
|
87
|
-
if (reference) return field({ key, type: "relation", required, default: defaultFromSchema(schema), relation: { collection: reference.collection ?? "", multiple: false } });
|
|
87
|
+
if (reference) return field({ key, type: "relation", required, default: defaultFromSchema(schema, key), relation: { collection: reference.collection ?? "", multiple: false } });
|
|
88
88
|
const typeName = getSchemaTypeName(unwrapped);
|
|
89
|
-
if (typeName === "ZodObject") return field({ key, type: "object", required, localized, default: defaultFromSchema(schema), fields: nestedFields(unwrapped), validation: validationFromSchema(schema) });
|
|
89
|
+
if (typeName === "ZodObject") return field({ key, type: "object", required, localized, default: defaultFromSchema(schema, key), fields: nestedFields(unwrapped), validation: validationFromSchema(schema) });
|
|
90
90
|
if (typeName === "ZodArray") {
|
|
91
91
|
const element = getSchemaDef(unwrapped)?.element;
|
|
92
92
|
const relation = getReferenceDescriptor(unwrapSchema(element));
|
|
93
|
-
if (relation) return field({ key, type: "relations", required, default: defaultFromSchema(schema), relation: { collection: relation.collection ?? "", multiple: true } });
|
|
93
|
+
if (relation) return field({ key, type: "relations", required, default: defaultFromSchema(schema, key), relation: { collection: relation.collection ?? "", multiple: true } });
|
|
94
94
|
const children = getSchemaTypeName(unwrapSchema(element)) === "ZodObject" ? nestedFields(element) : null;
|
|
95
|
-
return field({ key, type: children ? "array" : "json", required, localized, default: defaultFromSchema(schema), fields: children, validation: validationFromSchema(schema) });
|
|
95
|
+
return field({ key, type: children ? "array" : "json", required, localized, default: defaultFromSchema(schema, key), fields: children, validation: validationFromSchema(schema) });
|
|
96
96
|
}
|
|
97
|
-
if (typeName === "ZodNumber") return field({ key, type: "number", required, default: defaultFromSchema(schema), validation: validationFromSchema(schema) });
|
|
98
|
-
if (typeName === "ZodBoolean") return field({ key, type: "toggle", required, default: defaultFromSchema(schema), validation: validationFromSchema(schema) });
|
|
99
|
-
if (typeName === "ZodDate") return field({ key, type: "date", required, default: defaultFromSchema(schema), validation: validationFromSchema(schema) });
|
|
100
|
-
return field({ key, type: typeName === "ZodString" ? "text" : "json", required, localized, default: defaultFromSchema(schema), validation: validationFromSchema(schema) });
|
|
97
|
+
if (typeName === "ZodNumber") return field({ key, type: "number", required, default: defaultFromSchema(schema, key), validation: validationFromSchema(schema) });
|
|
98
|
+
if (typeName === "ZodBoolean") return field({ key, type: "toggle", required, default: defaultFromSchema(schema, key), validation: validationFromSchema(schema) });
|
|
99
|
+
if (typeName === "ZodDate") return field({ key, type: "date", required, default: defaultFromSchema(schema, key), validation: validationFromSchema(schema) });
|
|
100
|
+
return field({ key, type: typeName === "ZodString" ? "text" : "json", required, localized, default: defaultFromSchema(schema, key), validation: validationFromSchema(schema) });
|
|
101
101
|
}
|
|
102
102
|
function fieldFromMetadata(key, metadata, sourceSchema, schema, localized, required) {
|
|
103
103
|
const type = metadata.type === "boolean" ? "toggle" : metadata.type === "asset" ? "file" : metadata.type;
|
|
@@ -107,7 +107,7 @@ function fieldFromMetadata(key, metadata, sourceSchema, schema, localized, requi
|
|
|
107
107
|
type,
|
|
108
108
|
required: metadata.required ?? required,
|
|
109
109
|
localized: metadata.localized ?? localized,
|
|
110
|
-
default: defaultFromSchema(sourceSchema),
|
|
110
|
+
default: defaultFromSchema(sourceSchema, key),
|
|
111
111
|
options: metadata.options ?? null,
|
|
112
112
|
relation: metadata.relation ? { collection: metadata.relation.collectionId, multiple: metadata.relation.multiple ?? type === "relations" } : null,
|
|
113
113
|
media: metadata.image || metadata.asset ? { mediaTypes: portableMediaTypes(metadata.image?.accept ?? metadata.asset?.accept), aspectRatio: metadata.image?.aspectRatio ?? null } : null,
|
|
@@ -132,7 +132,7 @@ function mergeField(existing, key, override, localized) {
|
|
|
132
132
|
localized: override.localized ?? base.localized,
|
|
133
133
|
searchable: override.searchable ?? base.searchable,
|
|
134
134
|
sortable: override.sortable ?? base.sortable,
|
|
135
|
-
default: Object.prototype.hasOwnProperty.call(override, "defaultValue") ? { present: true, value: override.defaultValue } : base.default,
|
|
135
|
+
default: Object.prototype.hasOwnProperty.call(override, "defaultValue") ? { present: true, value: jsonDefault(override.defaultValue, key) } : base.default,
|
|
136
136
|
options: override.options ?? base.options,
|
|
137
137
|
relation: override.relation ? { collection: override.relation.collectionId, multiple: override.relation.multiple ?? type === "relations" } : base.relation,
|
|
138
138
|
fields: override.fields ? configuredFields(override.fields, false) : base.fields,
|
|
@@ -143,17 +143,45 @@ function mergeField(existing, key, override, localized) {
|
|
|
143
143
|
language: override.language ?? base.language
|
|
144
144
|
});
|
|
145
145
|
}
|
|
146
|
-
function defaultFromSchema(schema) {
|
|
146
|
+
function defaultFromSchema(schema, key) {
|
|
147
147
|
let current = schema;
|
|
148
148
|
while (current) {
|
|
149
149
|
const typeName = getSchemaTypeName(current);
|
|
150
150
|
const definition = getSchemaDef(current);
|
|
151
|
-
if (typeName === "ZodDefault") return { present: true, value: definition?.defaultValue };
|
|
151
|
+
if (typeName === "ZodDefault") return { present: true, value: jsonDefault(definition?.defaultValue, key) };
|
|
152
152
|
if (!["ZodOptional", "ZodNullable"].includes(typeName ?? "")) break;
|
|
153
153
|
current = definition?.innerType;
|
|
154
154
|
}
|
|
155
155
|
return { present: false };
|
|
156
156
|
}
|
|
157
|
+
function jsonDefault(value, key, ancestors = /* @__PURE__ */ new Set()) {
|
|
158
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") return value;
|
|
159
|
+
if (typeof value === "number") {
|
|
160
|
+
if (!Number.isFinite(value)) throw new TypeError(`Field "${key}" has a non-finite default value.`);
|
|
161
|
+
return value;
|
|
162
|
+
}
|
|
163
|
+
if (value instanceof Date) {
|
|
164
|
+
if (Number.isNaN(value.getTime())) throw new TypeError(`Field "${key}" has an invalid date default.`);
|
|
165
|
+
return value.toISOString();
|
|
166
|
+
}
|
|
167
|
+
if (typeof value !== "object" || value === null) throw new TypeError(`Field "${key}" has a non-JSON-serializable default value.`);
|
|
168
|
+
if (ancestors.has(value)) throw new TypeError(`Field "${key}" has a cyclic default value.`);
|
|
169
|
+
ancestors.add(value);
|
|
170
|
+
try {
|
|
171
|
+
if (Array.isArray(value)) return value.map((entry, index) => jsonDefault(entry, `${key}[${index}]`, ancestors));
|
|
172
|
+
const prototype = Object.getPrototypeOf(value);
|
|
173
|
+
if (prototype !== Object.prototype && prototype !== null) throw new TypeError(`Field "${key}" has a non-JSON-serializable default value.`);
|
|
174
|
+
if (Object.getOwnPropertySymbols(value).length > 0) throw new TypeError(`Field "${key}" has a non-JSON-serializable default value.`);
|
|
175
|
+
const result = {};
|
|
176
|
+
for (const [property, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(value))) {
|
|
177
|
+
if (!descriptor.enumerable || !("value" in descriptor)) throw new TypeError(`Field "${key}" has a non-JSON-serializable default value.`);
|
|
178
|
+
result[property] = jsonDefault(descriptor.value, `${key}.${property}`, ancestors);
|
|
179
|
+
}
|
|
180
|
+
return result;
|
|
181
|
+
} finally {
|
|
182
|
+
ancestors.delete(value);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
157
185
|
function validationFromSchema(schema, semanticType) {
|
|
158
186
|
const typeName = getSchemaTypeName(schema);
|
|
159
187
|
const definition = getSchemaDef(schema);
|
|
@@ -170,16 +198,16 @@ function validationFromSchema(schema, semanticType) {
|
|
|
170
198
|
const format = ["email", "url", "date", "datetime", "time"].includes(String(value.format)) ? value.format : null;
|
|
171
199
|
return {
|
|
172
200
|
kind: "string",
|
|
173
|
-
minLength:
|
|
174
|
-
maxLength:
|
|
201
|
+
minLength: finiteNumber(value.minLength),
|
|
202
|
+
maxLength: finiteNumber(value.maxLength),
|
|
175
203
|
format
|
|
176
204
|
};
|
|
177
205
|
}
|
|
178
206
|
if (typeName === "ZodNumber") {
|
|
179
207
|
return {
|
|
180
208
|
kind: "number",
|
|
181
|
-
min:
|
|
182
|
-
max:
|
|
209
|
+
min: finiteNumber(value.minValue),
|
|
210
|
+
max: finiteNumber(value.maxValue),
|
|
183
211
|
integer: value.isInt === true
|
|
184
212
|
};
|
|
185
213
|
}
|
|
@@ -210,12 +238,17 @@ function arrayLimits(checks) {
|
|
|
210
238
|
if (Array.isArray(checks)) {
|
|
211
239
|
for (const check of checks) {
|
|
212
240
|
const definition = check._zod?.def ?? check.def;
|
|
213
|
-
|
|
214
|
-
|
|
241
|
+
const minimum = finiteNumber(definition?.minimum);
|
|
242
|
+
const maximum = finiteNumber(definition?.maximum);
|
|
243
|
+
if (definition?.check === "min_length" && minimum !== null) minItems = minimum;
|
|
244
|
+
if (definition?.check === "max_length" && maximum !== null) maxItems = maximum;
|
|
215
245
|
}
|
|
216
246
|
}
|
|
217
247
|
return { minItems, maxItems };
|
|
218
248
|
}
|
|
249
|
+
function finiteNumber(value) {
|
|
250
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
251
|
+
}
|
|
219
252
|
function configuredFields(input, localized) {
|
|
220
253
|
const entries = Array.isArray(input) ? input.map((value, index) => [String(index), value]) : Object.entries(input);
|
|
221
254
|
return entries.flatMap(([key, value]) => value.type === "divider" || value.type === "section" ? [] : [mergeField(void 0, key, value, localized)]);
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { computed, onServerPrefetch, shallowRef, useAttrs, watch } from "vue";
|
|
|
4
4
|
import { useRuntimeConfig, useState } from "#imports";
|
|
5
5
|
import MarkdownRenderer from "./internal/MarkdownRenderer.js";
|
|
6
6
|
import { useUnwrap } from "../composables/useUnwrap";
|
|
7
|
-
import { resolveMarkdownPlugins, resolveMarkdownRendererComponents } from "../../markdown/plugins";
|
|
7
|
+
import { resolveMarkdownPlugins, resolveMarkdownRendererComponents, resolveMarkdownRendererFallbackComponents } from "../../markdown/plugins";
|
|
8
8
|
import { toMarkdownRoot } from "../../../core/markdown/tree";
|
|
9
9
|
import { parseComark } from "../../../core/markdown/parse-comark";
|
|
10
10
|
import { loadContentComponentEntries } from "../../../integrations/vue/content-components";
|
|
@@ -44,6 +44,7 @@ const initialState = useState(stateKey, () => ({
|
|
|
44
44
|
tree: null
|
|
45
45
|
}));
|
|
46
46
|
const tree = shallowRef(initialState.value.tree);
|
|
47
|
+
const fallbackComponents = resolveMarkdownRendererFallbackComponents();
|
|
47
48
|
const resolvedComponents = computed(() => ({
|
|
48
49
|
...Object.fromEntries(loadContentComponentEntries({
|
|
49
50
|
type: "root",
|
|
@@ -92,6 +93,7 @@ watch(
|
|
|
92
93
|
:tag="tag"
|
|
93
94
|
:prose="prose"
|
|
94
95
|
:components="resolvedComponents"
|
|
96
|
+
:fallback-components="fallbackComponents"
|
|
95
97
|
v-bind="rendererAttrs"
|
|
96
98
|
/>
|
|
97
99
|
</template>
|
|
@@ -7,7 +7,7 @@ import MarkdownRenderer from "./MarkdownRenderer.js";
|
|
|
7
7
|
import { useLocalePath } from "../../composables/content-i18n";
|
|
8
8
|
import { resolveMarkdownRenderRefs, rewriteMarkdownRefLinks } from "../../../../core/references/resolve";
|
|
9
9
|
import { loadContentComponentEntries } from "../../../../integrations/vue/content-components";
|
|
10
|
-
import { resolveMarkdownRendererComponents } from "../../../markdown/plugins";
|
|
10
|
+
import { resolveMarkdownRendererComponents, resolveMarkdownRendererFallbackComponents } from "../../../markdown/plugins";
|
|
11
11
|
import { isMarkdownRoot } from "../../../../core/markdown/tree";
|
|
12
12
|
const props = defineProps({
|
|
13
13
|
value: {
|
|
@@ -79,6 +79,7 @@ const renderedBody = computed(() => {
|
|
|
79
79
|
}
|
|
80
80
|
return props.unwrap ? unwrapRoot(body.value, props.unwrap) : body.value;
|
|
81
81
|
});
|
|
82
|
+
const fallbackComponents = resolveMarkdownRendererFallbackComponents();
|
|
82
83
|
const resolvedComponents = computed(() => {
|
|
83
84
|
if (!renderedBody.value) {
|
|
84
85
|
return props.components;
|
|
@@ -108,6 +109,7 @@ const rendererAttrs = computed(() => {
|
|
|
108
109
|
:default-locale="defaultLocale"
|
|
109
110
|
:locales="locales"
|
|
110
111
|
:components="resolvedComponents"
|
|
112
|
+
:fallback-components="fallbackComponents"
|
|
111
113
|
:render-policy="renderPolicy"
|
|
112
114
|
:data-content-id="debug ? value.id : void 0"
|
|
113
115
|
v-bind="rendererAttrs"
|
|
@@ -11,6 +11,10 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
|
|
|
11
11
|
type: PropType<Record<string, any>>;
|
|
12
12
|
default: () => {};
|
|
13
13
|
};
|
|
14
|
+
fallbackComponents: {
|
|
15
|
+
type: PropType<Record<string, any>>;
|
|
16
|
+
default: () => {};
|
|
17
|
+
};
|
|
14
18
|
prose: {
|
|
15
19
|
type: PropType<boolean | undefined>;
|
|
16
20
|
default: undefined;
|
|
@@ -56,6 +60,10 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
|
|
|
56
60
|
type: PropType<Record<string, any>>;
|
|
57
61
|
default: () => {};
|
|
58
62
|
};
|
|
63
|
+
fallbackComponents: {
|
|
64
|
+
type: PropType<Record<string, any>>;
|
|
65
|
+
default: () => {};
|
|
66
|
+
};
|
|
59
67
|
prose: {
|
|
60
68
|
type: PropType<boolean | undefined>;
|
|
61
69
|
default: undefined;
|
|
@@ -97,6 +105,7 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
|
|
|
97
105
|
components: Record<string, any>;
|
|
98
106
|
tag: string;
|
|
99
107
|
class: string | unknown[] | Record<string, any>;
|
|
108
|
+
fallbackComponents: Record<string, any>;
|
|
100
109
|
prose: boolean | undefined;
|
|
101
110
|
dataContentId: string;
|
|
102
111
|
renderPolicy: PortableComponentPolicyV1;
|
|
@@ -23,7 +23,7 @@ export function localizeMarkdownNodeProps(props, locale, defaultLocale, locales
|
|
|
23
23
|
localizeLinkProps(localizedProps, locale, defaultLocale, locales);
|
|
24
24
|
return localizedProps;
|
|
25
25
|
}
|
|
26
|
-
function resolveVueComponent(name, components, registry, prose, seen = /* @__PURE__ */ new Set()) {
|
|
26
|
+
function resolveVueComponent(name, components, registry, fallbacks, prose, seen = /* @__PURE__ */ new Set()) {
|
|
27
27
|
if (seen.has(name)) {
|
|
28
28
|
return null;
|
|
29
29
|
}
|
|
@@ -40,7 +40,7 @@ function resolveVueComponent(name, components, registry, prose, seen = /* @__PUR
|
|
|
40
40
|
const explicit = components[candidate];
|
|
41
41
|
if (explicit) {
|
|
42
42
|
if (typeof explicit === "string" && explicit !== candidate && !htmlTags.includes(explicit)) {
|
|
43
|
-
return resolveVueComponent(explicit, components, registry, prose, seen) || explicit;
|
|
43
|
+
return resolveVueComponent(explicit, components, registry, fallbacks, prose, seen) || explicit;
|
|
44
44
|
}
|
|
45
45
|
return explicit;
|
|
46
46
|
}
|
|
@@ -51,6 +51,12 @@ function resolveVueComponent(name, components, registry, prose, seen = /* @__PUR
|
|
|
51
51
|
return registered;
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
|
+
for (const candidate of candidates) {
|
|
55
|
+
const fallback = fallbacks[candidate];
|
|
56
|
+
if (fallback) {
|
|
57
|
+
return fallback;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
54
60
|
return null;
|
|
55
61
|
}
|
|
56
62
|
function renderNode(node, options, key, parent) {
|
|
@@ -72,8 +78,8 @@ function renderNode(node, options, key, parent) {
|
|
|
72
78
|
if (parent?.tag === "pre") {
|
|
73
79
|
component = tag;
|
|
74
80
|
} else {
|
|
75
|
-
const resolvedAs = typeof nodeProps.as === "string" ? resolveVueComponent(nodeProps.as, options.components, options.registry, options.prose) : null;
|
|
76
|
-
const resolvedComponent = resolvedAs || resolveVueComponent(tag, options.components, options.registry, options.prose);
|
|
81
|
+
const resolvedAs = typeof nodeProps.as === "string" ? resolveVueComponent(nodeProps.as, options.components, options.registry, options.fallbacks, options.prose) : null;
|
|
82
|
+
const resolvedComponent = resolvedAs || resolveVueComponent(tag, options.components, options.registry, options.fallbacks, options.prose);
|
|
77
83
|
if (resolvedComponent) {
|
|
78
84
|
component = resolvedComponent;
|
|
79
85
|
} else if (htmlTags.includes(tag)) {
|
|
@@ -143,6 +149,10 @@ export default defineComponent({
|
|
|
143
149
|
type: Object,
|
|
144
150
|
default: () => ({})
|
|
145
151
|
},
|
|
152
|
+
fallbackComponents: {
|
|
153
|
+
type: Object,
|
|
154
|
+
default: () => ({})
|
|
155
|
+
},
|
|
146
156
|
prose: {
|
|
147
157
|
type: Boolean,
|
|
148
158
|
default: void 0
|
|
@@ -184,6 +194,7 @@ export default defineComponent({
|
|
|
184
194
|
const children = (props.tree.children || []).map((node, index) => renderNode(node, {
|
|
185
195
|
components: props.components,
|
|
186
196
|
registry,
|
|
197
|
+
fallbacks: props.fallbackComponents,
|
|
187
198
|
prose: props.prose,
|
|
188
199
|
locale: props.locale,
|
|
189
200
|
defaultLocale: props.defaultLocale,
|
|
@@ -1,3 +1,8 @@
|
|
|
1
1
|
import type { ResolvedMarkdownPlugin } from '../../types/content';
|
|
2
2
|
export { resolveMarkdownPlugins } from '../../parsers/markdown-plugins';
|
|
3
|
+
/**
|
|
4
|
+
* Builtin prose components resolve after the app's component registry, so an
|
|
5
|
+
* app-registered ProseImg (or a tags remap) wins over the bundled default.
|
|
6
|
+
*/
|
|
7
|
+
export declare function resolveMarkdownRendererFallbackComponents(): Record<string, unknown>;
|
|
3
8
|
export declare function resolveMarkdownRendererComponents(plugins: ResolvedMarkdownPlugin[]): Record<string, unknown>;
|
|
@@ -23,10 +23,11 @@ const builtinMarkdownPlugins = {
|
|
|
23
23
|
const builtinMarkdownComponents = {
|
|
24
24
|
ProseImg: defineAsyncComponent(async () => await import("../app/components/Prose/ProseImg.vue"))
|
|
25
25
|
};
|
|
26
|
+
export function resolveMarkdownRendererFallbackComponents() {
|
|
27
|
+
return { ...builtinMarkdownComponents };
|
|
28
|
+
}
|
|
26
29
|
export function resolveMarkdownRendererComponents(plugins) {
|
|
27
|
-
const components = {
|
|
28
|
-
...builtinMarkdownComponents
|
|
29
|
-
};
|
|
30
|
+
const components = {};
|
|
30
31
|
for (const plugin of plugins) {
|
|
31
32
|
const builtin = builtinMarkdownPlugins[plugin.name];
|
|
32
33
|
if (builtin?.componentName && builtin.component) {
|
package/dist/web-types.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"framework": "vue",
|
|
3
3
|
"name": "@lupinum/ginko-content",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.5",
|
|
5
5
|
"contributions": {
|
|
6
6
|
"html": {
|
|
7
7
|
"description-markup": "markdown",
|
|
@@ -202,6 +202,14 @@
|
|
|
202
202
|
},
|
|
203
203
|
"default": "() => ({})"
|
|
204
204
|
},
|
|
205
|
+
{
|
|
206
|
+
"name": "fallbackComponents",
|
|
207
|
+
"value": {
|
|
208
|
+
"kind": "expression",
|
|
209
|
+
"type": "Record<string, any>"
|
|
210
|
+
},
|
|
211
|
+
"default": "() => ({})"
|
|
212
|
+
},
|
|
205
213
|
{
|
|
206
214
|
"name": "prose",
|
|
207
215
|
"value": {
|