From 43f0b61f207d40712c4fd0121a12f20806a39190 Mon Sep 17 00:00:00 2001 From: Emma Hamilton Date: Tue, 5 Sep 2023 15:26:07 +1000 Subject: [PATCH] Improve bundle size with reader API in React server component environments (#618) --- .changeset/witty-olives-obey.md | 5 +++ packages/keystatic/package.json | 12 +++++++ packages/keystatic/src/app/useItemData.ts | 2 +- .../blank-for-react-server.tsx | 1 + .../component-blocks/cloud-image-preview.tsx | 2 -- .../src/component-blocks/cloud-image.tsx | 2 +- .../keystatic/src/form/error-formatting.ts | 32 +++++++++++++++++ packages/keystatic/src/form/errors.ts | 34 ++----------------- .../src/form/fields/blocks/index.tsx | 2 +- .../keystatic/src/form/fields/blocks/ui.tsx | 1 - .../{checkbox.tsx => checkbox/index.tsx} | 20 +++++------ .../keystatic/src/form/fields/checkbox/ui.tsx | 21 ++++++++++++ .../src/form/fields/cloudImage/index.tsx | 2 +- .../src/form/fields/cloudImage/ui.tsx | 2 -- .../keystatic/src/form/fields/date/index.tsx | 2 +- .../keystatic/src/form/fields/date/ui.tsx | 1 - .../src/form/fields/datetime/index.tsx | 2 +- .../keystatic/src/form/fields/datetime/ui.tsx | 2 +- .../primitives/blank-for-react-server.tsx | 3 ++ .../DocumentEditor/primitives/index.tsx | 1 - .../src/form/fields/document/index.tsx | 2 +- .../keystatic/src/form/fields/document/ui.tsx | 2 -- .../src/form/fields/empty-field-ui.tsx | 30 ++++++++++++++++ .../keystatic/src/form/fields/file/index.tsx | 2 +- .../keystatic/src/form/fields/file/ui.tsx | 1 - .../keystatic/src/form/fields/image/index.tsx | 2 +- .../keystatic/src/form/fields/image/ui.tsx | 1 - .../src/form/fields/integer/index.tsx | 2 +- .../keystatic/src/form/fields/integer/ui.tsx | 1 - .../fields/markdoc/editor/editor-state.tsx | 1 - .../fields/markdoc/editor/markdoc/parse.ts | 1 - .../markdoc/editor/markdoc/serialize.ts | 1 - .../markdoc/editor/primitives/index.tsx | 1 - .../src/form/fields/markdoc/editor/schema.tsx | 1 - .../src/form/fields/markdoc/index.tsx | 26 +++++++------- .../keystatic/src/form/fields/markdoc/ui.tsx | 29 +++++++++++++++- .../src/form/fields/multiselect/index.tsx | 2 +- .../src/form/fields/multiselect/ui.tsx | 1 - .../keystatic/src/form/fields/object/ui.tsx | 1 - .../src/form/fields/pathReference/index.tsx | 2 +- .../src/form/fields/pathReference/ui.tsx | 1 - .../src/form/fields/relationship/index.tsx | 2 +- .../src/form/fields/relationship/ui.tsx | 1 - .../src/form/fields/select/index.tsx | 2 +- .../keystatic/src/form/fields/select/ui.tsx | 2 -- .../keystatic/src/form/fields/slug/index.tsx | 4 +-- .../keystatic/src/form/fields/slug/ui.tsx | 1 - .../keystatic/src/form/fields/text/index.tsx | 2 +- .../keystatic/src/form/fields/text/ui.tsx | 1 - .../keystatic/src/form/fields/url/index.tsx | 2 +- packages/keystatic/src/form/fields/url/ui.tsx | 1 - packages/keystatic/src/index.ts | 2 +- packages/keystatic/src/reader/index.ts | 2 +- 53 files changed, 177 insertions(+), 104 deletions(-) create mode 100644 .changeset/witty-olives-obey.md create mode 100644 packages/keystatic/src/component-blocks/blank-for-react-server.tsx create mode 100644 packages/keystatic/src/form/error-formatting.ts rename packages/keystatic/src/form/fields/{checkbox.tsx => checkbox/index.tsx} (55%) create mode 100644 packages/keystatic/src/form/fields/checkbox/ui.tsx create mode 100644 packages/keystatic/src/form/fields/document/DocumentEditor/primitives/blank-for-react-server.tsx create mode 100644 packages/keystatic/src/form/fields/empty-field-ui.tsx diff --git a/.changeset/witty-olives-obey.md b/.changeset/witty-olives-obey.md new file mode 100644 index 000000000..7cd82baf9 --- /dev/null +++ b/.changeset/witty-olives-obey.md @@ -0,0 +1,5 @@ +--- +"@keystatic/core": patch +--- + +Fixed bundle size increase when using `@keystatic/core/reader` with React server components diff --git a/packages/keystatic/package.json b/packages/keystatic/package.json index fdf053e42..1ed518adf 100644 --- a/packages/keystatic/package.json +++ b/packages/keystatic/package.json @@ -284,6 +284,18 @@ "#sha1": { "node": "./src/sha1/node.ts", "default": "./src/sha1/webcrypto.ts" + }, + "#field-ui/*": { + "react-server": "./src/form/fields/empty-field-ui.tsx", + "default": "./src/form/fields/*/ui.tsx" + }, + "#component-block-primitives": { + "react-server": "./src/form/fields/document/DocumentEditor/primitives/blank-for-react-server.tsx", + "default": "./src/form/fields/document/DocumentEditor/primitives/index.tsx" + }, + "#cloud-image-preview": { + "react-server": "./src/component-blocks/blank-for-react-server.tsx", + "default": "./src/component-blocks/cloud-image-preview.tsx" } } } diff --git a/packages/keystatic/src/app/useItemData.ts b/packages/keystatic/src/app/useItemData.ts index 2b4844b0d..63aeab8a3 100644 --- a/packages/keystatic/src/app/useItemData.ts +++ b/packages/keystatic/src/app/useItemData.ts @@ -18,7 +18,7 @@ import { KEYSTATIC_CLOUD_HEADERS, MaybePromise, } from './utils'; -import { toFormattedFormDataError } from '../form/errors'; +import { toFormattedFormDataError } from '../form/error-formatting'; import { serializeRepoConfig } from './repo-config'; function parseEntry(args: UseItemDataArgs, files: Map) { diff --git a/packages/keystatic/src/component-blocks/blank-for-react-server.tsx b/packages/keystatic/src/component-blocks/blank-for-react-server.tsx new file mode 100644 index 000000000..8c346be41 --- /dev/null +++ b/packages/keystatic/src/component-blocks/blank-for-react-server.tsx @@ -0,0 +1 @@ +export function CloudImagePreview() {} diff --git a/packages/keystatic/src/component-blocks/cloud-image-preview.tsx b/packages/keystatic/src/component-blocks/cloud-image-preview.tsx index 41b699b0a..9f7d3e2d3 100644 --- a/packages/keystatic/src/component-blocks/cloud-image-preview.tsx +++ b/packages/keystatic/src/component-blocks/cloud-image-preview.tsx @@ -1,5 +1,3 @@ -'use client'; - import { useEffect, useState } from 'react'; import { useSelected, useSlateStatic } from 'slate-react'; import { useOverlayTriggerState } from '@react-stately/overlays'; diff --git a/packages/keystatic/src/component-blocks/cloud-image.tsx b/packages/keystatic/src/component-blocks/cloud-image.tsx index ffca968c2..49d1c1569 100644 --- a/packages/keystatic/src/component-blocks/cloud-image.tsx +++ b/packages/keystatic/src/component-blocks/cloud-image.tsx @@ -1,5 +1,5 @@ import { component } from '..'; -import { CloudImagePreview } from './cloud-image-preview'; +import { CloudImagePreview } from '#cloud-image-preview'; import { cloudImageSchema } from './cloud-image-schema'; /** @deprecated Experimental */ diff --git a/packages/keystatic/src/form/error-formatting.ts b/packages/keystatic/src/form/error-formatting.ts new file mode 100644 index 000000000..b5fbe0874 --- /dev/null +++ b/packages/keystatic/src/form/error-formatting.ts @@ -0,0 +1,32 @@ +import { FieldDataError } from './fields/error'; +import { PropValidationError } from './parse-props'; + +function flattenErrors(error: unknown): unknown[] { + if (error instanceof AggregateError) { + return error.errors.flatMap(flattenErrors); + } + return [error]; +} + +export function formatFormDataError(error: unknown) { + const flatErrors = flattenErrors(error); + + return flatErrors + .map(error => { + if (error instanceof PropValidationError) { + const path = error.path.join('.'); + return `${path}: ${ + error.cause instanceof FieldDataError + ? error.cause.message + : `Unexpected error: ${error.cause}` + }`; + } + return `Unexpected error: ${error}`; + }) + .join('\n'); +} + +export function toFormattedFormDataError(error: unknown) { + const formatted = formatFormDataError(error); + return new Error(`Field validation failed:\n` + formatted); +} diff --git a/packages/keystatic/src/form/errors.ts b/packages/keystatic/src/form/errors.ts index b3e816718..822552c94 100644 --- a/packages/keystatic/src/form/errors.ts +++ b/packages/keystatic/src/form/errors.ts @@ -1,40 +1,10 @@ import { getSlugFromState } from '../app/utils'; import { ComponentSchema } from './api'; import { SlugFieldInfo } from './fields/text/path-slug-context'; -import { FieldDataError } from './fields/error'; import { PropValidationError } from './parse-props'; import { ReadonlyPropPath } from './fields/document/DocumentEditor/component-blocks/utils'; import { validateArrayLength } from './validate-array-length'; - -function flattenErrors(error: unknown): unknown[] { - if (error instanceof AggregateError) { - return error.errors.flatMap(flattenErrors); - } - return [error]; -} - -export function formatFormDataError(error: unknown) { - const flatErrors = flattenErrors(error); - - return flatErrors - .map(error => { - if (error instanceof PropValidationError) { - const path = error.path.join('.'); - return `${path}: ${ - error.cause instanceof FieldDataError - ? error.cause.message - : `Unexpected error: ${error.cause}` - }`; - } - return `Unexpected error: ${error}`; - }) - .join('\n'); -} - -export function toFormattedFormDataError(error: unknown) { - const formatted = formatFormDataError(error); - return new Error(`Field validation failed:\n` + formatted); -} +import { toFormattedFormDataError } from './error-formatting'; export function clientSideValidateProp( schema: ComponentSchema, @@ -50,7 +20,7 @@ export function clientSideValidateProp( } } -export function validateValueWithSchema( +function validateValueWithSchema( schema: ComponentSchema, value: any, slugField: SlugFieldInfo | undefined, diff --git a/packages/keystatic/src/form/fields/blocks/index.tsx b/packages/keystatic/src/form/fields/blocks/index.tsx index dd0ab61e2..dcf21e97a 100644 --- a/packages/keystatic/src/form/fields/blocks/index.tsx +++ b/packages/keystatic/src/form/fields/blocks/index.tsx @@ -6,7 +6,7 @@ import { BasicFormField, fields, } from '../../api'; -import { BlocksFieldInput } from './ui'; +import { BlocksFieldInput } from '#field-ui/blocks'; export function blocks>( blocks: { diff --git a/packages/keystatic/src/form/fields/blocks/ui.tsx b/packages/keystatic/src/form/fields/blocks/ui.tsx index d5e1aea03..b545426d4 100644 --- a/packages/keystatic/src/form/fields/blocks/ui.tsx +++ b/packages/keystatic/src/form/fields/blocks/ui.tsx @@ -1,4 +1,3 @@ -'use client'; import { useLocalizedStringFormatter } from '@react-aria/i18n'; import { ActionButton, ButtonGroup, Button } from '@keystar/ui/button'; import { diff --git a/packages/keystatic/src/form/fields/checkbox.tsx b/packages/keystatic/src/form/fields/checkbox/index.tsx similarity index 55% rename from packages/keystatic/src/form/fields/checkbox.tsx rename to packages/keystatic/src/form/fields/checkbox/index.tsx index d08359406..bcf8b3f11 100644 --- a/packages/keystatic/src/form/fields/checkbox.tsx +++ b/packages/keystatic/src/form/fields/checkbox/index.tsx @@ -1,8 +1,7 @@ -import { Checkbox } from '@keystar/ui/checkbox'; -import { Text } from '@keystar/ui/typography'; -import { BasicFormField } from '../api'; -import { FieldDataError } from './error'; -import { basicFormFieldWithSimpleReaderParse } from './utils'; +import { CheckboxFieldInput } from '#field-ui/checkbox'; +import { BasicFormField } from '../../api'; +import { FieldDataError } from '../error'; +import { basicFormFieldWithSimpleReaderParse } from '../utils'; export function checkbox({ label, @@ -14,12 +13,13 @@ export function checkbox({ description?: string; }): BasicFormField { return basicFormFieldWithSimpleReaderParse({ - Input({ value, onChange, autoFocus }) { + Input(props) { return ( - - {label} - {description && {description}} - + ); }, defaultValue() { diff --git a/packages/keystatic/src/form/fields/checkbox/ui.tsx b/packages/keystatic/src/form/fields/checkbox/ui.tsx new file mode 100644 index 000000000..973d21d65 --- /dev/null +++ b/packages/keystatic/src/form/fields/checkbox/ui.tsx @@ -0,0 +1,21 @@ +import { FormFieldInputProps } from '../../api'; +import { Checkbox } from '@keystar/ui/checkbox'; +import { Text } from '@keystar/ui/typography'; + +export function CheckboxFieldInput( + props: FormFieldInputProps & { + label: string; + description?: string; + } +) { + return ( + + {props.label} + {props.description && {props.description}} + + ); +} diff --git a/packages/keystatic/src/form/fields/cloudImage/index.tsx b/packages/keystatic/src/form/fields/cloudImage/index.tsx index 010474f00..3a3e6c3ae 100644 --- a/packages/keystatic/src/form/fields/cloudImage/index.tsx +++ b/packages/keystatic/src/form/fields/cloudImage/index.tsx @@ -3,7 +3,7 @@ import { ObjectField } from '../../api'; import { integer } from '../integer'; import { object } from '../object'; import { text } from '../text'; -import { CloudImageFieldInput } from './ui'; +import { CloudImageFieldInput } from '#field-ui/cloudImage'; export function cloudImage({ label, diff --git a/packages/keystatic/src/form/fields/cloudImage/ui.tsx b/packages/keystatic/src/form/fields/cloudImage/ui.tsx index d16be9b66..37194909d 100644 --- a/packages/keystatic/src/form/fields/cloudImage/ui.tsx +++ b/packages/keystatic/src/form/fields/cloudImage/ui.tsx @@ -1,5 +1,3 @@ -'use client'; - import { useEffect, useId, useState } from 'react'; import { ActionButton, ClearButton, ToggleButton } from '@keystar/ui/button'; diff --git a/packages/keystatic/src/form/fields/date/index.tsx b/packages/keystatic/src/form/fields/date/index.tsx index 56723362a..1d2891031 100644 --- a/packages/keystatic/src/form/fields/date/index.tsx +++ b/packages/keystatic/src/form/fields/date/index.tsx @@ -5,7 +5,7 @@ import { assertRequired, basicFormFieldWithSimpleReaderParse, } from '../utils'; -import { DateFieldInput } from './ui'; +import { DateFieldInput } from '#field-ui/date'; import { validateDate } from './validateDate'; export function date({ diff --git a/packages/keystatic/src/form/fields/date/ui.tsx b/packages/keystatic/src/form/fields/date/ui.tsx index 4dc3a1bd9..189d83824 100644 --- a/packages/keystatic/src/form/fields/date/ui.tsx +++ b/packages/keystatic/src/form/fields/date/ui.tsx @@ -1,4 +1,3 @@ -'use client'; import { TextField } from '@keystar/ui/text-field'; import { useReducer } from 'react'; import { validateDate } from './validateDate'; diff --git a/packages/keystatic/src/form/fields/datetime/index.tsx b/packages/keystatic/src/form/fields/datetime/index.tsx index 3d0a496f7..117e8d37a 100644 --- a/packages/keystatic/src/form/fields/datetime/index.tsx +++ b/packages/keystatic/src/form/fields/datetime/index.tsx @@ -5,7 +5,7 @@ import { assertRequired, basicFormFieldWithSimpleReaderParse, } from '../utils'; -import { DatetimeFieldInput } from './ui'; +import { DatetimeFieldInput } from '#field-ui/datetime'; import { validateDatetime } from './validateDatetime'; export function datetime({ diff --git a/packages/keystatic/src/form/fields/datetime/ui.tsx b/packages/keystatic/src/form/fields/datetime/ui.tsx index cc5214f2d..d88574b73 100644 --- a/packages/keystatic/src/form/fields/datetime/ui.tsx +++ b/packages/keystatic/src/form/fields/datetime/ui.tsx @@ -1,5 +1,5 @@ //ui.tsx -'use client'; + import { TextField } from '@keystar/ui/text-field'; import { useReducer } from 'react'; import { validateDatetime } from './validateDatetime'; diff --git a/packages/keystatic/src/form/fields/document/DocumentEditor/primitives/blank-for-react-server.tsx b/packages/keystatic/src/form/fields/document/DocumentEditor/primitives/blank-for-react-server.tsx new file mode 100644 index 000000000..6e4d6bcbe --- /dev/null +++ b/packages/keystatic/src/form/fields/document/DocumentEditor/primitives/blank-for-react-server.tsx @@ -0,0 +1,3 @@ +export function BlockWrapper() {} +export function NotEditable() {} +export function ToolbarSeparator() {} diff --git a/packages/keystatic/src/form/fields/document/DocumentEditor/primitives/index.tsx b/packages/keystatic/src/form/fields/document/DocumentEditor/primitives/index.tsx index 8ba0012b0..7e59ea0b2 100644 --- a/packages/keystatic/src/form/fields/document/DocumentEditor/primitives/index.tsx +++ b/packages/keystatic/src/form/fields/document/DocumentEditor/primitives/index.tsx @@ -1,4 +1,3 @@ -'use client'; export { ActiveBlockPopoverProvider, BlockPopover, diff --git a/packages/keystatic/src/form/fields/document/index.tsx b/packages/keystatic/src/form/fields/document/index.tsx index 391a824f1..1d55c2a9d 100644 --- a/packages/keystatic/src/form/fields/document/index.tsx +++ b/packages/keystatic/src/form/fields/document/index.tsx @@ -17,7 +17,7 @@ import { FormFieldStoredValue, } from '../../api'; import { text } from '../text'; -import { DocumentFieldInput } from './ui'; +import { DocumentFieldInput } from '#field-ui/document'; import { createDocumentEditorForNormalization } from './DocumentEditor/create-editor'; import { object } from '../object'; import { FieldDataError } from '../error'; diff --git a/packages/keystatic/src/form/fields/document/ui.tsx b/packages/keystatic/src/form/fields/document/ui.tsx index 6d1b01d84..faa822aac 100644 --- a/packages/keystatic/src/form/fields/document/ui.tsx +++ b/packages/keystatic/src/form/fields/document/ui.tsx @@ -1,5 +1,3 @@ -'use client'; - import { Field, FieldProps } from '@keystar/ui/field'; import { useState } from 'react'; diff --git a/packages/keystatic/src/form/fields/empty-field-ui.tsx b/packages/keystatic/src/form/fields/empty-field-ui.tsx new file mode 100644 index 000000000..42efc1e1b --- /dev/null +++ b/packages/keystatic/src/form/fields/empty-field-ui.tsx @@ -0,0 +1,30 @@ +// this is used in react-server environments to avoid bundling UI when the reader API is used +// if you added a new field and get an error that there's missing a missing export here, +// you probably just need to add another empty export here + +function empty() { + throw new Error( + "unexpected call to function that shouldn't be called in React server component environment" + ); +} + +export let SlugFieldInput = empty, + TextFieldInput = empty, + UrlFieldInput = empty, + SelectFieldInput = empty, + RelationshipInput = empty, + PathReferenceInput = empty, + MultiselectFieldInput = empty, + IntegerFieldInput = empty, + ImageFieldInput = empty, + FileFieldInput = empty, + DatetimeFieldInput = empty, + DateFieldInput = empty, + CloudImageFieldInput = empty, + BlocksFieldInput = empty, + DocumentFieldInput = empty, + CheckboxFieldInput = empty, + createEditorSchema = empty, + getDefaultValue = empty, + parseToEditorState = empty, + serializeFromEditorState = empty; diff --git a/packages/keystatic/src/form/fields/file/index.tsx b/packages/keystatic/src/form/fields/file/index.tsx index 9201acee2..d0be9302e 100644 --- a/packages/keystatic/src/form/fields/file/index.tsx +++ b/packages/keystatic/src/form/fields/file/index.tsx @@ -3,7 +3,7 @@ import { AssetFormField } from '../../api'; import { FieldDataError } from '../error'; import { getSrcPrefix } from '../image/getSrcPrefix'; import { RequiredValidation, assertRequired } from '../utils'; -import { FileFieldInput } from './ui'; +import { FileFieldInput } from '#field-ui/file'; export function file({ label, diff --git a/packages/keystatic/src/form/fields/file/ui.tsx b/packages/keystatic/src/form/fields/file/ui.tsx index 60a97e76a..16cd65ef6 100644 --- a/packages/keystatic/src/form/fields/file/ui.tsx +++ b/packages/keystatic/src/form/fields/file/ui.tsx @@ -1,4 +1,3 @@ -'use client'; import { ButtonGroup, ActionButton, Button } from '@keystar/ui/button'; import { FieldLabel, FieldMessage } from '@keystar/ui/field'; import { Flex } from '@keystar/ui/layout'; diff --git a/packages/keystatic/src/form/fields/image/index.tsx b/packages/keystatic/src/form/fields/image/index.tsx index 251e5ae7e..57fc53a40 100644 --- a/packages/keystatic/src/form/fields/image/index.tsx +++ b/packages/keystatic/src/form/fields/image/index.tsx @@ -3,7 +3,7 @@ import { AssetFormField } from '../../api'; import { FieldDataError } from '../error'; import { RequiredValidation, assertRequired } from '../utils'; import { getSrcPrefix } from './getSrcPrefix'; -import { ImageFieldInput } from './ui'; +import { ImageFieldInput } from '#field-ui/image'; export function image({ label, diff --git a/packages/keystatic/src/form/fields/image/ui.tsx b/packages/keystatic/src/form/fields/image/ui.tsx index 173c1a805..62670871e 100644 --- a/packages/keystatic/src/form/fields/image/ui.tsx +++ b/packages/keystatic/src/form/fields/image/ui.tsx @@ -1,4 +1,3 @@ -'use client'; import { ButtonGroup, ActionButton } from '@keystar/ui/button'; import { FieldLabel, FieldMessage } from '@keystar/ui/field'; import { Flex, Box } from '@keystar/ui/layout'; diff --git a/packages/keystatic/src/form/fields/integer/index.tsx b/packages/keystatic/src/form/fields/integer/index.tsx index fc1fe3c85..ac07615ea 100644 --- a/packages/keystatic/src/form/fields/integer/index.tsx +++ b/packages/keystatic/src/form/fields/integer/index.tsx @@ -5,7 +5,7 @@ import { assertRequired, basicFormFieldWithSimpleReaderParse, } from '../utils'; -import { IntegerFieldInput } from './ui'; +import { IntegerFieldInput } from '#field-ui/integer'; import { validateInteger } from './validateInteger'; export function integer({ diff --git a/packages/keystatic/src/form/fields/integer/ui.tsx b/packages/keystatic/src/form/fields/integer/ui.tsx index fbcbe3b26..cb430c163 100644 --- a/packages/keystatic/src/form/fields/integer/ui.tsx +++ b/packages/keystatic/src/form/fields/integer/ui.tsx @@ -1,4 +1,3 @@ -'use client'; import { NumberField } from '@keystar/ui/number-field'; import { useReducer } from 'react'; import { validateInteger } from './validateInteger'; diff --git a/packages/keystatic/src/form/fields/markdoc/editor/editor-state.tsx b/packages/keystatic/src/form/fields/markdoc/editor/editor-state.tsx index 1466d4c77..eff34e401 100644 --- a/packages/keystatic/src/form/fields/markdoc/editor/editor-state.tsx +++ b/packages/keystatic/src/form/fields/markdoc/editor/editor-state.tsx @@ -1,4 +1,3 @@ -'use client'; import { EditorState, Selection } from 'prosemirror-state'; import { history } from 'prosemirror-history'; import { keymap } from 'prosemirror-keymap'; diff --git a/packages/keystatic/src/form/fields/markdoc/editor/markdoc/parse.ts b/packages/keystatic/src/form/fields/markdoc/editor/markdoc/parse.ts index 3d05ca804..e1e8315ab 100644 --- a/packages/keystatic/src/form/fields/markdoc/editor/markdoc/parse.ts +++ b/packages/keystatic/src/form/fields/markdoc/editor/markdoc/parse.ts @@ -1,4 +1,3 @@ -'use client'; import { Ast, Node as MarkdocNode, ValidateError } from '@markdoc/markdoc'; import { Mark, diff --git a/packages/keystatic/src/form/fields/markdoc/editor/markdoc/serialize.ts b/packages/keystatic/src/form/fields/markdoc/editor/markdoc/serialize.ts index 714e4a144..f81d5875a 100644 --- a/packages/keystatic/src/form/fields/markdoc/editor/markdoc/serialize.ts +++ b/packages/keystatic/src/form/fields/markdoc/editor/markdoc/serialize.ts @@ -1,4 +1,3 @@ -'use client'; import { Ast, Node as MarkdocNode, NodeType } from '@markdoc/markdoc'; import { Fragment, Mark, Node as ProseMirrorNode } from 'prosemirror-model'; import { getEditorSchema } from '../schema'; diff --git a/packages/keystatic/src/form/fields/markdoc/editor/primitives/index.tsx b/packages/keystatic/src/form/fields/markdoc/editor/primitives/index.tsx index cc51375d8..e79304462 100644 --- a/packages/keystatic/src/form/fields/markdoc/editor/primitives/index.tsx +++ b/packages/keystatic/src/form/fields/markdoc/editor/primitives/index.tsx @@ -1,4 +1,3 @@ -'use client'; export { BlockPopover } from './BlockPopover'; export * from './toolbar'; diff --git a/packages/keystatic/src/form/fields/markdoc/editor/schema.tsx b/packages/keystatic/src/form/fields/markdoc/editor/schema.tsx index 37db1d578..3b8d382e5 100644 --- a/packages/keystatic/src/form/fields/markdoc/editor/schema.tsx +++ b/packages/keystatic/src/form/fields/markdoc/editor/schema.tsx @@ -1,4 +1,3 @@ -'use client'; import { css, tokenSchema } from '@keystar/ui/style'; import { DOMOutputSpec, diff --git a/packages/keystatic/src/form/fields/markdoc/index.tsx b/packages/keystatic/src/form/fields/markdoc/index.tsx index 50d094ed0..1d4a37f98 100644 --- a/packages/keystatic/src/form/fields/markdoc/index.tsx +++ b/packages/keystatic/src/form/fields/markdoc/index.tsx @@ -4,14 +4,16 @@ import Markdoc, { } from '@markdoc/markdoc'; import { ContentFormField } from '../../api'; -import { DocumentFieldInput } from './ui'; -import { EditorState } from 'prosemirror-state'; -import { createEditorState } from './editor/editor-state'; -import { EditorSchema, createEditorSchema } from './editor/schema'; -import { proseMirrorToMarkdoc } from './editor/markdoc/serialize'; -import { markdocToProseMirror } from './editor/markdoc/parse'; +import { + DocumentFieldInput, + getDefaultValue, + serializeFromEditorState, + createEditorSchema, + parseToEditorState, +} from '#field-ui/markdoc'; +import type { EditorSchema } from './editor/schema'; +import type { EditorState } from 'prosemirror-state'; -const textEncoder = new TextEncoder(); const textDecoder = new TextDecoder(); /** @@ -37,7 +39,7 @@ export function __experimental_markdoc_field({ kind: 'form', formKind: 'content', defaultValue() { - return createEditorState(getSchema().nodes.doc.createAndFill()!); + return getDefaultValue(getSchema()); }, Input(props) { return ( @@ -50,19 +52,15 @@ export function __experimental_markdoc_field({ }, parse: (_, { content }) => { - const markdoc = textDecoder.decode(content); - const doc = markdocToProseMirror(Markdoc.parse(markdoc), getSchema()); - return createEditorState(doc); + return parseToEditorState(content, getSchema()); }, contentExtension: '.mdoc', validate(value) { return value; }, serialize(value) { - const markdocNode = proseMirrorToMarkdoc(value.doc); - const markdoc = Markdoc.format(markdocNode); return { - content: textEncoder.encode(Markdoc.format(Markdoc.parse(markdoc))), + content: serializeFromEditorState(value), external: new Map(), other: new Map(), value: undefined, diff --git a/packages/keystatic/src/form/fields/markdoc/ui.tsx b/packages/keystatic/src/form/fields/markdoc/ui.tsx index d9a56cd08..ac7d55d86 100644 --- a/packages/keystatic/src/form/fields/markdoc/ui.tsx +++ b/packages/keystatic/src/form/fields/markdoc/ui.tsx @@ -1,9 +1,36 @@ -'use client'; import { FieldPrimitive } from '@keystar/ui/field'; import { FormFieldInputProps } from '../../api'; import { EditorState } from 'prosemirror-state'; import { Editor } from './editor'; +import { createEditorState } from './editor/editor-state'; +import { EditorSchema } from './editor/schema'; +import { markdocToProseMirror } from './editor/markdoc/parse'; +import Markdoc from '@markdoc/markdoc'; +import { proseMirrorToMarkdoc } from './editor/markdoc/serialize'; +export { createEditorSchema } from './editor/schema'; + +export function getDefaultValue(schema: EditorSchema) { + return createEditorState(schema.nodes.doc.createAndFill()!); +} + +const textDecoder = new TextDecoder(); +const textEncoder = new TextEncoder(); + +export function parseToEditorState( + content: Uint8Array | undefined, + schema: EditorSchema +) { + const markdoc = textDecoder.decode(content); + const doc = markdocToProseMirror(Markdoc.parse(markdoc), schema); + return createEditorState(doc); +} + +export function serializeFromEditorState(value: EditorState) { + const markdocNode = proseMirrorToMarkdoc(value.doc); + const markdoc = Markdoc.format(markdocNode); + return textEncoder.encode(Markdoc.format(Markdoc.parse(markdoc))); +} export function DocumentFieldInput( props: FormFieldInputProps & { label: string; diff --git a/packages/keystatic/src/form/fields/multiselect/index.tsx b/packages/keystatic/src/form/fields/multiselect/index.tsx index 5f98dd90c..502559a2d 100644 --- a/packages/keystatic/src/form/fields/multiselect/index.tsx +++ b/packages/keystatic/src/form/fields/multiselect/index.tsx @@ -1,7 +1,7 @@ import { BasicFormField } from '../../api'; import { FieldDataError } from '../error'; import { basicFormFieldWithSimpleReaderParse } from '../utils'; -import { MultiselectFieldInput } from './ui'; +import { MultiselectFieldInput } from '#field-ui/multiselect'; export function multiselect