UNPKG

sanity

Version:

Sanity is a real-time content infrastructure with a scalable, hosted backend featuring a Graph Oriented Query Language (GROQ), asset pipelines and fast edge caches

512 lines (511 loc) • 15.6 kB
import { c } from "react-compiler-runtime"; import { ThemeProvider, useRootTheme } from "@sanity/ui"; import { isValidElement, memo, useEffect } from "react"; import { useWorkspace, RELEASES_STUDIO_CLIENT_OPTIONS, isReleasePerspective, useClient, getPublishedId } from "sanity"; import { API_VERSION } from "./presentation.mjs"; import { jsx } from "react/jsx-runtime"; import { renderToString } from "react-dom/server"; import { ServerStyleSheet, StyleSheetManager } from "styled-components"; function isFieldRequired(field) { const { validation } = field.type; if (!validation) return !1; const rules = Array.isArray(validation) ? validation : [validation]; for (const rule of rules) { let required = !1; const proxy = new Proxy({}, { get: (target, methodName) => () => (methodName === "required" && (required = !0), proxy) }); if (typeof rule == "function" && (rule(proxy), required) || typeof rule == "object" && rule !== null && "_required" in rule && rule._required === "required") return !0; } return !1; } function isType(typeDef, typeName) { let type = typeDef; for (; type; ) { if (type.name === typeName || type.type && type.type.name === typeName) return !0; type = type.type; } return !1; } function isObjectType(typeDef) { return isType(typeDef, "object") || typeDef.jsonType === "object" || "fields" in typeDef; } function isArrayType(typeDef) { return isType(typeDef, "array"); } function isReferenceType(typeDef) { return isType(typeDef, "reference"); } function isCrossDatasetReferenceType(typeDef) { return isType(typeDef, "crossDatasetReference"); } function isStringType(typeDef) { return isType(typeDef, "string"); } function isNumberType(typeDef) { return isType(typeDef, "number"); } function lastType(typeDef) { let type = typeDef; for (; type; ) { if (!type.type) return type; type = type.type; } } function gatherFields(type) { return "fields" in type ? type.type ? gatherFields(type.type).concat(type.fields) : type.fields : []; } function sortByDependencies(compiledSchema) { const seen = /* @__PURE__ */ new Set(); function walkDependencies(schemaType, dependencies) { if (!seen.has(schemaType)) { if (seen.add(schemaType), "fields" in schemaType) for (const field of gatherFields(schemaType)) { const last = lastType(field.type); if (last.name === "document") { dependencies.add(last); continue; } let schemaTypeName; schemaType.type.type ? schemaTypeName = field.type.type.name : "jsonType" in schemaType.type && (schemaTypeName = field.type.jsonType), (schemaTypeName === "object" || schemaTypeName === "block") && (isReferenceType(field.type) ? field.type.to.forEach((ref) => dependencies.add(ref.type)) : dependencies.add(field.type)), walkDependencies(field.type, dependencies); } else if ("of" in schemaType) for (const item of schemaType.of) walkDependencies(item, dependencies); } } const dependencyMap = /* @__PURE__ */ new Map(); compiledSchema.getTypeNames().forEach((typeName) => { const schemaType = compiledSchema.get(typeName); if (schemaType === void 0 || schemaType.type === null) return; const dependencies = /* @__PURE__ */ new Set(); walkDependencies(schemaType, dependencies), dependencyMap.set(schemaType, dependencies), seen.clear(); }); const typeNames = [], currentlyVisiting = /* @__PURE__ */ new Set(), visited = /* @__PURE__ */ new Set(); function visit(type) { if (visited.has(type) || currentlyVisiting.has(type)) return; currentlyVisiting.add(type); const deps = dependencyMap.get(type); deps !== void 0 && deps.forEach((dep) => visit(dep)), currentlyVisiting.delete(type), visited.add(type), typeNames.includes(type.name) || typeNames.unshift(type.name); } for (const [type] of dependencyMap) visit(type); return typeNames; } const SchemaIcon = function(t0) { const $ = c(6), { schemaType, theme: themeContext } = t0, { theme, scheme, tone } = themeContext; let t1; $[0] === Symbol.for("react.memo_cache_sentinel") ? (t1 = new ServerStyleSheet(), $[0] = t1) : t1 = $[0]; const sheet = t1, Icon = schemaType.icon; let t2; return $[1] !== Icon || $[2] !== scheme || $[3] !== theme || $[4] !== tone ? (t2 = Icon ? /* @__PURE__ */ jsx(StyleSheetManager, { sheet: sheet.instance, children: /* @__PURE__ */ jsx(ThemeProvider, { theme, scheme, tone, children: isValidElement(Icon) ? Icon : /* @__PURE__ */ jsx(Icon, {}) }) }) : null, $[1] = Icon, $[2] = scheme, $[3] = theme, $[4] = tone, $[5] = t2) : t2 = $[5], t2; }, documentDefaultFields = (typeName) => ({ _id: { type: "objectField", name: "_id", value: { type: "string" } }, _type: { type: "objectField", name: "_type", value: { type: "string", value: typeName } }, _createdAt: { type: "objectField", name: "_createdAt", value: { type: "string" } }, _updatedAt: { type: "objectField", name: "_updatedAt", value: { type: "string" } }, _rev: { type: "objectField", name: "_rev", value: { type: "string" } } }); function createStringNodeDefintion(stringSchemaType) { const listOptions = stringSchemaType.options?.list; return listOptions && Array.isArray(listOptions) ? { type: "union", of: listOptions.map((v) => ({ type: "string", value: typeof v == "string" ? v : v.value })) } : { type: "string" }; } function createNumberNodeDefintion(numberSchemaType) { const listOptions = numberSchemaType.options?.list; return listOptions && Array.isArray(listOptions) ? { type: "union", of: listOptions.map((v) => ({ type: "number", value: typeof v == "number" ? v : v.value })) } : { type: "number" }; } function createReferenceNode(name, inArray = !1) { const fields = { _ref: { type: "objectField", name: "_ref", value: { type: "string" } }, _type: { type: "objectField", name: "_type", value: { type: "string", value: "reference" } }, _weak: { type: "objectField", name: "_weak", value: { type: "boolean" }, optional: !0 } }; return inArray && (fields._key = { type: "objectField", name: "_key", value: { type: "string" } }), { type: "object", fields, dereferencesTo: name }; } function createReferenceNodeDefintion(reference) { const references = gatherReferenceNames(reference); return references.length === 1 ? createReferenceNode(references[0]) : { type: "union", of: references.map((name) => ({ type: "unionOption", name, value: createReferenceNode(name) })) }; } function gatherReferenceNames(type) { const allReferences = gatherReferenceTypes(type); return [...new Set(allReferences.map((ref) => ref.name))]; } function gatherReferenceTypes(type) { const refTo = "to" in type ? type.to : []; return "type" in type && isReferenceType(type.type) ? [...gatherReferenceTypes(type.type), ...refTo] : refTo; } const typesMap = /* @__PURE__ */ new Map([["text", { type: "string" }], ["url", { type: "string" }], ["datetime", { type: "string" }], ["date", { type: "string" }], ["boolean", { type: "boolean" }], ["email", { type: "string" }]]); function extractSchema(workspace, theme) { const inlineFields = /* @__PURE__ */ new Set(), { schema: schemaDef, basePath } = workspace; return sortByDependencies(schemaDef).map((typeName) => { const schemaType = schemaDef.get(typeName); if (schemaType === void 0) return; const base = convertBaseType(schemaType); if (base !== null) return base.type === "type" && inlineFields.add(schemaType), base; }).filter((type) => type !== void 0); function extractIcon(schemaType) { if (schemaType.icon) return renderToString(/* @__PURE__ */ jsx(SchemaIcon, { schemaType, theme })); } function convertBaseType(schemaType) { let typeName; if (schemaType.type ? typeName = schemaType.type.name : "jsonType" in schemaType && (typeName = schemaType.jsonType), typeName === "document") { const object = createObject(schemaType); return object.type === "unknown" ? null : { type: "document", name: schemaType.name, title: typeof schemaType.title == "string" ? schemaType.title : void 0, icon: extractIcon(schemaType), fields: { ...documentDefaultFields(schemaType.name), ...object.fields } }; } const value = convertSchemaType(schemaType); return value.type === "unknown" ? null : value.type === "object" ? { name: schemaType.name, type: "type", value: { type: "object", fields: { _type: { type: "objectField", name: "_type", value: { type: "string", value: schemaType.name } }, ...value.fields } } } : { name: schemaType.name, title: typeof schemaType.title == "string" ? schemaType.title : void 0, type: "type", value }; } function createObject(schemaType) { const fields = {}; for (const field of gatherFields(schemaType)) { const value = convertSchemaType(field.type); value !== null && (fields[field.name] = { type: "objectField", name: field.name, title: typeof field.type.title == "string" ? field.type.title : void 0, value, optional: isFieldRequired(field) === !1 }); } return { type: "object", fields }; } function convertSchemaType(schemaType) { if (lastType(schemaType)?.name === "document") return createReferenceNode(schemaType.name); if (inlineFields.has(schemaType.type)) return { type: "inline", name: schemaType.type.name }; if (schemaType.type?.type?.name === "object") return { type: "inline", name: schemaType.type.name }; if (isStringType(schemaType)) return createStringNodeDefintion(schemaType); if (isNumberType(schemaType)) return createNumberNodeDefintion(schemaType); const mapped = typesMap.get(schemaType.type?.name || ""); if (mapped) return mapped; if (schemaType.type && typesMap.has(schemaType.type.name)) return typesMap.get(schemaType.type.name); if (isCrossDatasetReferenceType(schemaType)) return { type: "unknown" }; if (isReferenceType(schemaType)) return createReferenceNodeDefintion(schemaType); if (isArrayType(schemaType)) return createArray(schemaType); if (isObjectType(schemaType)) return createObject(schemaType); throw new Error(`Type "${schemaType.name}" not found`); } function createUnionNodeOptions(schemaType, of) { const { options } = schemaType; if (!options) return; const opts = { ...options }; return options.insertMenu && (opts.insertMenu = { ...options.insertMenu, views: options.insertMenu.views?.map((view) => view.name === "grid" ? { name: "grid", previewImageUrls: view.previewImageUrl ? of.reduce((acc, { name }) => { const url = view.previewImageUrl?.(name); if (!url) return acc; try { new URL(url), acc[name] = url; } catch { acc[name] = new URL(url, `${window.location.origin}${basePath ? `${basePath}/` : ""}`).toString(); } return acc; }, {}) : void 0 } : view) }), opts; } function createArray(arraySchemaType) { const of = []; for (const item of arraySchemaType.of) { let field = convertSchemaType(item); const option = { type: "unionOption", icon: extractIcon(item), name: item.name, title: typeof item.title == "string" ? item.title : void 0, value: field }; field.type === "inline" ? field = { type: "object", fields: { _key: createKeyField() }, rest: field } : field.type === "object" && (field.rest = { type: "object", fields: { _key: createKeyField() } }), option.value = field, of.push(option); } if (of.length === 0) return { type: "null" }; if (of.length > 1) return { type: "union", of, options: createUnionNodeOptions(arraySchemaType, of) }; const { name, title, value } = of[0]; return { type: "array", of: { type: "arrayItem", name, title: typeof title == "string" ? title : void 0, value } }; } } function createKeyField() { return { type: "objectField", name: "_key", value: { type: "string" } }; } function getDocumentPathArray(paths) { const documentPathMap = paths.reduce((acc, { id, path }) => (acc[id] ? acc[id].add(path) : acc[id] = /* @__PURE__ */ new Set([path]), acc), {}); return Object.entries(documentPathMap); } function PostMessageSchema(props) { const $ = c(12), { comlink, perspective } = props, workspace = useWorkspace(), theme = useRootTheme(); let t0, t1; $[0] !== comlink || $[1] !== theme || $[2] !== workspace ? (t0 = () => { try { const schema = extractSchema(workspace, theme); return comlink.post("presentation/schema", { schema }), comlink.on("visual-editing/schema", () => ({ schema })); } catch { return; } }, t1 = [comlink, theme, workspace], $[0] = comlink, $[1] = theme, $[2] = workspace, $[3] = t0, $[4] = t1) : (t0 = $[3], t1 = $[4]), useEffect(t0, t1); let t2; $[5] !== perspective ? (t2 = isReleasePerspective(perspective) ? RELEASES_STUDIO_CLIENT_OPTIONS : { apiVersion: API_VERSION }, $[5] = perspective, $[6] = t2) : t2 = $[6]; const client = useClient(t2); let t3, t4; return $[7] !== client || $[8] !== comlink || $[9] !== perspective ? (t3 = () => comlink.on("visual-editing/schema-union-types", async (data) => { const documentPathArray = getDocumentPathArray(data.paths), unionTypes = await Promise.all(documentPathArray.map(async (t5) => { const [id, paths] = t5, arr = Array.from(paths), query = `*[_id == $id][0]{${arr.map(_temp).join(",")}}`, result = await client.fetch(query, { id: getPublishedId(id) }, { tag: "presentation-schema", perspective }), mapped = arr.map((path_0, i_0) => ({ path: path_0, type: result[i_0] })); return { id, paths: mapped }; })), newState = /* @__PURE__ */ new Map(); return unionTypes.forEach((action) => { newState.set(action.id, new Map(action.paths.map(_temp2))); }), { types: newState }; }), t4 = [comlink, client, perspective], $[7] = client, $[8] = comlink, $[9] = perspective, $[10] = t3, $[11] = t4) : (t3 = $[10], t4 = $[11]), useEffect(t3, t4), null; } function _temp2(t0) { const { path: path_1, type } = t0; return [path_1, type]; } function _temp(path, i) { return `"${i}": ${path}[0]._type`; } var PostMessageSchema$1 = memo(PostMessageSchema); export { PostMessageSchema$1 as default }; //# sourceMappingURL=PostMessageSchema.mjs.map