diff --git a/packages/@sanity/block-tools/src/HtmlDeserializer/helpers.ts b/packages/@sanity/block-tools/src/HtmlDeserializer/helpers.ts
index 5e76801bfff..ef5adce6c63 100644
--- a/packages/@sanity/block-tools/src/HtmlDeserializer/helpers.ts
+++ b/packages/@sanity/block-tools/src/HtmlDeserializer/helpers.ts
@@ -1,4 +1,4 @@
-import {ArraySchemaType, Block, isBlock} from '@sanity/types'
+import {ArraySchemaType, PortableTextTextBlock, isPortableTextTextBlock} from '@sanity/types'
import {isEqual} from 'lodash'
import {DEFAULT_BLOCK} from '../constants'
import {resolveJsType} from '../util/resolveJsType'
@@ -87,7 +87,7 @@ export function flattenNestedBlocks(blocks: TypedObject[]): TypedObject[] {
if (depth === 0) {
flattened.push(node)
}
- if (isBlock(node)) {
+ if (isPortableTextTextBlock(node)) {
if (depth > 0) {
toRemove.push(node)
flattened.push(node)
@@ -109,12 +109,12 @@ export function flattenNestedBlocks(blocks: TypedObject[]): TypedObject[] {
return flattened
}
-function nextSpan(block: Block, index: number) {
+function nextSpan(block: PortableTextTextBlock, index: number) {
const next = block.children[index + 1]
return next && next._type === 'span' ? next : null
}
-function prevSpan(block: Block, index: number) {
+function prevSpan(block: PortableTextTextBlock, index: number) {
const prev = block.children[index - 1]
return prev && prev._type === 'span' ? prev : null
}
@@ -131,13 +131,13 @@ function isWhiteSpaceChar(text: string) {
*/
export function trimWhitespace(blocks: TypedObject[]): TypedObject[] {
blocks.forEach((block) => {
- if (!isBlock(block)) {
+ if (!isPortableTextTextBlock(block)) {
return
}
// eslint-disable-next-line complexity
block.children.forEach((child, index) => {
- if (child._type !== 'span') {
+ if (!isMinimalSpan(child)) {
return
}
const nextChild = nextSpan(block, index)
@@ -151,6 +151,7 @@ export function trimWhitespace(blocks: TypedObject[]): TypedObject[] {
if (
/\s/.test(child.text.substring(child.text.length - 1)) &&
nextChild &&
+ isMinimalSpan(nextChild) &&
/\s/.test(nextChild.text.substring(0, 1))
) {
child.text = child.text.replace(/[^\S\n]+$/g, '')
@@ -158,6 +159,7 @@ export function trimWhitespace(blocks: TypedObject[]): TypedObject[] {
if (
/\s/.test(child.text.substring(0, 1)) &&
prevChild &&
+ isMinimalSpan(prevChild) &&
/\s/.test(prevChild.text.substring(prevChild.text.length - 1))
) {
child.text = child.text.replace(/^[^\S\n]+/g, '')
@@ -195,7 +197,11 @@ export function ensureRootIsBlocks(blocks: TypedObject[]): TypedObject[] {
}
const lastBlock = memo[memo.length - 1]
- if (i > 0 && !isBlock(original[i - 1]) && isBlock(lastBlock)) {
+ if (
+ i > 0 &&
+ !isPortableTextTextBlock(original[i - 1]) &&
+ isPortableTextTextBlock(lastBlock)
+ ) {
lastBlock.children.push(node)
return memo
}
diff --git a/packages/@sanity/block-tools/src/HtmlDeserializer/index.ts b/packages/@sanity/block-tools/src/HtmlDeserializer/index.ts
index 20da7c0e96c..980aa171179 100644
--- a/packages/@sanity/block-tools/src/HtmlDeserializer/index.ts
+++ b/packages/@sanity/block-tools/src/HtmlDeserializer/index.ts
@@ -1,4 +1,9 @@
-import type {ArraySchemaType, Block, MarkDefinition} from '@sanity/types'
+import type {
+ ArraySchemaType,
+ PortableTextBlock,
+ PortableTextObject,
+ PortableTextTextBlock,
+} from '@sanity/types'
import {flatten} from 'lodash'
import {findBlockType} from '../util/findBlockType'
import {resolveJsType} from '../util/resolveJsType'
@@ -34,7 +39,7 @@ export default class HtmlDeserializer {
blockContentType: ArraySchemaType
rules: DeserializerRule[]
parseHtml: (html: string) => HTMLElement
- _markDefs: MarkDefinition[] = []
+ _markDefs: PortableTextObject[] = []
/**
* Create a new serializer respecting a Sanity block content type's schema
@@ -75,7 +80,7 @@ export default class HtmlDeserializer {
if (this._markDefs.length > 0) {
blocks
- .filter((block): block is Block => block._type === 'block')
+ .filter((block): block is PortableTextTextBlock => block._type === 'block')
.forEach((block) => {
block.markDefs = block.markDefs || []
block.markDefs = block.markDefs.concat(
@@ -216,7 +221,7 @@ export default class HtmlDeserializer {
// Only apply marks if this is an actual text
node.marks.unshift(name)
}
- } else if ('children' in node && Array.isArray((node as Block).children)) {
+ } else if ('children' in node && Array.isArray((node as PortableTextBlock).children)) {
const block = node as any
block.children = block.children.map(applyDecorator)
}
@@ -251,7 +256,7 @@ export default class HtmlDeserializer {
// Only apply marks if this is an actual text
node.marks.unshift(markDef._key)
}
- } else if ('children' in node && Array.isArray((node as Block).children)) {
+ } else if ('children' in node && Array.isArray((node as PortableTextBlock).children)) {
const block = node as any
block.children = block.children.map(applyAnnotation)
}
diff --git a/packages/@sanity/block-tools/src/index.ts b/packages/@sanity/block-tools/src/index.ts
index 499ac6df217..b052befa439 100644
--- a/packages/@sanity/block-tools/src/index.ts
+++ b/packages/@sanity/block-tools/src/index.ts
@@ -1,4 +1,4 @@
-import type {ArraySchemaType, Block, Span} from '@sanity/types'
+import type {ArraySchemaType, PortableTextTextBlock} from '@sanity/types'
import blockContentTypeFeatures from './util/blockContentTypeFeatures'
import HtmlDeserializer from './HtmlDeserializer'
import {normalizeBlock} from './util/normalizeBlock'
@@ -17,7 +17,7 @@ export function htmlToBlocks(
html: string,
blockContentType: ArraySchemaType,
options: HtmlDeserializerOptions = {}
-): (TypedObject | Block)[] {
+): (TypedObject | PortableTextTextBlock)[] {
const deserializer = new HtmlDeserializer(blockContentType, options)
return deserializer.deserialize(html).map((block) => normalizeBlock(block))
}
diff --git a/packages/@sanity/block-tools/src/types.ts b/packages/@sanity/block-tools/src/types.ts
index 0e9daf93b92..9d67609ea39 100644
--- a/packages/@sanity/block-tools/src/types.ts
+++ b/packages/@sanity/block-tools/src/types.ts
@@ -1,8 +1,8 @@
import type {ComponentType} from 'react'
import type {
ArraySchemaType,
- MarkDefinition,
ObjectSchemaType,
+ PortableTextObject,
SpanSchemaType,
TitledListValue,
} from '@sanity/types'
@@ -67,7 +67,7 @@ export interface MinimalSpan {
export interface MinimalBlock extends TypedObject {
_type: 'block'
children: TypedObject[]
- markDefs?: string[]
+ markDefs?: TypedObject[]
style?: string
level?: number
listItem?: string
@@ -81,7 +81,7 @@ export interface PlaceholderDecorator {
export interface PlaceholderAnnotation {
_type: '__annotation'
- markDef: MarkDefinition
+ markDef: PortableTextObject
children: TypedObject[]
}
diff --git a/packages/@sanity/block-tools/src/util/blockContentTypeFeatures.ts b/packages/@sanity/block-tools/src/util/blockContentTypeFeatures.ts
index c57115a4bbc..049ee283cb3 100644
--- a/packages/@sanity/block-tools/src/util/blockContentTypeFeatures.ts
+++ b/packages/@sanity/block-tools/src/util/blockContentTypeFeatures.ts
@@ -3,10 +3,10 @@ import {
BlockSchemaType,
EnumListProps,
isBlockChildrenObjectField,
+ isBlockListObjectField,
isBlockSchemaType,
- isListObjectField,
+ isBlockStyleObjectField,
isObjectSchemaType,
- isStyleObjectField,
isTitledListValue,
ObjectSchemaType,
SpanSchemaType,
@@ -63,7 +63,7 @@ export default function blockContentFeatures(
}
function resolveEnabledStyles(blockType: BlockSchemaType): TitledListValue[] {
- const styleField = blockType.fields.find(isStyleObjectField)
+ const styleField = blockType.fields.find(isBlockStyleObjectField)
if (!styleField) {
throw new Error("A field with name 'style' is not defined in the block type (required).")
}
@@ -81,7 +81,6 @@ function resolveEnabledStyles(blockType: BlockSchemaType): TitledListValue ({
- blockEditor: annotation.blockEditor,
title: annotation.title,
type: annotation,
value: annotation.name,
@@ -94,7 +93,7 @@ function resolveEnabledDecorators(spanType: SpanSchemaType): TitledListValue[] {
- const listField = blockType.fields.find(isListObjectField)
+ const listField = blockType.fields.find(isBlockListObjectField)
if (!listField) {
throw new Error("A field with name 'list' is not defined in the block type (required).")
}
diff --git a/packages/@sanity/block-tools/src/util/normalizeBlock.ts b/packages/@sanity/block-tools/src/util/normalizeBlock.ts
index d33f5224bee..ecf8b00413b 100644
--- a/packages/@sanity/block-tools/src/util/normalizeBlock.ts
+++ b/packages/@sanity/block-tools/src/util/normalizeBlock.ts
@@ -1,4 +1,4 @@
-import {Block, Span, isSpan} from '@sanity/types'
+import {PortableTextTextBlock, PortableTextSpan, isPortableTextSpan} from '@sanity/types'
import {isEqual} from 'lodash'
import {TypedObject} from '../types'
import {randomKey} from './randomKey'
@@ -38,12 +38,14 @@ export interface BlockNormalizationOptions {
export function normalizeBlock(
node: TypedObject,
options: BlockNormalizationOptions = {}
-): Omit, '_key'> & {_key: string} {
+): Omit, '_key'> & {
+ _key: string
+} {
if (node._type !== (options.blockTypeName || 'block')) {
return '_key' in node ? (node as TypedObject & {_key: string}) : {...node, _key: randomKey(12)}
}
- const block: Omit, 'style'> = {
+ const block: Omit, 'style'> = {
_key: randomKey(12),
children: [],
markDefs: [],
@@ -75,8 +77,8 @@ export function normalizeBlock(
const previousChild = acc[acc.length - 1]
if (
previousChild &&
- isSpan(child) &&
- isSpan(previousChild) &&
+ isPortableTextSpan(child) &&
+ isPortableTextSpan(previousChild) &&
isEqual(previousChild.marks, child.marks)
) {
if (lastChild && lastChild === child && child.text === '' && block.children.length > 1) {
@@ -88,20 +90,20 @@ export function normalizeBlock(
}
acc.push(child)
return acc
- }, [] as (TypedObject | Span)[])
+ }, [] as (TypedObject | PortableTextSpan)[])
.map((child, index) => {
if (!child) {
throw new Error('missing child')
}
child._key = `${block._key}${index}`
- if (isSpan(child)) {
+ if (isPortableTextSpan(child)) {
if (!child.marks) {
child.marks = []
} else if (allowedDecorators) {
child.marks = child.marks.filter((mark) => {
const isAllowed = allowedDecorators.includes(mark)
- const isUsed = block.markDefs.some((def) => def._key === mark)
+ const isUsed = block.markDefs?.some((def) => def._key === mark)
return isAllowed || isUsed
})
}
@@ -113,6 +115,6 @@ export function normalizeBlock(
})
// Remove leftover (unused) markDefs
- block.markDefs = block.markDefs.filter((markDef) => usedMarkDefs.includes(markDef._key))
+ block.markDefs = (block.markDefs || []).filter((markDef) => usedMarkDefs.includes(markDef._key))
return block
}
diff --git a/packages/@sanity/block-tools/test/tests/util/__snapshots__/blockContentTypeFeatures.test.ts.snap b/packages/@sanity/block-tools/test/tests/util/__snapshots__/blockContentTypeFeatures.test.ts.snap
index 1a173c5f68c..11cebc01a2b 100644
--- a/packages/@sanity/block-tools/test/tests/util/__snapshots__/blockContentTypeFeatures.test.ts.snap
+++ b/packages/@sanity/block-tools/test/tests/util/__snapshots__/blockContentTypeFeatures.test.ts.snap
@@ -4,7 +4,6 @@ exports[`blockContentTypeFeatures will give a sane feature set for the default s
Object {
"annotations": Array [
Object {
- "blockEditor": undefined,
"icon": undefined,
"title": "Link",
"type": Object {
@@ -1069,7 +1068,6 @@ exports[`blockContentTypeFeatures will give spesific features for a custom schem
Object {
"annotations": Array [
Object {
- "blockEditor": undefined,
"icon": undefined,
"title": "Author",
"type": Object {