From 81a972d966bc25b04dbd652647af1ef03341d942 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Jablo=C3=B1ski?= <43938777+GermanJablo@users.noreply.github.com> Date: Fri, 20 Sep 2024 15:42:30 -0300 Subject: [PATCH] chore(richtext-lexical): add strictNullChecks to the richtext-lexical package (#8295) This PR addresses around 500 TypeScript errors by enabling strictNullChecks in the richtext-lexical package. In the process, several bugs were identified and fixed. In some cases, I applied non-null assertions where necessary, although there may be room for further type refinement in the future. The focus of this PR is to resolve the immediate issues without introducing additional technical debt, rather than aiming for perfect type definitions at this stage. --------- Co-authored-by: Alessio Gravili --- packages/payload/src/fields/config/types.ts | 2 +- packages/payload/src/index.ts | 12 +- .../Forms/DynamicFieldSelector.tsx | 1 + packages/richtext-lexical/src/cell/index.tsx | 2 +- .../blockquote/markdownTransformer.ts | 2 +- .../blocks/client/component/BlockContent.tsx | 12 +- .../blocks/client/component/index.tsx | 20 ++- .../blocks/client/componentInline/index.tsx | 10 +- .../src/features/blocks/client/index.tsx | 44 +++-- .../blocks/client/nodes/BlocksNode.tsx | 8 +- .../features/blocks/client/plugin/index.tsx | 17 +- .../src/features/blocks/server/index.ts | 6 +- .../blocks/server/nodes/BlocksNode.tsx | 15 +- .../blocks/server/nodes/InlineBlocksNode.tsx | 4 +- .../src/features/blocks/server/validate.ts | 2 +- .../converters/html/converter/index.ts | 4 +- .../features/converters/html/field/index.ts | 4 +- .../testRecorder/client/plugin/index.tsx | 2 +- .../plugins/TableActionMenuPlugin/index.tsx | 4 +- .../experimental_table/server/index.ts | 4 +- .../server/nodes/HorizontalRuleNode.tsx | 4 +- .../src/features/indent/client/index.tsx | 3 +- .../src/features/link/client/index.tsx | 2 +- .../link/client/plugins/autoLink/index.tsx | 6 +- .../floatingLinkEditor/LinkEditor/index.tsx | 22 ++- .../src/features/link/nodes/AutoLinkNode.ts | 4 +- .../src/features/link/nodes/LinkNode.ts | 18 ++- .../src/features/link/nodes/types.ts | 6 +- .../src/features/link/server/baseFields.ts | 9 +- .../src/features/link/server/index.ts | 17 +- .../link/server/transformExtraFields.ts | 3 +- .../src/features/link/server/validate.ts | 2 +- .../src/features/lists/shared/markdown.ts | 2 +- .../lexicalPluginToLexical/converter/index.ts | 1 + .../nodes/unknownConvertedNode/index.tsx | 2 +- .../converters/blockquote/converter.ts | 2 +- .../converter/converters/heading/converter.ts | 2 +- .../converter/converters/link/converter.ts | 2 +- .../converters/listItem/converter.ts | 2 +- .../converters/orderedList/converter.ts | 2 +- .../converter/converters/unknown/converter.ts | 2 +- .../converters/unorderedList/converter.ts | 2 +- .../slateToLexical/converter/index.ts | 5 +- .../nodes/unknownConvertedNode/index.tsx | 2 +- .../components/RelationshipComponent.tsx | 18 ++- .../relationship/client/drawer/index.tsx | 8 +- .../relationship/client/plugins/index.tsx | 4 +- .../utils/EnabledRelationshipsCondition.tsx | 4 +- .../server/nodes/RelationshipNode.tsx | 2 +- .../toolbars/fixed/client/Toolbar/index.tsx | 30 ++-- .../toolbars/inline/client/Toolbar/index.tsx | 11 +- .../toolbars/shared/ToolbarButton/index.tsx | 5 +- .../shared/ToolbarDropdown/DropDown.tsx | 4 +- .../toolbars/shared/ToolbarDropdown/index.tsx | 3 + .../src/features/toolbars/types.ts | 2 +- .../src/features/typesServer.ts | 1 + .../upload/client/component/index.tsx | 6 +- .../features/upload/client/drawer/index.tsx | 2 + .../features/upload/client/plugin/index.tsx | 2 +- .../features/upload/server/feature.server.ts | 6 +- .../src/features/upload/server/validate.ts | 2 +- packages/richtext-lexical/src/field/Field.tsx | 18 +-- packages/richtext-lexical/src/field/index.tsx | 4 +- packages/richtext-lexical/src/index.ts | 153 +++++++++--------- .../src/lexical/EditorPlugin.tsx | 3 +- .../src/lexical/LexicalEditor.tsx | 14 +- .../config/client/EditorConfigProvider.tsx | 5 +- .../src/lexical/config/client/sanitize.ts | 4 +- .../src/lexical/config/server/loader.ts | 6 +- .../src/lexical/config/server/sanitize.ts | 26 +-- .../LexicalTypeaheadMenuPlugin/index.tsx | 6 +- .../LexicalTypeaheadMenuPlugin/types.ts | 2 +- .../src/lexical/plugins/SlashMenu/index.tsx | 15 +- .../handles/AddBlockHandlePlugin/index.tsx | 15 +- .../handles/DraggableBlockPlugin/debounce.ts | 4 +- .../getBoundingRectWithoutTransform.ts | 2 +- .../handles/DraggableBlockPlugin/index.tsx | 18 ++- .../handles/utils/getNodeCloseToPoint.ts | 11 +- .../lexical/utils/setFloatingElemPosition.ts | 2 +- .../src/populateGraphQL/populate.ts | 12 +- .../populateLexicalPopulationPromises.ts | 15 +- .../recursivelyPopulateFieldsForGraphQL.ts | 4 +- .../utilities/fieldsDrawer/DrawerContent.tsx | 9 +- .../src/utilities/generateComponentMap.tsx | 1 + .../migrateDocumentFieldsRecursively.ts | 23 +-- .../upgradeDocumentFieldsRecursively.ts | 18 +-- .../src/validate/validateNodes.ts | 8 +- packages/richtext-lexical/tsconfig.json | 1 + packages/ui/src/forms/NullifyField/index.tsx | 1 + 89 files changed, 447 insertions(+), 360 deletions(-) diff --git a/packages/payload/src/fields/config/types.ts b/packages/payload/src/fields/config/types.ts index 7d822e5d17..1876ce21ac 100644 --- a/packages/payload/src/fields/config/types.ts +++ b/packages/payload/src/fields/config/types.ts @@ -443,7 +443,7 @@ export interface FieldBaseClient { * Must not be one of reserved field names: ['__v', 'salt', 'hash', 'file'] * @link https://payloadcms.com/docs/fields/overview#field-names */ - name?: string + name: string required?: boolean saveToJWT?: boolean | string /** diff --git a/packages/payload/src/index.ts b/packages/payload/src/index.ts index 1a5a4bc6c4..8dacfd3a2a 100644 --- a/packages/payload/src/index.ts +++ b/packages/payload/src/index.ts @@ -63,8 +63,6 @@ import localOperations from './collections/operations/local/index.js' import { consoleEmailAdapter } from './email/consoleEmailAdapter.js' import { fieldAffectsData } from './fields/config/types.js' import localGlobalOperations from './globals/operations/local/index.js' -import { checkDependencies } from './utilities/dependencies/dependencyChecker.js' -import flattenFields from './utilities/flattenTopLevelFields.js' import { getLogger } from './utilities/logger.js' import { serverInit as serverInitTelemetry } from './utilities/telemetry/events/serverInit.js' import { traverseFields } from './utilities/traverseFields.js' @@ -91,6 +89,9 @@ export interface GeneratedTypes { collectionsUntyped: { [slug: string]: JsonObject & TypeWithID } + dbUntyped: { + defaultIDType: number | string + } globalsUntyped: { [slug: string]: JsonObject } @@ -115,6 +116,13 @@ type StringKeyOf = Extract // Define the types for slugs using the appropriate collections and globals export type CollectionSlug = StringKeyOf + +type ResolveDbType = 'db' extends keyof T + ? T['db'] + : // @ts-expect-error + T['dbUntyped'] + +export type DefaultDocumentIDType = ResolveDbType['defaultIDType'] export type GlobalSlug = StringKeyOf // now for locale and user diff --git a/packages/plugin-form-builder/src/collections/Forms/DynamicFieldSelector.tsx b/packages/plugin-form-builder/src/collections/Forms/DynamicFieldSelector.tsx index 309a4400fa..1994b9f8d4 100644 --- a/packages/plugin-form-builder/src/collections/Forms/DynamicFieldSelector.tsx +++ b/packages/plugin-form-builder/src/collections/Forms/DynamicFieldSelector.tsx @@ -40,6 +40,7 @@ export const DynamicFieldSelector: React.FC< (() => { - const clientFeatures: GeneratedFeatureProviderComponent[] = richTextComponentMap.get( + const clientFeatures: GeneratedFeatureProviderComponent[] = richTextComponentMap?.get( 'features', ) as GeneratedFeatureProviderComponent[] diff --git a/packages/richtext-lexical/src/features/blockquote/markdownTransformer.ts b/packages/richtext-lexical/src/features/blockquote/markdownTransformer.ts index 3713dc93a9..be9f3e2f1e 100644 --- a/packages/richtext-lexical/src/features/blockquote/markdownTransformer.ts +++ b/packages/richtext-lexical/src/features/blockquote/markdownTransformer.ts @@ -12,7 +12,7 @@ export const MarkdownTransformer: ElementTransformer = { } const lines = exportChildren(node).split('\n') - const output = [] + const output: string[] = [] for (const line of lines) { output.push('> ' + line) } diff --git a/packages/richtext-lexical/src/features/blocks/client/component/BlockContent.tsx b/packages/richtext-lexical/src/features/blocks/client/component/BlockContent.tsx index 0c84982302..4d3af4a0ff 100644 --- a/packages/richtext-lexical/src/features/blocks/client/component/BlockContent.tsx +++ b/packages/richtext-lexical/src/features/blocks/client/component/BlockContent.tsx @@ -21,8 +21,8 @@ import React, { useCallback } from 'react' import type { LexicalRichTextFieldProps } from '../../../../types.js' import type { BlockFields } from '../../server/nodes/BlocksNode.js' -import type { BlockNode } from '../nodes/BlocksNode.js' +import { $isBlockNode } from '../nodes/BlocksNode.js' import { FormSavePlugin } from './FormSavePlugin.js' type Props = { @@ -122,8 +122,8 @@ export const BlockContent: React.FC = (props) => { // ensure that the nested editor has finished its update cycle before we update the block node. setTimeout(() => { editor.update(() => { - const node: BlockNode = $getNodeByKey(nodeKey) - if (node) { + const node = $getNodeByKey(nodeKey) + if (node && $isBlockNode(node)) { formData = newFormData node.setFields(newFormData) } @@ -176,7 +176,7 @@ export const BlockContent: React.FC = (props) => { const removeBlock = useCallback(() => { editor.update(() => { - $getNodeByKey(nodeKey).remove() + $getNodeByKey(nodeKey)?.remove() }) }, [editor, nodeKey]) @@ -198,11 +198,11 @@ export const BlockContent: React.FC = (props) => { className={`${baseClass}__block-pill ${baseClass}__block-pill-${formData?.blockType}`} pillStyle="white" > - {typeof clientBlock?.labels.singular === 'string' + {typeof clientBlock?.labels?.singular === 'string' ? getTranslation(clientBlock?.labels.singular, i18n) : clientBlock.slug} - + {fieldHasErrors && } {editor.isEditable() && ( diff --git a/packages/richtext-lexical/src/features/blocks/client/component/index.tsx b/packages/richtext-lexical/src/features/blocks/client/component/index.tsx index 541e6457bc..623edd3955 100644 --- a/packages/richtext-lexical/src/features/blocks/client/component/index.tsx +++ b/packages/richtext-lexical/src/features/blocks/client/component/index.tsx @@ -1,7 +1,5 @@ 'use client' -import type { FormProps } from '@payloadcms/ui' - import { Collapsible, Form, @@ -33,7 +31,7 @@ import './index.scss' type Props = { readonly children?: React.ReactNode readonly formData: BlockFields - readonly nodeKey?: string + readonly nodeKey: string } export const BlockComponent: React.FC = (props) => { @@ -52,13 +50,16 @@ export const BlockComponent: React.FC = (props) => { const schemaFieldsPath = `${schemaPath}.lexical_internal_feature.blocks.lexical_blocks.lexical_blocks.${formData.blockType}` const componentMapRenderedBlockPath = `lexical_internal_feature.blocks.fields.lexical_blocks` - const blocksField: BlocksFieldClient = richTextComponentMap.get(componentMapRenderedBlockPath)[0] + const blocksField: BlocksFieldClient = richTextComponentMap?.get(componentMapRenderedBlockPath)[0] const clientBlock = blocksField.blocks.find((block) => block.slug === formData.blockType) // Field Schema useEffect(() => { const awaitInitialState = async () => { + if (!id) { + return + } const { state } = await getFormState({ apiRoute: config.routes.api, body: { @@ -87,8 +88,11 @@ export const BlockComponent: React.FC = (props) => { } }, [config.routes.api, config.serverURL, schemaFieldsPath, id]) // DO NOT ADD FORMDATA HERE! Adding formData will kick you out of sub block editors while writing. - const onChange: FormProps['onChange'][0] = useCallback( + const onChange = useCallback( async ({ formState: prevFormState }) => { + if (!id) { + throw new Error('No ID found') + } const { state: formState } = await getFormState({ apiRoute: config.routes.api, body: { @@ -155,13 +159,13 @@ export const BlockComponent: React.FC = (props) => { className={`${baseClass}__block-pill ${baseClass}__block-pill-${formData?.blockType}`} pillStyle="white" > - {clientBlock && typeof clientBlock.labels.singular === 'string' + {clientBlock && typeof clientBlock.labels?.singular === 'string' ? getTranslation(clientBlock.labels.singular, i18n) - : clientBlock.slug} + : clientBlock?.slug} diff --git a/packages/richtext-lexical/src/features/blocks/client/componentInline/index.tsx b/packages/richtext-lexical/src/features/blocks/client/componentInline/index.tsx index f7db7af968..1a30c6a99b 100644 --- a/packages/richtext-lexical/src/features/blocks/client/componentInline/index.tsx +++ b/packages/richtext-lexical/src/features/blocks/client/componentInline/index.tsx @@ -29,7 +29,7 @@ import './index.scss' type Props = { readonly formData: InlineBlockFields - readonly nodeKey?: string + readonly nodeKey: string } export const InlineBlockComponent: React.FC = (props) => { @@ -45,12 +45,12 @@ export const InlineBlockComponent: React.FC = (props) => { } = useEditorConfigContext() const componentMapRenderedBlockPath = `lexical_internal_feature.blocks.fields.lexical_inline_blocks` - const blocksField: BlocksFieldClient = richTextComponentMap.get(componentMapRenderedBlockPath)[0] + const blocksField: BlocksFieldClient = richTextComponentMap?.get(componentMapRenderedBlockPath)[0] const clientBlock = blocksField.blocks.find((block) => block.slug === formData.blockType) const removeInlineBlock = useCallback(() => { editor.update(() => { - $getNodeByKey(nodeKey).remove() + $getNodeByKey(nodeKey)?.remove() }) }, [editor, nodeKey]) @@ -103,7 +103,7 @@ export const InlineBlockComponent: React.FC = (props) => { const blockDisplayName = clientBlock?.labels?.singular ? getTranslation(clientBlock.labels.singular, i18n) - : clientBlock.slug + : clientBlock?.slug return (
= (props) => { mappedComponent={clientBlock.admin.components.Label} /> ) : ( -
{getTranslation(clientBlock.labels?.singular, i18n)}
+
{getTranslation(clientBlock!.labels!.singular, i18n)}
)} {editor.isEditable() && (
diff --git a/packages/richtext-lexical/src/features/blocks/client/index.tsx b/packages/richtext-lexical/src/features/blocks/client/index.tsx index 573522b46b..c11e2537b9 100644 --- a/packages/richtext-lexical/src/features/blocks/client/index.tsx +++ b/packages/richtext-lexical/src/features/blocks/client/index.tsx @@ -19,7 +19,7 @@ export type BlocksFeatureClientProps = { clientBlockSlugs: string[] clientInlineBlockSlugs: string[] } - +// @ts-expect-error - TODO: fix this export const BlocksFeatureClient = createClientFeature(({ props }) => ({ nodes: [BlockNode, InlineBlockNode], plugins: [ @@ -39,22 +39,25 @@ export const BlocksFeatureClient = createClientFeature key: 'block-' + blockSlug, keywords: ['block', 'blocks', blockSlug], label: ({ i18n, richTextComponentMap }) => { + if (!richTextComponentMap) { + return blockSlug + } + const componentMapRenderedBlockPath = `lexical_internal_feature.blocks.fields.lexical_blocks` const blocksField: BlocksFieldClient = richTextComponentMap.get( componentMapRenderedBlockPath, - )[0] + )?.[0] const clientBlock = blocksField.blocks.find((_block) => _block.slug === blockSlug) - const blockDisplayName = clientBlock.labels.singular + const blockDisplayName = clientBlock?.labels?.singular ? getTranslation(clientBlock.labels.singular, i18n) - : clientBlock.slug + : clientBlock?.slug return blockDisplayName }, onSelect: ({ editor }) => { editor.dispatchCommand(INSERT_BLOCK_COMMAND, { - id: null, blockName: '', blockType: blockSlug, }) @@ -75,26 +78,29 @@ export const BlocksFeatureClient = createClientFeature key: 'inlineBlocks-' + inlineBlockSlug, keywords: ['inlineBlock', 'inline block', inlineBlockSlug], label: ({ i18n, richTextComponentMap }) => { + if (!richTextComponentMap) { + return inlineBlockSlug + } + const componentMapRenderedBlockPath = `lexical_internal_feature.blocks.fields.lexical_inline_blocks` const blocksField: BlocksFieldClient = richTextComponentMap.get( componentMapRenderedBlockPath, - )[0] + )?.[0] const clientBlock = blocksField.blocks.find( (_block) => _block.slug === inlineBlockSlug, ) - const blockDisplayName = clientBlock.labels.singular + const blockDisplayName = clientBlock?.labels?.singular ? getTranslation(clientBlock.labels.singular, i18n) - : clientBlock.slug + : clientBlock?.slug return blockDisplayName }, onSelect: ({ editor }) => { editor.dispatchCommand(OPEN_INLINE_BLOCK_DRAWER_COMMAND, { fields: { - id: null, blockName: '', blockType: inlineBlockSlug, }, @@ -122,22 +128,24 @@ export const BlocksFeatureClient = createClientFeature isActive: undefined, // At this point, we would be inside a sub-richtext-editor. And at this point this will be run against the focused sub-editor, not the parent editor which has the actual block. Thus, no point in running this key: 'block-' + blockSlug, label: ({ i18n, richTextComponentMap }) => { + if (!richTextComponentMap) { + return blockSlug + } const componentMapRenderedBlockPath = `lexical_internal_feature.blocks.fields.lexical_blocks` const blocksField: BlocksFieldClient = richTextComponentMap.get( componentMapRenderedBlockPath, - )[0] + )?.[0] const clientBlock = blocksField.blocks.find((_block) => _block.slug === blockSlug) - const blockDisplayName = clientBlock.labels.singular + const blockDisplayName = clientBlock?.labels?.singular ? getTranslation(clientBlock.labels.singular, i18n) - : clientBlock.slug + : clientBlock?.slug return blockDisplayName }, onSelect: ({ editor }) => { editor.dispatchCommand(INSERT_BLOCK_COMMAND, { - id: null, blockName: '', blockType: blockSlug, }) @@ -159,18 +167,22 @@ export const BlocksFeatureClient = createClientFeature isActive: undefined, key: 'inlineBlock-' + inlineBlockSlug, label: ({ i18n, richTextComponentMap }) => { + if (!richTextComponentMap) { + return inlineBlockSlug + } + const componentMapRenderedBlockPath = `lexical_internal_feature.blocks.fields.lexical_inline_blocks` const blocksField: BlocksFieldClient = richTextComponentMap.get( componentMapRenderedBlockPath, - )[0] + )?.[0] const clientBlock = blocksField.blocks.find( (_block) => _block.slug === inlineBlockSlug, ) - const blockDisplayName = clientBlock.labels.singular + const blockDisplayName = clientBlock?.labels?.singular ? getTranslation(clientBlock.labels.singular, i18n) - : clientBlock.slug + : clientBlock?.slug return blockDisplayName }, diff --git a/packages/richtext-lexical/src/features/blocks/client/nodes/BlocksNode.tsx b/packages/richtext-lexical/src/features/blocks/client/nodes/BlocksNode.tsx index 901947b7a3..698a15065c 100644 --- a/packages/richtext-lexical/src/features/blocks/client/nodes/BlocksNode.tsx +++ b/packages/richtext-lexical/src/features/blocks/client/nodes/BlocksNode.tsx @@ -4,7 +4,11 @@ import type { EditorConfig, LexicalEditor, LexicalNode } from 'lexical' import ObjectID from 'bson-objectid' import React, { type JSX } from 'react' -import type { BlockFields, SerializedBlockNode } from '../../server/nodes/BlocksNode.js' +import type { + BlockFields, + BlockFieldsOptionalID, + SerializedBlockNode, +} from '../../server/nodes/BlocksNode.js' import { ServerBlockNode } from '../../server/nodes/BlocksNode.js' @@ -48,7 +52,7 @@ export class BlockNode extends ServerBlockNode { } } -export function $createBlockNode(fields: Exclude): BlockNode { +export function $createBlockNode(fields: BlockFieldsOptionalID): BlockNode { return new BlockNode({ fields: { ...fields, diff --git a/packages/richtext-lexical/src/features/blocks/client/plugin/index.tsx b/packages/richtext-lexical/src/features/blocks/client/plugin/index.tsx index 2ae5e0857c..cd4b84923f 100644 --- a/packages/richtext-lexical/src/features/blocks/client/plugin/index.tsx +++ b/packages/richtext-lexical/src/features/blocks/client/plugin/index.tsx @@ -27,26 +27,25 @@ import { import React, { useEffect, useState } from 'react' import type { PluginComponent } from '../../../typesClient.js' -import type { BlockFields } from '../../server/nodes/BlocksNode.js' +import type { BlockFields, BlockFieldsOptionalID } from '../../server/nodes/BlocksNode.js' import type { BlocksFeatureClientProps } from '../index.js' -import type { InlineBlockNode } from '../nodes/InlineBlocksNode.js' import { useEditorConfigContext } from '../../../../lexical/config/client/EditorConfigProvider.js' import { FieldsDrawer } from '../../../../utilities/fieldsDrawer/Drawer.js' import { $createBlockNode, BlockNode } from '../nodes/BlocksNode.js' -import { $createInlineBlockNode } from '../nodes/InlineBlocksNode.js' +import { $createInlineBlockNode, $isInlineBlockNode } from '../nodes/InlineBlocksNode.js' import { INSERT_BLOCK_COMMAND, INSERT_INLINE_BLOCK_COMMAND, OPEN_INLINE_BLOCK_DRAWER_COMMAND, } from './commands.js' -export type InsertBlockPayload = Exclude +export type InsertBlockPayload = BlockFieldsOptionalID export const BlocksPlugin: PluginComponent = () => { const [editor] = useLexicalComposerContext() const { closeModal, toggleModal } = useModal() - const [blockFields, setBlockFields] = useState(null) + const [blockFields, setBlockFields] = useState(null) const [blockType, setBlockType] = useState('' as any) const [targetNodeKey, setTargetNodeKey] = useState(null) const { i18n, t } = useTranslation() @@ -89,7 +88,7 @@ export const BlocksPlugin: PluginComponent = () => { $isParagraphNode(focusNode) && focusNode.getTextContentSize() === 0 && focusNode - .getParent() + .getParentOrThrow() .getChildren() .filter((node) => $isParagraphNode(node)).length > 1 ) { @@ -106,9 +105,9 @@ export const BlocksPlugin: PluginComponent = () => { INSERT_INLINE_BLOCK_COMMAND, (fields) => { if (targetNodeKey) { - const node: InlineBlockNode = $getNodeByKey(targetNodeKey) + const node = $getNodeByKey(targetNodeKey) - if (!node) { + if (!node || !$isInlineBlockNode(node)) { return false } @@ -167,7 +166,7 @@ export const BlocksPlugin: PluginComponent = () => { const schemaFieldsPath = `${schemaPath}.lexical_internal_feature.blocks.lexical_inline_blocks.lexical_inline_blocks.${blockFields?.blockType}` const componentMapRenderedBlockPath = `lexical_internal_feature.blocks.fields.lexical_inline_blocks` - const blocksField: BlocksFieldClient = richTextComponentMap.has(componentMapRenderedBlockPath) + const blocksField: BlocksFieldClient = richTextComponentMap?.has(componentMapRenderedBlockPath) ? richTextComponentMap.get(componentMapRenderedBlockPath)[0] : null diff --git a/packages/richtext-lexical/src/features/blocks/server/index.ts b/packages/richtext-lexical/src/features/blocks/server/index.ts index 4f1445a399..d7967ff96f 100644 --- a/packages/richtext-lexical/src/features/blocks/server/index.ts +++ b/packages/richtext-lexical/src/features/blocks/server/index.ts @@ -129,6 +129,7 @@ export const BlocksFeature = createServerFeature< i18n, nodes: [ createNode({ + // @ts-expect-error - TODO: fix this getSubFields: ({ node }) => { if (!node) { if (props?.blocks?.length) { @@ -145,7 +146,7 @@ export const BlocksFeature = createServerFeature< const blockType = node.fields.blockType - const block = props.blocks.find((block) => block.slug === blockType) + const block = props.blocks?.find((block) => block.slug === blockType) return block?.fields }, getSubFieldsData: ({ node }) => { @@ -156,6 +157,7 @@ export const BlocksFeature = createServerFeature< validations: [blockValidationHOC(props.blocks)], }), createNode({ + // @ts-expect-error - TODO: fix this getSubFields: ({ node }) => { if (!node) { if (props?.inlineBlocks?.length) { @@ -172,7 +174,7 @@ export const BlocksFeature = createServerFeature< const blockType = node.fields.blockType - const block = props.inlineBlocks.find((block) => block.slug === blockType) + const block = props.inlineBlocks?.find((block) => block.slug === blockType) return block?.fields }, getSubFieldsData: ({ node }) => { diff --git a/packages/richtext-lexical/src/features/blocks/server/nodes/BlocksNode.tsx b/packages/richtext-lexical/src/features/blocks/server/nodes/BlocksNode.tsx index 75b521e0bd..afcab56063 100644 --- a/packages/richtext-lexical/src/features/blocks/server/nodes/BlocksNode.tsx +++ b/packages/richtext-lexical/src/features/blocks/server/nodes/BlocksNode.tsx @@ -16,13 +16,20 @@ import { DecoratorBlockNode } from '@lexical/react/LexicalDecoratorBlockNode.js' import ObjectID from 'bson-objectid' import { deepCopyObjectSimple } from 'payload/shared' -export type BlockFields = { +type BaseBlockFields = { /** Block form data */ blockName: string blockType: string - id: string } & TBlockFields +export type BlockFields = { + id: string +} & BaseBlockFields + +export type BlockFieldsOptionalID = { + id?: string +} & BaseBlockFields + export type SerializedBlockNode = Spread< { children?: never // required so that our typed editor state doesn't automatically add children @@ -84,7 +91,7 @@ export class ServerBlockNode extends DecoratorBlockNode { return false } - decorate(editor: LexicalEditor, config: EditorConfig): JSX.Element { + decorate(editor: LexicalEditor, config: EditorConfig): JSX.Element | null { return null } @@ -121,7 +128,7 @@ export class ServerBlockNode extends DecoratorBlockNode { } } -export function $createServerBlockNode(fields: Exclude): ServerBlockNode { +export function $createServerBlockNode(fields: BlockFieldsOptionalID): ServerBlockNode { return new ServerBlockNode({ fields: { ...fields, diff --git a/packages/richtext-lexical/src/features/blocks/server/nodes/InlineBlocksNode.tsx b/packages/richtext-lexical/src/features/blocks/server/nodes/InlineBlocksNode.tsx index e5298cfadf..1ca3f78870 100644 --- a/packages/richtext-lexical/src/features/blocks/server/nodes/InlineBlocksNode.tsx +++ b/packages/richtext-lexical/src/features/blocks/server/nodes/InlineBlocksNode.tsx @@ -32,7 +32,7 @@ export type SerializedServerInlineBlockNode = Spread< SerializedLexicalNode > -export class ServerInlineBlockNode extends DecoratorNode { +export class ServerInlineBlockNode extends DecoratorNode { __fields: InlineBlockFields constructor({ fields, key }: { fields: InlineBlockFields; key?: NodeKey }) { @@ -74,7 +74,7 @@ export class ServerInlineBlockNode extends DecoratorNode { return element } - decorate(editor: LexicalEditor, config: EditorConfig): JSX.Element { + decorate(editor: LexicalEditor, config: EditorConfig): JSX.Element | null { return null } diff --git a/packages/richtext-lexical/src/features/blocks/server/validate.ts b/packages/richtext-lexical/src/features/blocks/server/validate.ts index 7b6bc01036..d074589b2f 100644 --- a/packages/richtext-lexical/src/features/blocks/server/validate.ts +++ b/packages/richtext-lexical/src/features/blocks/server/validate.ts @@ -39,7 +39,7 @@ export const blockValidationHOC = ( siblingData: blockFieldData, }) - let errorPaths = [] + let errorPaths: string[] = [] for (const fieldKey in result) { if (result[fieldKey].errorPaths) { errorPaths = errorPaths.concat(result[fieldKey].errorPaths) diff --git a/packages/richtext-lexical/src/features/converters/html/converter/index.ts b/packages/richtext-lexical/src/features/converters/html/converter/index.ts index 5ca9428e37..7c3a74f71a 100644 --- a/packages/richtext-lexical/src/features/converters/html/converter/index.ts +++ b/packages/richtext-lexical/src/features/converters/html/converter/index.ts @@ -69,12 +69,12 @@ export async function convertLexicalToHTML({ return await convertLexicalNodesToHTML({ converters, currentDepth, - depth, + depth: depth!, draft: draft === undefined ? false : draft, lexicalNodes: data?.root?.children, overrideAccess: overrideAccess === undefined ? false : overrideAccess, parent: data?.root, - req, + req: req!, showHiddenFields: showHiddenFields === undefined ? false : showHiddenFields, }) } diff --git a/packages/richtext-lexical/src/features/converters/html/field/index.ts b/packages/richtext-lexical/src/features/converters/html/field/index.ts index 767ee5e063..4375fe095a 100644 --- a/packages/richtext-lexical/src/features/converters/html/field/index.ts +++ b/packages/richtext-lexical/src/features/converters/html/field/index.ts @@ -91,7 +91,7 @@ function findFieldPathAndSiblingFields( ): { path: string[] siblingFields: Field[] -} { +} | null { for (const curField of fields) { if (curField === field) { return { @@ -172,7 +172,7 @@ export const lexicalHTML: ( showHiddenFields, siblingData, }) => { - const fields = collection ? collection.fields : global.fields + const fields = collection ? collection.fields : global!.fields const foundSiblingFields = findFieldPathAndSiblingFields(fields, [], field) diff --git a/packages/richtext-lexical/src/features/debug/testRecorder/client/plugin/index.tsx b/packages/richtext-lexical/src/features/debug/testRecorder/client/plugin/index.tsx index 062774eb96..50f9c74193 100644 --- a/packages/richtext-lexical/src/features/debug/testRecorder/client/plugin/index.tsx +++ b/packages/richtext-lexical/src/features/debug/testRecorder/client/plugin/index.tsx @@ -106,7 +106,7 @@ function sanitizeSelection(selection: Selection) { function getPathFromNodeToEditor(node: Node, rootElement: HTMLElement | null) { let currentNode: Node | null | undefined = node - const path = [] + const path: number[] = [] while (currentNode !== rootElement) { if (currentNode !== null && currentNode !== undefined) { path.unshift( diff --git a/packages/richtext-lexical/src/features/experimental_table/client/plugins/TableActionMenuPlugin/index.tsx b/packages/richtext-lexical/src/features/experimental_table/client/plugins/TableActionMenuPlugin/index.tsx index d3693b36ca..af8263ad3e 100644 --- a/packages/richtext-lexical/src/features/experimental_table/client/plugins/TableActionMenuPlugin/index.tsx +++ b/packages/richtext-lexical/src/features/experimental_table/client/plugins/TableActionMenuPlugin/index.tsx @@ -63,8 +63,8 @@ function computeSelectionCount(selection: TableSelection): { function isTableSelectionRectangular(selection: TableSelection): boolean { const nodes = selection.getNodes() const currentRows: Array = [] - let currentRow = null - let expectedColumns = null + let currentRow: null | TableRowNode = null + let expectedColumns: null | number = null let currentColumns = 0 for (let i = 0; i < nodes.length; i++) { const node = nodes[i] diff --git a/packages/richtext-lexical/src/features/experimental_table/server/index.ts b/packages/richtext-lexical/src/features/experimental_table/server/index.ts index 014c6dc4a0..8bcd6bb4d3 100644 --- a/packages/richtext-lexical/src/features/experimental_table/server/index.ts +++ b/packages/richtext-lexical/src/features/experimental_table/server/index.ts @@ -138,8 +138,8 @@ export const EXPERIMENTAL_TableFeature = createServerFeature({ const backgroundColor = node.backgroundColor ? `background-color: ${node.backgroundColor};` : '' - const colSpan = node.colSpan > 1 ? `colspan="${node.colSpan}"` : '' - const rowSpan = node.rowSpan > 1 ? `rowspan="${node.rowSpan}"` : '' + const colSpan = node.colSpan && node.colSpan > 1 ? `colspan="${node.colSpan}"` : '' + const rowSpan = node.rowSpan && node.rowSpan > 1 ? `rowspan="${node.rowSpan}"` : '' return `<${tagName} class="lexical-table-cell ${headerStateClass}" style="border: 1px solid #ccc; padding: 8px; ${backgroundColor}" ${colSpan} ${rowSpan}>${childrenText}` }, diff --git a/packages/richtext-lexical/src/features/horizontalRule/server/nodes/HorizontalRuleNode.tsx b/packages/richtext-lexical/src/features/horizontalRule/server/nodes/HorizontalRuleNode.tsx index d56af82785..4fa1b9196f 100644 --- a/packages/richtext-lexical/src/features/horizontalRule/server/nodes/HorizontalRuleNode.tsx +++ b/packages/richtext-lexical/src/features/horizontalRule/server/nodes/HorizontalRuleNode.tsx @@ -35,7 +35,7 @@ export const INSERT_HORIZONTAL_RULE_COMMAND: LexicalCommand = createComman * * If we used DecoratorBlockNode instead, we would only need a decorate method */ -export class HorizontalRuleServerNode extends DecoratorNode { +export class HorizontalRuleServerNode extends DecoratorNode { static clone(node: HorizontalRuleServerNode): HorizontalRuleServerNode { return new this(node.__key) } @@ -74,7 +74,7 @@ export class HorizontalRuleServerNode extends DecoratorNode return element } - decorate() { + decorate(): null | React.ReactElement { return null } diff --git a/packages/richtext-lexical/src/features/indent/client/index.tsx b/packages/richtext-lexical/src/features/indent/client/index.tsx index 97ea23553a..66d6f23d13 100644 --- a/packages/richtext-lexical/src/features/indent/client/index.tsx +++ b/packages/richtext-lexical/src/features/indent/client/index.tsx @@ -19,10 +19,11 @@ const toolbarGroups: ToolbarGroup[] = [ return false } for (const node of selection.getNodes()) { + const parent = node.getParentOrThrow() // If at least one node is indented, this should be active if ( ('__indent' in node && (node.__indent as number) > 0) || - (node.getParent() && '__indent' in node.getParent() && node.getParent().__indent > 0) + ('__indent' in parent && parent.__indent > 0) ) { return true } diff --git a/packages/richtext-lexical/src/features/link/client/index.tsx b/packages/richtext-lexical/src/features/link/client/index.tsx index 60adcbf68a..aff1ffded1 100644 --- a/packages/richtext-lexical/src/features/link/client/index.tsx +++ b/packages/richtext-lexical/src/features/link/client/index.tsx @@ -45,7 +45,7 @@ const toolbarGroups: ToolbarGroup[] = [ }, onSelect: ({ editor, isActive }) => { if (!isActive) { - let selectedText: string = null + let selectedText: string | undefined let selectedNodes: LexicalNode[] = [] editor.getEditorState().read(() => { selectedText = $getSelection()?.getTextContent() diff --git a/packages/richtext-lexical/src/features/link/client/plugins/autoLink/index.tsx b/packages/richtext-lexical/src/features/link/client/plugins/autoLink/index.tsx index cd27479e01..756b7d01b7 100644 --- a/packages/richtext-lexical/src/features/link/client/plugins/autoLink/index.tsx +++ b/packages/richtext-lexical/src/features/link/client/plugins/autoLink/index.tsx @@ -179,11 +179,11 @@ function $createAutoLinkNode_( endIndex: number, match: LinkMatcherResult, ): TextNode | undefined { - const fields: LinkFields = { + const fields = { linkType: 'custom', url: match.url, ...match.fields, - } + } as LinkFields const linkNode = $createAutoLinkNode({ fields }) if (nodes.length === 1) { @@ -210,7 +210,7 @@ function $createAutoLinkNode_( } else { ;[, firstLinkTextNode] = firstTextNode.splitText(startIndex) } - const linkNodes = [] + const linkNodes: LexicalNode[] = [] let remainingTextNode for (let i = 1; i < nodes.length; i++) { const currentNode = nodes[i] diff --git a/packages/richtext-lexical/src/features/link/client/plugins/floatingLinkEditor/LinkEditor/index.tsx b/packages/richtext-lexical/src/features/link/client/plugins/floatingLinkEditor/LinkEditor/index.tsx index 033de390c9..4750e60f15 100644 --- a/packages/richtext-lexical/src/features/link/client/plugins/floatingLinkEditor/LinkEditor/index.tsx +++ b/packages/richtext-lexical/src/features/link/client/plugins/floatingLinkEditor/LinkEditor/index.tsx @@ -1,5 +1,5 @@ 'use client' -import type { LexicalNode } from 'lexical' +import type { ElementNode, LexicalNode } from 'lexical' import type { Data, FormState } from 'payload' import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext.js' @@ -41,8 +41,8 @@ export function LinkEditor({ anchorElem }: { anchorElem: HTMLElement }): React.R const [editor] = useLexicalComposerContext() const editorRef = useRef(null) - const [linkUrl, setLinkUrl] = useState(null) - const [linkLabel, setLinkLabel] = useState(null) + const [linkUrl, setLinkUrl] = useState(null) + const [linkLabel, setLinkLabel] = useState(null) const { uuid } = useEditorConfigContext() @@ -50,7 +50,9 @@ export function LinkEditor({ anchorElem }: { anchorElem: HTMLElement }): React.R const { i18n, t } = useTranslation() - const [stateData, setStateData] = useState<{ id?: string; text: string } & LinkFields>(null) + const [stateData, setStateData] = useState< + ({ id?: string; text: string } & LinkFields) | undefined + >() const { closeModal, toggleModal } = useModal() const editDepth = useEditDepth() @@ -74,12 +76,12 @@ export function LinkEditor({ anchorElem }: { anchorElem: HTMLElement }): React.R setLinkUrl(null) setLinkLabel(null) setSelectedNodes([]) - setStateData(null) + setStateData(undefined) }, [setIsLink, setLinkUrl, setLinkLabel, setSelectedNodes]) const $updateLinkEditor = useCallback(() => { const selection = $getSelection() - let selectedNodeDomRect: DOMRect | undefined = null + let selectedNodeDomRect: DOMRect | undefined if (!$isRangeSelection(selection) || !selection) { setNotLink() @@ -90,7 +92,7 @@ export function LinkEditor({ anchorElem }: { anchorElem: HTMLElement }): React.R const focusNode = getSelectedNode(selection) selectedNodeDomRect = editor.getElementByKey(focusNode.getKey())?.getBoundingClientRect() - const focusLinkParent: LinkNode = $findMatchingParent(focusNode, $isLinkNode) + const focusLinkParent = $findMatchingParent(focusNode, $isLinkNode) // Prevent link modal from showing if selection spans further than the link: https://github.com/facebook/lexical/issues/4064 const badNode = selection @@ -111,10 +113,6 @@ export function LinkEditor({ anchorElem }: { anchorElem: HTMLElement }): React.R // Initial state: const data: { text: string } & LinkFields = { - doc: undefined, - linkType: undefined, - newTab: undefined, - url: '', ...focusLinkParent.getFields(), id: focusLinkParent.getID(), text: focusLinkParent.getTextContent(), @@ -343,7 +341,7 @@ export function LinkEditor({ anchorElem }: { anchorElem: HTMLElement }): React.R // See: https://github.com/facebook/lexical/pull/5536. This updates autolink nodes to link nodes whenever a change was made (which is good!). editor.update(() => { const selection = $getSelection() - let linkParent = null + let linkParent: ElementNode | null = null if ($isRangeSelection(selection)) { linkParent = getSelectedNode(selection).getParent() } else { diff --git a/packages/richtext-lexical/src/features/link/nodes/AutoLinkNode.ts b/packages/richtext-lexical/src/features/link/nodes/AutoLinkNode.ts index cde03bb34e..9dfdd71b27 100644 --- a/packages/richtext-lexical/src/features/link/nodes/AutoLinkNode.ts +++ b/packages/richtext-lexical/src/features/link/nodes/AutoLinkNode.ts @@ -11,7 +11,7 @@ import { LinkNode } from './LinkNode.js' export class AutoLinkNode extends LinkNode { static clone(node: AutoLinkNode): AutoLinkNode { - return new AutoLinkNode({ id: undefined, fields: node.__fields, key: node.__key }) + return new AutoLinkNode({ id: '', fields: node.__fields, key: node.__key }) } static getType(): string { @@ -67,7 +67,7 @@ export class AutoLinkNode extends LinkNode { } export function $createAutoLinkNode({ fields }: { fields: LinkFields }): AutoLinkNode { - return $applyNodeReplacement(new AutoLinkNode({ id: undefined, fields })) + return $applyNodeReplacement(new AutoLinkNode({ id: '', fields })) } export function $isAutoLinkNode(node: LexicalNode | null | undefined): node is AutoLinkNode { return node instanceof AutoLinkNode diff --git a/packages/richtext-lexical/src/features/link/nodes/LinkNode.ts b/packages/richtext-lexical/src/features/link/nodes/LinkNode.ts index dd7adf3aac..3866f3f541 100644 --- a/packages/richtext-lexical/src/features/link/nodes/LinkNode.ts +++ b/packages/richtext-lexical/src/features/link/nodes/LinkNode.ts @@ -38,7 +38,7 @@ export class LinkNode extends ElementNode { doc: null, linkType: 'custom', newTab: false, - url: undefined, + url: '', }, key, }: { @@ -269,14 +269,14 @@ export const TOGGLE_LINK_COMMAND: LexicalCommand = export function $toggleLink(payload: LinkPayload): void { const selection = $getSelection() - if (!$isRangeSelection(selection) && !payload.selectedNodes.length) { + if (!$isRangeSelection(selection) && !payload.selectedNodes?.length) { return } const nodes = $isRangeSelection(selection) ? selection.extract() : payload.selectedNodes if (payload === null) { // Remove LinkNodes - nodes.forEach((node) => { + nodes?.forEach((node) => { const parent = node.getParent() if ($isLinkNode(parent)) { @@ -291,7 +291,7 @@ export function $toggleLink(payload: LinkPayload): void { }) } else { // Add or merge LinkNodes - if (nodes.length === 1) { + if (nodes?.length === 1) { const firstNode = nodes[0] // if the first node is a LinkNode or if its // parent is a LinkNode, we update the URL, target and rel. @@ -317,7 +317,7 @@ export function $toggleLink(payload: LinkPayload): void { let prevParent: ElementNodeType | LinkNode | null = null let linkNode: LinkNode | null = null - nodes.forEach((node) => { + nodes?.forEach((node) => { const parent = node.getParent() if (parent === linkNode || parent === null || ($isElementNode(node) && !node.isInline())) { @@ -386,8 +386,12 @@ function $getAncestor( predicate: (ancestor: LexicalNode) => boolean, ): LexicalNode | null { let parent: LexicalNode | null = node - // eslint-disable-next-line no-empty - while (parent !== null && (parent = parent.getParent()) !== null && !predicate(parent)) {} + while (parent !== null) { + parent = parent.getParent() + if (parent === null || predicate(parent)) { + break + } + } return parent } diff --git a/packages/richtext-lexical/src/features/link/nodes/types.ts b/packages/richtext-lexical/src/features/link/nodes/types.ts index eef8195565..d6877ce0b8 100644 --- a/packages/richtext-lexical/src/features/link/nodes/types.ts +++ b/packages/richtext-lexical/src/features/link/nodes/types.ts @@ -1,5 +1,5 @@ import type { SerializedElementNode, SerializedLexicalNode, Spread } from 'lexical' -import type { JsonValue } from 'payload' +import type { DefaultDocumentIDType, JsonValue } from 'payload' export type LinkFields = { [key: string]: JsonValue @@ -9,9 +9,9 @@ export type LinkFields = { | { // Actual doc data, populated in afterRead hook [key: string]: JsonValue - id: string + id: DefaultDocumentIDType } - | string + | DefaultDocumentIDType } | null linkType: 'custom' | 'internal' newTab: boolean diff --git a/packages/richtext-lexical/src/features/link/server/baseFields.ts b/packages/richtext-lexical/src/features/link/server/baseFields.ts index 1a011349a5..ec1dcc2cf9 100644 --- a/packages/richtext-lexical/src/features/link/server/baseFields.ts +++ b/packages/richtext-lexical/src/features/link/server/baseFields.ts @@ -11,8 +11,8 @@ import { validateUrl, validateUrlMinimal } from '../../../lexical/utils/url.js' export const getBaseFields = ( config: SanitizedConfig, - enabledCollections: CollectionSlug[], - disabledCollections: CollectionSlug[], + enabledCollections?: CollectionSlug[], + disabledCollections?: CollectionSlug[], maxDepth?: number, ): FieldAffectingData[] => { let enabledRelations: CollectionSlug[] @@ -76,6 +76,7 @@ export const getBaseFields = ( }, label: ({ t }) => t('fields:enterURL'), required: true, + // @ts-expect-error - TODO: fix this validate: (value: string) => { if (!validateUrlMinimal(value)) { return 'Invalid URL' @@ -106,10 +107,12 @@ export const getBaseFields = ( filterOptions: !enabledCollections && !disabledCollections ? ({ relationTo, user }) => { - const hidden = config.collections.find(({ slug }) => slug === relationTo).admin.hidden + const hidden = config.collections.find(({ slug }) => slug === relationTo)?.admin + .hidden if (typeof hidden === 'function' && hidden({ user } as { user: User })) { return false } + return true } : null, label: ({ t }) => t('fields:chooseDocumentToLink'), diff --git a/packages/richtext-lexical/src/features/link/server/index.ts b/packages/richtext-lexical/src/features/link/server/index.ts index 942f6320ca..ed22692a6d 100644 --- a/packages/richtext-lexical/src/features/link/server/index.ts +++ b/packages/richtext-lexical/src/features/link/server/index.ts @@ -1,4 +1,11 @@ -import type { CollectionSlug, Config, Field, FieldAffectingData, SanitizedConfig } from 'payload' +import type { + CollectionSlug, + Config, + DefaultDocumentIDType, + Field, + FieldAffectingData, + SanitizedConfig, +} from 'payload' import escapeHTML from 'escape-html' import { sanitizeFields } from 'payload' @@ -71,7 +78,7 @@ export const LinkFeature = createServerFeature< const validRelationships = _config.collections.map((c) => c.slug) || [] const _transformedFields = transformExtraFields( - deepCopyObject(props.fields), + props.fields ? deepCopyObject(props.fields) : null, _config, props.enabledCollections, props.disabledCollections, @@ -148,9 +155,9 @@ export const LinkFeature = createServerFeature< let href: string = node.fields.url if (node.fields.linkType === 'internal') { href = - typeof node.fields.doc?.value === 'string' - ? node.fields.doc?.value - : node.fields.doc?.value?.id + typeof node.fields.doc?.value !== 'object' + ? String(node.fields.doc?.value) + : String(node.fields.doc?.value?.id) } return `${childrenText}` diff --git a/packages/richtext-lexical/src/features/link/server/transformExtraFields.ts b/packages/richtext-lexical/src/features/link/server/transformExtraFields.ts index b4003e0cbd..ee651cc7b5 100644 --- a/packages/richtext-lexical/src/features/link/server/transformExtraFields.ts +++ b/packages/richtext-lexical/src/features/link/server/transformExtraFields.ts @@ -11,7 +11,8 @@ export function transformExtraFields( config: SanitizedConfig defaultFields: FieldAffectingData[] }) => (Field | FieldAffectingData)[]) - | Field[], + | Field[] + | null, config: SanitizedConfig, enabledCollections?: CollectionSlug[], disabledCollections?: CollectionSlug[], diff --git a/packages/richtext-lexical/src/features/link/server/validate.ts b/packages/richtext-lexical/src/features/link/server/validate.ts index 28a95a9f24..d015722d5e 100644 --- a/packages/richtext-lexical/src/features/link/server/validate.ts +++ b/packages/richtext-lexical/src/features/link/server/validate.ts @@ -31,7 +31,7 @@ export const linkValidation = ( siblingData: node.fields, }) - let errorPaths = [] + let errorPaths: string[] = [] for (const fieldKey in result) { if (result[fieldKey].errorPaths) { errorPaths = errorPaths.concat(result[fieldKey].errorPaths) diff --git a/packages/richtext-lexical/src/features/lists/shared/markdown.ts b/packages/richtext-lexical/src/features/lists/shared/markdown.ts index f13afada31..8393adc32b 100644 --- a/packages/richtext-lexical/src/features/lists/shared/markdown.ts +++ b/packages/richtext-lexical/src/features/lists/shared/markdown.ts @@ -45,7 +45,7 @@ export const listExport = ( exportChildren: (node: ElementNode) => string, depth: number, ): string => { - const output = [] + const output: string[] = [] const children = listNode.getChildren() let index = 0 for (const listItemNode of children) { diff --git a/packages/richtext-lexical/src/features/migrations/lexicalPluginToLexical/converter/index.ts b/packages/richtext-lexical/src/features/migrations/lexicalPluginToLexical/converter/index.ts index 59081b24d9..10fbea4578 100644 --- a/packages/richtext-lexical/src/features/migrations/lexicalPluginToLexical/converter/index.ts +++ b/packages/richtext-lexical/src/features/migrations/lexicalPluginToLexical/converter/index.ts @@ -51,6 +51,7 @@ export function convertLexicalPluginNodesToLexical({ return [] } const unknownConverter = converters.find((converter) => converter.nodeTypes.includes('unknown')) + // @ts-expect-error - vestiges of the migration to strict mode. Probably not important enough in this file to fix return ( lexicalPluginNodes.map((lexicalPluginNode, i) => { if (lexicalPluginNode.type === 'paragraph') { diff --git a/packages/richtext-lexical/src/features/migrations/lexicalPluginToLexical/nodes/unknownConvertedNode/index.tsx b/packages/richtext-lexical/src/features/migrations/lexicalPluginToLexical/nodes/unknownConvertedNode/index.tsx index 3f62c05c05..b345d09e36 100644 --- a/packages/richtext-lexical/src/features/migrations/lexicalPluginToLexical/nodes/unknownConvertedNode/index.tsx +++ b/packages/richtext-lexical/src/features/migrations/lexicalPluginToLexical/nodes/unknownConvertedNode/index.tsx @@ -62,7 +62,7 @@ export class UnknownConvertedNode extends DecoratorNode { return element } - decorate(): JSX.Element | null { + decorate(): JSX.Element { return } diff --git a/packages/richtext-lexical/src/features/migrations/slateToLexical/converter/converters/blockquote/converter.ts b/packages/richtext-lexical/src/features/migrations/slateToLexical/converter/converters/blockquote/converter.ts index bbf62362cd..60675bb0ed 100644 --- a/packages/richtext-lexical/src/features/migrations/slateToLexical/converter/converters/blockquote/converter.ts +++ b/packages/richtext-lexical/src/features/migrations/slateToLexical/converter/converters/blockquote/converter.ts @@ -11,7 +11,7 @@ export const SlateBlockquoteConverter: SlateNodeConverter = { canContainParagraphs: false, converters, parentNodeType: 'quote', - slateNodes: slateNode.children, + slateNodes: slateNode.children!, }), direction: 'ltr', format: '', diff --git a/packages/richtext-lexical/src/features/migrations/slateToLexical/converter/converters/heading/converter.ts b/packages/richtext-lexical/src/features/migrations/slateToLexical/converter/converters/heading/converter.ts index 0b23f69d55..18d6a4f44b 100644 --- a/packages/richtext-lexical/src/features/migrations/slateToLexical/converter/converters/heading/converter.ts +++ b/packages/richtext-lexical/src/features/migrations/slateToLexical/converter/converters/heading/converter.ts @@ -11,7 +11,7 @@ export const SlateHeadingConverter: SlateNodeConverter = { canContainParagraphs: false, converters, parentNodeType: 'heading', - slateNodes: slateNode.children, + slateNodes: slateNode.children!, }), direction: 'ltr', format: '', diff --git a/packages/richtext-lexical/src/features/migrations/slateToLexical/converter/converters/link/converter.ts b/packages/richtext-lexical/src/features/migrations/slateToLexical/converter/converters/link/converter.ts index 1b44f1762b..73a0bb8e50 100644 --- a/packages/richtext-lexical/src/features/migrations/slateToLexical/converter/converters/link/converter.ts +++ b/packages/richtext-lexical/src/features/migrations/slateToLexical/converter/converters/link/converter.ts @@ -11,7 +11,7 @@ export const SlateLinkConverter: SlateNodeConverter = { canContainParagraphs: false, converters, parentNodeType: 'link', - slateNodes: slateNode.children, + slateNodes: slateNode.children!, }), direction: 'ltr', fields: { diff --git a/packages/richtext-lexical/src/features/migrations/slateToLexical/converter/converters/listItem/converter.ts b/packages/richtext-lexical/src/features/migrations/slateToLexical/converter/converters/listItem/converter.ts index 1d7b9b86b9..39ed123398 100644 --- a/packages/richtext-lexical/src/features/migrations/slateToLexical/converter/converters/listItem/converter.ts +++ b/packages/richtext-lexical/src/features/migrations/slateToLexical/converter/converters/listItem/converter.ts @@ -12,7 +12,7 @@ export const SlateListItemConverter: SlateNodeConverter = { canContainParagraphs: false, converters, parentNodeType: 'listitem', - slateNodes: slateNode.children, + slateNodes: slateNode.children!, }), direction: 'ltr', format: '', diff --git a/packages/richtext-lexical/src/features/migrations/slateToLexical/converter/converters/orderedList/converter.ts b/packages/richtext-lexical/src/features/migrations/slateToLexical/converter/converters/orderedList/converter.ts index 903764e1fd..39fb2f0a87 100644 --- a/packages/richtext-lexical/src/features/migrations/slateToLexical/converter/converters/orderedList/converter.ts +++ b/packages/richtext-lexical/src/features/migrations/slateToLexical/converter/converters/orderedList/converter.ts @@ -11,7 +11,7 @@ export const SlateOrderedListConverter: SlateNodeConverter = { canContainParagraphs: false, converters, parentNodeType: 'list', - slateNodes: slateNode.children, + slateNodes: slateNode.children!, }), direction: 'ltr', format: '', diff --git a/packages/richtext-lexical/src/features/migrations/slateToLexical/converter/converters/unknown/converter.ts b/packages/richtext-lexical/src/features/migrations/slateToLexical/converter/converters/unknown/converter.ts index 450025f9b0..efc533ff56 100644 --- a/packages/richtext-lexical/src/features/migrations/slateToLexical/converter/converters/unknown/converter.ts +++ b/packages/richtext-lexical/src/features/migrations/slateToLexical/converter/converters/unknown/converter.ts @@ -11,7 +11,7 @@ export const SlateUnknownConverter: SlateNodeConverter = { canContainParagraphs: false, converters, parentNodeType: 'unknownConverted', - slateNodes: slateNode.children, + slateNodes: slateNode.children!, }), data: { nodeData: slateNode, diff --git a/packages/richtext-lexical/src/features/migrations/slateToLexical/converter/converters/unorderedList/converter.ts b/packages/richtext-lexical/src/features/migrations/slateToLexical/converter/converters/unorderedList/converter.ts index 2ec23c9c1b..9d9f44ac19 100644 --- a/packages/richtext-lexical/src/features/migrations/slateToLexical/converter/converters/unorderedList/converter.ts +++ b/packages/richtext-lexical/src/features/migrations/slateToLexical/converter/converters/unorderedList/converter.ts @@ -11,7 +11,7 @@ export const SlateUnorderedListConverter: SlateNodeConverter = { canContainParagraphs: false, converters, parentNodeType: 'list', - slateNodes: slateNode.children, + slateNodes: slateNode.children!, }), direction: 'ltr', format: '', diff --git a/packages/richtext-lexical/src/features/migrations/slateToLexical/converter/index.ts b/packages/richtext-lexical/src/features/migrations/slateToLexical/converter/index.ts index f55cee5b61..fd67d71e91 100644 --- a/packages/richtext-lexical/src/features/migrations/slateToLexical/converter/index.ts +++ b/packages/richtext-lexical/src/features/migrations/slateToLexical/converter/index.ts @@ -51,6 +51,7 @@ export function convertSlateNodesToLexical({ return [] } const unknownConverter = converters.find((converter) => converter.nodeTypes.includes('unknown')) + // @ts-expect-error - vestiges of the migration to strict mode. Probably not important enough in this file to fix return ( slateNodes.map((slateNode, i) => { if (!('type' in slateNode)) { @@ -66,7 +67,9 @@ export function convertSlateNodesToLexical({ return convertParagraphNode(converters, slateNode) } - const converter = converters.find((converter) => converter.nodeTypes.includes(slateNode.type)) + const converter = converters.find((converter) => + converter.nodeTypes.includes(slateNode.type!), + ) if (converter) { return converter.converter({ childIndex: i, converters, parentNodeType, slateNode }) diff --git a/packages/richtext-lexical/src/features/migrations/slateToLexical/nodes/unknownConvertedNode/index.tsx b/packages/richtext-lexical/src/features/migrations/slateToLexical/nodes/unknownConvertedNode/index.tsx index 3f62c05c05..b345d09e36 100644 --- a/packages/richtext-lexical/src/features/migrations/slateToLexical/nodes/unknownConvertedNode/index.tsx +++ b/packages/richtext-lexical/src/features/migrations/slateToLexical/nodes/unknownConvertedNode/index.tsx @@ -62,7 +62,7 @@ export class UnknownConvertedNode extends DecoratorNode { return element } - decorate(): JSX.Element | null { + decorate(): JSX.Element { return } diff --git a/packages/richtext-lexical/src/features/relationship/client/components/RelationshipComponent.tsx b/packages/richtext-lexical/src/features/relationship/client/components/RelationshipComponent.tsx index 9c4bbee54d..6ba1357b43 100644 --- a/packages/richtext-lexical/src/features/relationship/client/components/RelationshipComponent.tsx +++ b/packages/richtext-lexical/src/features/relationship/client/components/RelationshipComponent.tsx @@ -52,7 +52,7 @@ const Component: React.FC = (props) => { const relationshipElemRef = useRef(null) const [editor] = useLexicalComposerContext() - const [isSelected, setSelected, clearSelection] = useLexicalNodeSelection(nodeKey) + const [isSelected, setSelected, clearSelection] = useLexicalNodeSelection(nodeKey!) const { field } = useEditorConfigContext() const { config: { @@ -62,8 +62,8 @@ const Component: React.FC = (props) => { }, } = useConfig() - const [relatedCollection, setRelatedCollection] = useState(() => - collections.find((coll) => coll.slug === relationTo), + const [relatedCollection, setRelatedCollection] = useState( + () => collections.find((coll) => coll.slug === relationTo)!, ) const { i18n, t } = useTranslation() @@ -80,7 +80,7 @@ const Component: React.FC = (props) => { const removeRelationship = useCallback(() => { editor.update(() => { - $getNodeByKey(nodeKey).remove() + $getNodeByKey(nodeKey!)?.remove() }) }, [editor, nodeKey]) @@ -102,7 +102,7 @@ const Component: React.FC = (props) => { if (isSelected && $isNodeSelection($getSelection())) { const event: KeyboardEvent = payload event.preventDefault() - const node = $getNodeByKey(nodeKey) + const node = $getNodeByKey(nodeKey!) if ($isRelationshipNode(node)) { node.remove() return true @@ -172,9 +172,11 @@ const Component: React.FC = (props) => { el="div" icon="swap" onClick={() => { - editor.dispatchCommand(INSERT_RELATIONSHIP_WITH_DRAWER_COMMAND, { - replace: { nodeKey }, - }) + if (nodeKey) { + editor.dispatchCommand(INSERT_RELATIONSHIP_WITH_DRAWER_COMMAND, { + replace: { nodeKey }, + }) + } }} round tooltip={t('fields:swapRelationship')} diff --git a/packages/richtext-lexical/src/features/relationship/client/drawer/index.tsx b/packages/richtext-lexical/src/features/relationship/client/drawer/index.tsx index 9821966fe3..ac640c16c3 100644 --- a/packages/richtext-lexical/src/features/relationship/client/drawer/index.tsx +++ b/packages/richtext-lexical/src/features/relationship/client/drawer/index.tsx @@ -44,12 +44,12 @@ type Props = { const RelationshipDrawerComponent: React.FC = ({ enabledCollectionSlugs }) => { const [editor] = useLexicalComposerContext() const [selectedCollectionSlug, setSelectedCollectionSlug] = useState( - () => enabledCollectionSlugs[0], + () => enabledCollectionSlugs?.[0], ) const [replaceNodeKey, setReplaceNodeKey] = useState(null) const [ListDrawer, ListDrawerToggler, { closeDrawer, isDrawerOpen, openDrawer }] = useListDrawer({ - collectionSlugs: enabledCollectionSlugs, + collectionSlugs: enabledCollectionSlugs!, selectedCollection: selectedCollectionSlug, }) @@ -83,14 +83,14 @@ const RelationshipDrawerComponent: React.FC = ({ enabledCollectionSlugs } useEffect(() => { // always reset back to first option // TODO: this is not working, see the ListDrawer component - setSelectedCollectionSlug(enabledCollectionSlugs[0]) + setSelectedCollectionSlug(enabledCollectionSlugs?.[0]) }, [isDrawerOpen, enabledCollectionSlugs]) return } export const RelationshipDrawer = (props: Props): React.ReactNode => { - return props?.enabledCollectionSlugs?.length > 0 ? ( // If enabledCollectionSlugs it overrides what EnabledRelationshipsCondition is doing + return (props?.enabledCollectionSlugs?.length ?? -1) > 0 ? ( // If enabledCollectionSlugs it overrides what EnabledRelationshipsCondition is doing ) : ( diff --git a/packages/richtext-lexical/src/features/relationship/client/plugins/index.tsx b/packages/richtext-lexical/src/features/relationship/client/plugins/index.tsx index 63afa6aff2..84affa0ed5 100644 --- a/packages/richtext-lexical/src/features/relationship/client/plugins/index.tsx +++ b/packages/richtext-lexical/src/features/relationship/client/plugins/index.tsx @@ -31,7 +31,7 @@ export const RelationshipPlugin: PluginComponent = ({ config: { collections }, } = useConfig() - let enabledRelations: string[] = null + let enabledRelations: null | string[] = null if (clientProps?.enabledCollections) { enabledRelations = clientProps?.enabledCollections @@ -65,7 +65,7 @@ export const RelationshipPlugin: PluginComponent = ({ $isParagraphNode(focusNode) && focusNode.getTextContentSize() === 0 && focusNode - .getParent() + .getParentOrThrow() .getChildren() .filter((node) => $isParagraphNode(node)).length > 1 ) { diff --git a/packages/richtext-lexical/src/features/relationship/client/utils/EnabledRelationshipsCondition.tsx b/packages/richtext-lexical/src/features/relationship/client/utils/EnabledRelationshipsCondition.tsx index e699679581..dea958962a 100644 --- a/packages/richtext-lexical/src/features/relationship/client/utils/EnabledRelationshipsCondition.tsx +++ b/packages/richtext-lexical/src/features/relationship/client/utils/EnabledRelationshipsCondition.tsx @@ -17,7 +17,7 @@ type FilteredCollectionsT = ( const filterRichTextCollections: FilteredCollectionsT = (collections, options) => { return collections.filter(({ slug, admin: { enableRichTextRelationship }, upload }) => { - if (!options.visibleEntities.collections.includes(slug)) { + if (!options?.visibleEntities.collections.includes(slug)) { return false } @@ -38,7 +38,7 @@ export const EnabledRelationshipsCondition: React.FC = (props) => { const { visibleEntities } = useEntityVisibility() const [enabledCollectionSlugs] = React.useState(() => - filterRichTextCollections(collections, { uploads, user, visibleEntities }).map( + filterRichTextCollections(collections, { uploads, user: user!, visibleEntities }).map( ({ slug }) => slug, ), ) diff --git a/packages/richtext-lexical/src/features/relationship/server/nodes/RelationshipNode.tsx b/packages/richtext-lexical/src/features/relationship/server/nodes/RelationshipNode.tsx index 465094dc3e..e5620790c7 100644 --- a/packages/richtext-lexical/src/features/relationship/server/nodes/RelationshipNode.tsx +++ b/packages/richtext-lexical/src/features/relationship/server/nodes/RelationshipNode.tsx @@ -104,7 +104,7 @@ export class RelationshipServerNode extends DecoratorBlockNode { return false } - decorate(editor: LexicalEditor, config: EditorConfig): JSX.Element { + decorate(editor: LexicalEditor, config: EditorConfig): JSX.Element | null { return null } diff --git a/packages/richtext-lexical/src/features/toolbars/fixed/client/Toolbar/index.tsx b/packages/richtext-lexical/src/features/toolbars/fixed/client/Toolbar/index.tsx index 48a813f1e5..4300dbdb5d 100644 --- a/packages/richtext-lexical/src/features/toolbars/fixed/client/Toolbar/index.tsx +++ b/packages/richtext-lexical/src/features/toolbars/fixed/client/Toolbar/index.tsx @@ -34,9 +34,13 @@ function ButtonGroupItem({ ) } + if (!item.ChildComponent) { + return null + } + return ( - {item?.ChildComponent && } + {} ) } @@ -58,14 +62,14 @@ function ToolbarGroupComponent({ const { field: { richTextComponentMap }, } = useEditorConfigContext() - const [dropdownLabel, setDropdownLabel] = React.useState(null) - const [DropdownIcon, setDropdownIcon] = React.useState(null) + const [dropdownLabel, setDropdownLabel] = React.useState(undefined) + const [DropdownIcon, setDropdownIcon] = React.useState(undefined) React.useEffect(() => { if (group?.type === 'dropdown' && group.items.length && group.ChildComponent) { - setDropdownIcon(() => group.ChildComponent) + setDropdownIcon(() => group.ChildComponent!) } else { - setDropdownIcon(null) + setDropdownIcon(undefined) } }, [group]) @@ -73,11 +77,11 @@ function ToolbarGroupComponent({ ({ activeItems }: { activeItems: ToolbarGroupItem[] }) => { if (!activeItems.length) { if (group?.type === 'dropdown' && group.items.length && group.ChildComponent) { - setDropdownIcon(() => group.ChildComponent) - setDropdownLabel(null) + setDropdownIcon(() => group.ChildComponent!) + setDropdownLabel(undefined) } else { - setDropdownIcon(null) - setDropdownLabel(null) + setDropdownIcon(undefined) + setDropdownLabel(undefined) } return } @@ -153,7 +157,7 @@ function FixedToolbar({ }): React.ReactNode { const currentToolbarRef = React.useRef(null) - const { y } = useScrollInfo() + const { y } = useScrollInfo!() // Memoize the parent toolbar element const parentToolbarElem = useMemo(() => { @@ -192,13 +196,13 @@ function FixedToolbar({ ) if (overlapping) { - currentToolbarRef.current.className = 'fixed-toolbar fixed-toolbar--overlapping' + currentToolbarElem.className = 'fixed-toolbar fixed-toolbar--overlapping' parentToolbarElem.className = 'fixed-toolbar fixed-toolbar--hide' } else { - if (!currentToolbarRef.current.classList.contains('fixed-toolbar--overlapping')) { + if (!currentToolbarElem.classList.contains('fixed-toolbar--overlapping')) { return } - currentToolbarRef.current.className = 'fixed-toolbar' + currentToolbarElem.className = 'fixed-toolbar' parentToolbarElem.className = 'fixed-toolbar' } }, diff --git a/packages/richtext-lexical/src/features/toolbars/inline/client/Toolbar/index.tsx b/packages/richtext-lexical/src/features/toolbars/inline/client/Toolbar/index.tsx index 299b98ccea..fd40818b03 100644 --- a/packages/richtext-lexical/src/features/toolbars/inline/client/Toolbar/index.tsx +++ b/packages/richtext-lexical/src/features/toolbars/inline/client/Toolbar/index.tsx @@ -40,10 +40,13 @@ function ButtonGroupItem({ ) ) } + if (!item.ChildComponent) { + return null + } return ( - {item?.ChildComponent && } + ) } @@ -61,13 +64,13 @@ function ToolbarGroupComponent({ }): React.ReactNode { const { editorConfig } = useEditorConfigContext() - const [DropdownIcon, setDropdownIcon] = React.useState(null) + const [DropdownIcon, setDropdownIcon] = React.useState() React.useEffect(() => { if (group?.type === 'dropdown' && group.items.length && group.ChildComponent) { setDropdownIcon(() => group.ChildComponent) } else { - setDropdownIcon(null) + setDropdownIcon(undefined) } }, [group]) @@ -77,7 +80,7 @@ function ToolbarGroupComponent({ if (group?.type === 'dropdown' && group.items.length && group.ChildComponent) { setDropdownIcon(() => group.ChildComponent) } else { - setDropdownIcon(null) + setDropdownIcon(undefined) } return } diff --git a/packages/richtext-lexical/src/features/toolbars/shared/ToolbarButton/index.tsx b/packages/richtext-lexical/src/features/toolbars/shared/ToolbarButton/index.tsx index 5e1f31ebac..6a8be0a99f 100644 --- a/packages/richtext-lexical/src/features/toolbars/shared/ToolbarButton/index.tsx +++ b/packages/richtext-lexical/src/features/toolbars/shared/ToolbarButton/index.tsx @@ -29,6 +29,9 @@ export const ToolbarButton = ({ const updateStates = useCallback(() => { editor.getEditorState().read(() => { const selection = $getSelection() + if (!selection) { + return + } if (item.isActive) { const isActive = item.isActive({ editor, editorConfigContext, selection }) if (active !== isActive) { @@ -85,7 +88,7 @@ export const ToolbarButton = ({ editor.focus(() => { // We need to wrap the onSelect in the callback, so the editor is properly focused before the onSelect is called. - item.onSelect({ + item.onSelect?.({ editor, isActive: active, }) diff --git a/packages/richtext-lexical/src/features/toolbars/shared/ToolbarDropdown/DropDown.tsx b/packages/richtext-lexical/src/features/toolbars/shared/ToolbarDropdown/DropDown.tsx index fd92f458bf..883871cd81 100644 --- a/packages/richtext-lexical/src/features/toolbars/shared/ToolbarDropdown/DropDown.tsx +++ b/packages/richtext-lexical/src/features/toolbars/shared/ToolbarDropdown/DropDown.tsx @@ -69,9 +69,9 @@ export function DropDownItem({ editor.focus(() => { // We need to wrap the onSelect in the callback, so the editor is properly focused before the onSelect is called. - item.onSelect({ + item.onSelect?.({ editor, - isActive: active, + isActive: active!, }) }) } diff --git a/packages/richtext-lexical/src/features/toolbars/shared/ToolbarDropdown/index.tsx b/packages/richtext-lexical/src/features/toolbars/shared/ToolbarDropdown/index.tsx index 49d340f252..ebb45348ec 100644 --- a/packages/richtext-lexical/src/features/toolbars/shared/ToolbarDropdown/index.tsx +++ b/packages/richtext-lexical/src/features/toolbars/shared/ToolbarDropdown/index.tsx @@ -100,6 +100,9 @@ export const ToolbarDropdown = ({ const updateStates = useCallback(() => { editor.getEditorState().read(() => { const selection = $getSelection() + if (!selection) { + return + } const _activeItemKeys: string[] = [] const _activeItems: ToolbarGroupItem[] = [] diff --git a/packages/richtext-lexical/src/features/toolbars/types.ts b/packages/richtext-lexical/src/features/toolbars/types.ts index 7480cc36b9..3d48d3f9e8 100644 --- a/packages/richtext-lexical/src/features/toolbars/types.ts +++ b/packages/richtext-lexical/src/features/toolbars/types.ts @@ -83,7 +83,7 @@ export type ToolbarGroupItem = { label?: | ((args: { i18n: I18nClient<{}, string> - richTextComponentMap: Map + richTextComponentMap?: Map }) => string) | string /** Each toolbar item needs to have a unique key. */ diff --git a/packages/richtext-lexical/src/features/typesServer.ts b/packages/richtext-lexical/src/features/typesServer.ts index 0bd70c7a0d..72d0288ffe 100644 --- a/packages/richtext-lexical/src/features/typesServer.ts +++ b/packages/richtext-lexical/src/features/typesServer.ts @@ -286,6 +286,7 @@ export type ServerFeature = { * This determines what props will be available on the Client. */ clientFeatureProps?: ClientFeatureProps + // @ts-expect-error - TODO: fix this componentImports?: Config['admin']['importMap']['generators'][0] | PayloadComponent[] componentMap?: | ((args: { i18n: I18nClient; payload: Payload; props: ServerProps; schemaPath: string }) => { diff --git a/packages/richtext-lexical/src/features/upload/client/component/index.tsx b/packages/richtext-lexical/src/features/upload/client/component/index.tsx index 1496488fe4..925e85a91c 100644 --- a/packages/richtext-lexical/src/features/upload/client/component/index.tsx +++ b/packages/richtext-lexical/src/features/upload/client/component/index.tsx @@ -81,8 +81,8 @@ const Component: React.FC = (props) => { const { i18n, t } = useTranslation() const [cacheBust, dispatchCacheBust] = useReducer((state) => state + 1, 0) - const [relatedCollection] = useState(() => - collections.find((coll) => coll.slug === relationTo), + const [relatedCollection] = useState( + () => collections.find((coll) => coll.slug === relationTo)!, ) const componentID = useId() @@ -107,7 +107,7 @@ const Component: React.FC = (props) => { const removeUpload = useCallback(() => { editor.update(() => { - $getNodeByKey(nodeKey).remove() + $getNodeByKey(nodeKey)?.remove() }) }, [editor, nodeKey]) diff --git a/packages/richtext-lexical/src/features/upload/client/drawer/index.tsx b/packages/richtext-lexical/src/features/upload/client/drawer/index.tsx index b7d522f122..ae22e96811 100644 --- a/packages/richtext-lexical/src/features/upload/client/drawer/index.tsx +++ b/packages/richtext-lexical/src/features/upload/client/drawer/index.tsx @@ -24,6 +24,7 @@ const insertUpload = ({ }) => { if (!replaceNodeKey) { editor.dispatchCommand(INSERT_UPLOAD_COMMAND, { + // @ts-expect-error - TODO: fix this fields: null, relationTo, value, @@ -35,6 +36,7 @@ const insertUpload = ({ node.replace( $createUploadNode({ data: { + // @ts-expect-error - TODO: fix this fields: null, relationTo, value, diff --git a/packages/richtext-lexical/src/features/upload/client/plugin/index.tsx b/packages/richtext-lexical/src/features/upload/client/plugin/index.tsx index 820c50408e..6ad94774db 100644 --- a/packages/richtext-lexical/src/features/upload/client/plugin/index.tsx +++ b/packages/richtext-lexical/src/features/upload/client/plugin/index.tsx @@ -67,7 +67,7 @@ export const UploadPlugin: PluginComponentWithAnchor = $isParagraphNode(focusNode) && focusNode.getTextContentSize() === 0 && focusNode - .getParent() + .getParentOrThrow() .getChildren() .filter((node) => $isParagraphNode(node)).length > 1 ) { diff --git a/packages/richtext-lexical/src/features/upload/server/feature.server.ts b/packages/richtext-lexical/src/features/upload/server/feature.server.ts index abfa8ede25..5fee0e94cb 100644 --- a/packages/richtext-lexical/src/features/upload/server/feature.server.ts +++ b/packages/richtext-lexical/src/features/upload/server/feature.server.ts @@ -109,8 +109,8 @@ export const UploadFeature = createServerFeature< req, showHiddenFields, }) => { - // @ts-expect-error - const id = node?.value?.id || node?.value // for backwards-compatibility + // @ts-expect-error - for backwards-compatibility + const id = node?.value?.id || node?.value if (req?.payload) { const uploadDocument: { @@ -141,7 +141,7 @@ export const UploadFeature = createServerFeature< return `` } - const url = getAbsoluteURL(uploadDocument?.value?.url, req?.payload) + const url = getAbsoluteURL(uploadDocument?.value?.url ?? '', req?.payload) /** * If the upload is not an image, return a link to the upload diff --git a/packages/richtext-lexical/src/features/upload/server/validate.ts b/packages/richtext-lexical/src/features/upload/server/validate.ts index c195ab73c5..c8a04f4871 100644 --- a/packages/richtext-lexical/src/features/upload/server/validate.ts +++ b/packages/richtext-lexical/src/features/upload/server/validate.ts @@ -53,7 +53,7 @@ export const uploadValidation = ( siblingData: node?.fields ?? {}, }) - let errorPaths = [] + let errorPaths: string[] = [] for (const fieldKey in result) { if (result[fieldKey].errorPaths) { errorPaths = errorPaths.concat(result[fieldKey].errorPaths) diff --git a/packages/richtext-lexical/src/field/Field.tsx b/packages/richtext-lexical/src/field/Field.tsx index c209e5aeec..46bfa5185b 100644 --- a/packages/richtext-lexical/src/field/Field.tsx +++ b/packages/richtext-lexical/src/field/Field.tsx @@ -34,13 +34,7 @@ const RichTextComponent: React.FC< field: { name, _path: pathFromProps, - admin: { - className, - components: { Description, Error, Label }, - readOnly: readOnlyFromAdmin, - style, - width, - } = {}, + admin: { className, components, readOnly: readOnlyFromAdmin, style, width } = {}, required, }, field, @@ -48,6 +42,9 @@ const RichTextComponent: React.FC< readOnly: readOnlyFromTopLevelProps, validate, // Users can pass in client side validation if they WANT to, but it's not required anymore } = props + const Description = components?.Description + const Error = components?.Error + const Label = components?.Label const readOnlyFromProps = readOnlyFromTopLevelProps || readOnlyFromAdmin const memoizedValidate = useCallback( @@ -65,6 +62,7 @@ const RichTextComponent: React.FC< const fieldType = useField({ path: pathFromContext ?? pathFromProps ?? name, + // @ts-expect-error: TODO: Fix this validate: memoizedValidate, }) @@ -95,12 +93,12 @@ const RichTextComponent: React.FC< > - +
{}}> - +
) diff --git a/packages/richtext-lexical/src/field/index.tsx b/packages/richtext-lexical/src/field/index.tsx index fdca6d8267..4ad22dd105 100644 --- a/packages/richtext-lexical/src/field/index.tsx +++ b/packages/richtext-lexical/src/field/index.tsx @@ -23,13 +23,13 @@ export const RichTextField: React.FC = (props) => { } = props const [finalSanitizedEditorConfig, setFinalSanitizedEditorConfig] = - useState(null) + useState(null) useEffect(() => { if (finalSanitizedEditorConfig) { return } - const clientFeatures: GeneratedFeatureProviderComponent[] = richTextComponentMap.get( + const clientFeatures: GeneratedFeatureProviderComponent[] = richTextComponentMap?.get( 'features', ) as GeneratedFeatureProviderComponent[] diff --git a/packages/richtext-lexical/src/index.ts b/packages/richtext-lexical/src/index.ts index d91417f5a8..a92b705098 100644 --- a/packages/richtext-lexical/src/index.ts +++ b/packages/richtext-lexical/src/index.ts @@ -1,9 +1,5 @@ import type { JSONSchema4 } from 'json-schema' -import type { - EditorConfig as LexicalEditorConfig, - SerializedEditorState, - SerializedLexicalNode, -} from 'lexical' +import type { SerializedEditorState, SerializedLexicalNode } from 'lexical' import { afterChangeTraverseFields, @@ -38,7 +34,7 @@ import { getGenerateSchemaMap } from './utilities/generateSchemaMap.js' import { recurseNodeTree } from './utilities/recurseNodeTree.js' import { richTextValidateHOC } from './validate/index.js' -let defaultSanitizedServerEditorConfig: SanitizedServerEditorConfig = null +let defaultSanitizedServerEditorConfig: null | SanitizedServerEditorConfig = null let checkedDependencies = false export function lexicalEditor(props?: LexicalEditorProps): LexicalRichTextAdapterProvider { @@ -106,8 +102,7 @@ export function lexicalEditor(props?: LexicalEditorProps): LexicalRichTextAdapte features = deepCopyObject(defaultEditorFeatures) } - const lexical: LexicalEditorConfig = - props.lexical ?? deepCopyObjectSimple(defaultEditorConfig.lexical) + const lexical = props.lexical ?? deepCopyObjectSimple(defaultEditorConfig.lexical)! resolvedFeatureMap = await loadFeatures({ config, @@ -212,8 +207,8 @@ export function lexicalEditor(props?: LexicalEditorProps): LexicalRichTextAdapte } } if ( - !finalSanitizedEditorConfig.features.nodeHooks.afterChange.size && - !finalSanitizedEditorConfig.features.getSubFields.size + !finalSanitizedEditorConfig.features.nodeHooks?.afterChange?.size && + !finalSanitizedEditorConfig.features.getSubFields?.size ) { return value } @@ -240,9 +235,10 @@ export function lexicalEditor(props?: LexicalEditorProps): LexicalRichTextAdapte // eslint-disable-next-line prefer-const for (let [id, node] of Object.entries(nodeIDMap)) { - const afterChangeHooks = finalSanitizedEditorConfig.features.nodeHooks.afterChange - if (afterChangeHooks?.has(node.type)) { - for (const hook of afterChangeHooks.get(node.type)) { + const afterChangeHooks = finalSanitizedEditorConfig.features.nodeHooks?.afterChange + const afterChangeHooksForNode = afterChangeHooks?.get(node.type) + if (afterChangeHooksForNode) { + for (const hook of afterChangeHooksForNode) { if (!originalNodeIDMap[id]) { console.warn( '(afterChange) No original node found for node with id', @@ -265,12 +261,12 @@ export function lexicalEditor(props?: LexicalEditorProps): LexicalRichTextAdapte }) } } - const subFieldFn = finalSanitizedEditorConfig.features.getSubFields.get(node.type) - const subFieldDataFn = finalSanitizedEditorConfig.features.getSubFieldsData.get( + const subFieldFn = finalSanitizedEditorConfig.features.getSubFields?.get(node.type) + const subFieldDataFn = finalSanitizedEditorConfig.features.getSubFieldsData?.get( node.type, ) - if (subFieldFn) { + if (subFieldFn && subFieldDataFn) { const subFields = subFieldFn({ node, req }) const data = subFieldDataFn({ node, req }) ?? {} const originalData = subFieldDataFn({ node: originalNodeIDMap[id], req }) ?? {} @@ -333,8 +329,8 @@ export function lexicalEditor(props?: LexicalEditorProps): LexicalRichTextAdapte } if ( - !finalSanitizedEditorConfig.features.nodeHooks.afterRead.size && - !finalSanitizedEditorConfig.features.getSubFields.size + !finalSanitizedEditorConfig.features.nodeHooks?.afterRead?.size && + !finalSanitizedEditorConfig.features.getSubFields?.size ) { return value } @@ -346,37 +342,38 @@ export function lexicalEditor(props?: LexicalEditorProps): LexicalRichTextAdapte }) for (let node of flattenedNodes) { - const afterReadHooks = finalSanitizedEditorConfig.features.nodeHooks.afterRead - if (afterReadHooks?.has(node.type)) { - for (const hook of afterReadHooks.get(node.type)) { + const afterReadHooks = finalSanitizedEditorConfig.features.nodeHooks?.afterRead + const afterReadHooksForNode = afterReadHooks?.get(node.type) + if (afterReadHooksForNode) { + for (const hook of afterReadHooksForNode) { node = await hook({ context, - currentDepth, - depth, - draft, - fallbackLocale, - fieldPromises, - findMany, - flattenLocales, - locale, + currentDepth: currentDepth!, + depth: depth!, + draft: draft!, + fallbackLocale: fallbackLocale!, + fieldPromises: fieldPromises!, + findMany: findMany!, + flattenLocales: flattenLocales!, + locale: locale!, node, - overrideAccess, + overrideAccess: overrideAccess!, parentRichTextFieldPath: path, parentRichTextFieldSchemaPath: schemaPath, - populationPromises, + populationPromises: populationPromises!, req, - showHiddenFields, - triggerAccessControl, - triggerHooks, + showHiddenFields: showHiddenFields!, + triggerAccessControl: triggerAccessControl!, + triggerHooks: triggerHooks!, }) } } - const subFieldFn = finalSanitizedEditorConfig.features.getSubFields.get(node.type) - const subFieldDataFn = finalSanitizedEditorConfig.features.getSubFieldsData.get( + const subFieldFn = finalSanitizedEditorConfig.features.getSubFields?.get(node.type) + const subFieldDataFn = finalSanitizedEditorConfig.features.getSubFieldsData?.get( node.type, ) - if (subFieldFn) { + if (subFieldFn && subFieldDataFn) { const subFields = subFieldFn({ node, req }) const data = subFieldDataFn({ node, req }) ?? {} @@ -384,23 +381,23 @@ export function lexicalEditor(props?: LexicalEditorProps): LexicalRichTextAdapte afterReadTraverseFields({ collection, context, - currentDepth, - depth, + currentDepth: currentDepth!, + depth: depth!, doc: data, - draft, - fallbackLocale, - fieldPromises, + draft: draft!, + fallbackLocale: fallbackLocale!, + fieldPromises: fieldPromises!, fields: subFields, - findMany, - flattenLocales, + findMany: findMany!, + flattenLocales: flattenLocales!, global, - locale, - overrideAccess, + locale: locale!, + overrideAccess: overrideAccess!, path, - populationPromises, + populationPromises: populationPromises!, req, schemaPath, - showHiddenFields, + showHiddenFields: showHiddenFields!, siblingDoc: data, triggerAccessControl, triggerHooks, @@ -438,8 +435,8 @@ export function lexicalEditor(props?: LexicalEditorProps): LexicalRichTextAdapte } if ( - !finalSanitizedEditorConfig.features.nodeHooks.beforeChange.size && - !finalSanitizedEditorConfig.features.getSubFields.size + !finalSanitizedEditorConfig.features.nodeHooks?.beforeChange?.size && + !finalSanitizedEditorConfig.features.getSubFields?.size ) { return value } @@ -469,7 +466,7 @@ export function lexicalEditor(props?: LexicalEditorProps): LexicalRichTextAdapte nodes: (value as SerializedEditorState)?.root?.children ?? [], }) - if (siblingDocWithLocales?.[field.name]) { + if (field.name && siblingDocWithLocales?.[field.name]) { recurseNodeTree({ nodeIDMap: originalNodeWithLocalesIDMap, nodes: @@ -480,9 +477,10 @@ export function lexicalEditor(props?: LexicalEditorProps): LexicalRichTextAdapte // eslint-disable-next-line prefer-const for (let [id, node] of Object.entries(nodeIDMap)) { - const beforeChangeHooks = finalSanitizedEditorConfig.features.nodeHooks.beforeChange - if (beforeChangeHooks?.has(node.type)) { - for (const hook of beforeChangeHooks.get(node.type)) { + const beforeChangeHooks = finalSanitizedEditorConfig.features.nodeHooks?.beforeChange + const beforeChangeHooksForNode = beforeChangeHooks?.get(node.type) + if (beforeChangeHooksForNode) { + for (const hook of beforeChangeHooksForNode) { if (!originalNodeIDMap[id]) { console.warn( '(beforeChange) No original node found for node with id', @@ -496,26 +494,26 @@ export function lexicalEditor(props?: LexicalEditorProps): LexicalRichTextAdapte } node = await hook({ context, - errors, - mergeLocaleActions, + errors: errors!, + mergeLocaleActions: mergeLocaleActions!, node, - operation, + operation: operation!, originalNode: originalNodeIDMap[id], originalNodeWithLocales: originalNodeWithLocalesIDMap[id], parentRichTextFieldPath: path, parentRichTextFieldSchemaPath: schemaPath, req, - skipValidation, + skipValidation: skipValidation!, }) } } - const subFieldFn = finalSanitizedEditorConfig.features.getSubFields.get(node.type) - const subFieldDataFn = finalSanitizedEditorConfig.features.getSubFieldsData.get( + const subFieldFn = finalSanitizedEditorConfig.features.getSubFields?.get(node.type) + const subFieldDataFn = finalSanitizedEditorConfig.features.getSubFieldsData?.get( node.type, ) - if (subFieldFn) { + if (subFieldFn && subFieldDataFn) { const subFields = subFieldFn({ node, req }) const data = subFieldDataFn({ node, req }) ?? {} const originalData = subFieldDataFn({ node: originalNodeIDMap[id], req }) ?? {} @@ -533,11 +531,11 @@ export function lexicalEditor(props?: LexicalEditorProps): LexicalRichTextAdapte data, doc: originalData, docWithLocales: originalDataWithLocales ?? {}, - errors, + errors: errors!, fields: subFields, global, - mergeLocaleActions, - operation, + mergeLocaleActions: mergeLocaleActions!, + operation: operation!, path, req, schemaPath, @@ -564,7 +562,7 @@ export function lexicalEditor(props?: LexicalEditorProps): LexicalRichTextAdapte [key: string]: SerializedLexicalNode } = {} - const previousValue = siblingData[field.name] + const previousValue = siblingData[field.name!] recurseNodeTree({ nodeIDMap: newOriginalNodeIDMap, @@ -607,10 +605,10 @@ export function lexicalEditor(props?: LexicalEditorProps): LexicalRichTextAdapte // return value if there are NO hooks if ( - !finalSanitizedEditorConfig.features.nodeHooks.beforeValidate.size && - !finalSanitizedEditorConfig.features.nodeHooks.afterChange.size && - !finalSanitizedEditorConfig.features.nodeHooks.beforeChange.size && - !finalSanitizedEditorConfig.features.getSubFields.size + !finalSanitizedEditorConfig.features.nodeHooks?.beforeValidate?.size && + !finalSanitizedEditorConfig.features.nodeHooks?.afterChange?.size && + !finalSanitizedEditorConfig.features.nodeHooks?.beforeChange?.size && + !finalSanitizedEditorConfig.features.getSubFields?.size ) { return value } @@ -662,7 +660,7 @@ export function lexicalEditor(props?: LexicalEditorProps): LexicalRichTextAdapte /** * Now that the maps for all hooks are set up, we can run the validate hook */ - if (!finalSanitizedEditorConfig.features.nodeHooks.beforeValidate.size) { + if (!finalSanitizedEditorConfig.features.nodeHooks?.beforeValidate?.size) { return value } const nodeIDMap: { @@ -678,8 +676,9 @@ export function lexicalEditor(props?: LexicalEditorProps): LexicalRichTextAdapte for (let [id, node] of Object.entries(nodeIDMap)) { const beforeValidateHooks = finalSanitizedEditorConfig.features.nodeHooks.beforeValidate - if (beforeValidateHooks?.has(node.type)) { - for (const hook of beforeValidateHooks.get(node.type)) { + const beforeValidateHooksForNode = beforeValidateHooks?.get(node.type) + if (beforeValidateHooksForNode) { + for (const hook of beforeValidateHooksForNode) { if (!originalNodeIDMap[id]) { console.warn( '(beforeValidate) No original node found for node with id', @@ -696,19 +695,19 @@ export function lexicalEditor(props?: LexicalEditorProps): LexicalRichTextAdapte node, operation, originalNode: originalNodeIDMap[id], - overrideAccess, + overrideAccess: overrideAccess!, parentRichTextFieldPath: path, parentRichTextFieldSchemaPath: schemaPath, req, }) } } - const subFieldFn = finalSanitizedEditorConfig.features.getSubFields.get(node.type) - const subFieldDataFn = finalSanitizedEditorConfig.features.getSubFieldsData.get( + const subFieldFn = finalSanitizedEditorConfig.features.getSubFields?.get(node.type) + const subFieldDataFn = finalSanitizedEditorConfig.features.getSubFieldsData?.get( node.type, ) - if (subFieldFn) { + if (subFieldFn && subFieldDataFn) { const subFields = subFieldFn({ node, req }) const data = subFieldDataFn({ node, req }) ?? {} const originalData = subFieldDataFn({ node: originalNodeIDMap[id], req }) ?? {} @@ -723,7 +722,7 @@ export function lexicalEditor(props?: LexicalEditorProps): LexicalRichTextAdapte fields: subFields, global, operation, - overrideAccess, + overrideAccess: overrideAccess!, path, req, schemaPath, diff --git a/packages/richtext-lexical/src/lexical/EditorPlugin.tsx b/packages/richtext-lexical/src/lexical/EditorPlugin.tsx index a8b27f8864..5c67dde4b0 100644 --- a/packages/richtext-lexical/src/lexical/EditorPlugin.tsx +++ b/packages/richtext-lexical/src/lexical/EditorPlugin.tsx @@ -8,11 +8,12 @@ export const EditorPlugin: React.FC<{ clientProps: unknown plugin: SanitizedPlugin }> = ({ anchorElem, clientProps, plugin }) => { - if (plugin.position === 'floatingAnchorElem') { + if (plugin.position === 'floatingAnchorElem' && anchorElem) { return ( plugin.Component && ) } + // @ts-expect-error - ts is not able to infer that plugin.Component is of type PluginComponent return plugin.Component && } diff --git a/packages/richtext-lexical/src/lexical/LexicalEditor.tsx b/packages/richtext-lexical/src/lexical/LexicalEditor.tsx index 1a26df2f33..7bcceade44 100644 --- a/packages/richtext-lexical/src/lexical/LexicalEditor.tsx +++ b/packages/richtext-lexical/src/lexical/LexicalEditor.tsx @@ -22,7 +22,7 @@ import { LexicalContentEditable } from './ui/ContentEditable.js' export const LexicalEditor: React.FC< { - editorContainerRef: React.RefObject + editorContainerRef: React.RefObject } & Pick > = (props) => { const { editorConfig, editorContainerRef, onChange } = props @@ -98,13 +98,13 @@ export const LexicalEditor: React.FC< return ( - {editorConfig.features.plugins.map((plugin) => { + {editorConfig.features.plugins?.map((plugin) => { if (plugin.position === 'aboveContainer') { return } })}
- {editorConfig.features.plugins.map((plugin) => { + {editorConfig.features.plugins?.map((plugin) => { if (plugin.position === 'top') { return ( @@ -143,7 +143,7 @@ export const LexicalEditor: React.FC< )} - {editorConfig.features.plugins.map((plugin) => { + {editorConfig.features.plugins?.map((plugin) => { if ( plugin.position === 'floatingAnchorElem' && !(plugin.desktopOnly === true && isSmallWidthViewport) @@ -173,14 +173,14 @@ export const LexicalEditor: React.FC< )} - {editorConfig.features.plugins.map((plugin) => { + {editorConfig.features.plugins?.map((plugin) => { if (plugin.position === 'normal') { return ( ) } })} - {editorConfig.features.plugins.map((plugin) => { + {editorConfig.features.plugins?.map((plugin) => { if (plugin.position === 'bottom') { return ( @@ -188,7 +188,7 @@ export const LexicalEditor: React.FC< } })}
- {editorConfig.features.plugins.map((plugin) => { + {editorConfig.features.plugins?.map((plugin) => { if (plugin.position === 'belowContainer') { return } diff --git a/packages/richtext-lexical/src/lexical/config/client/EditorConfigProvider.tsx b/packages/richtext-lexical/src/lexical/config/client/EditorConfigProvider.tsx index 655bf8ba4d..2455ef9daa 100644 --- a/packages/richtext-lexical/src/lexical/config/client/EditorConfigProvider.tsx +++ b/packages/richtext-lexical/src/lexical/config/client/EditorConfigProvider.tsx @@ -31,6 +31,7 @@ export interface EditorConfigContextType { uuid: string } +// @ts-expect-error: TODO: Fix this const Context: React.Context = createContext({ editorConfig: null, field: null, @@ -46,7 +47,7 @@ export const EditorConfigProvider = ({ }: { children: React.ReactNode editorConfig: SanitizedClientEditorConfig - editorContainerRef: React.RefObject + editorContainerRef: React.RefObject field: LexicalRichTextFieldProps['field'] parentContext?: EditorConfigContextType }): React.ReactNode => { @@ -87,7 +88,7 @@ export const EditorConfigProvider = ({ if (parentContext?.uuid) { parentContext.focusEditor(editorContext) } - childrenEditors.current.forEach((childEditor, childUUID) => { + childrenEditors.current.forEach((childEditor) => { childEditor.focusEditor(editorContext) }) diff --git a/packages/richtext-lexical/src/lexical/config/client/sanitize.ts b/packages/richtext-lexical/src/lexical/config/client/sanitize.ts index 06ae1102a2..2694a274d2 100644 --- a/packages/richtext-lexical/src/lexical/config/client/sanitize.ts +++ b/packages/richtext-lexical/src/lexical/config/client/sanitize.ts @@ -57,7 +57,7 @@ export const sanitizeClientFeatures = ( } if (feature.plugins?.length) { feature.plugins.forEach((plugin, i) => { - sanitized.plugins.push({ + sanitized.plugins?.push({ clientProps: feature.sanitizedClientFeatureProps, Component: plugin.Component, key: feature.key + i, @@ -219,7 +219,7 @@ export function sanitizeClientEditorConfig( return { admin, features: sanitizeClientFeatures(resolvedClientFeatureMap), - lexical, + lexical: lexical!, resolvedFeatureMap: resolvedClientFeatureMap, } } diff --git a/packages/richtext-lexical/src/lexical/config/server/loader.ts b/packages/richtext-lexical/src/lexical/config/server/loader.ts index de159d3dc3..2a837eca35 100644 --- a/packages/richtext-lexical/src/lexical/config/server/loader.ts +++ b/packages/richtext-lexical/src/lexical/config/server/loader.ts @@ -188,9 +188,9 @@ export async function loadFeatures({ : featureProvider.feature resolvedFeatures.set(featureProvider.key, { ...feature, - dependencies: featureProvider.dependencies, - dependenciesPriority: featureProvider.dependenciesPriority, - dependenciesSoft: featureProvider.dependenciesSoft, + dependencies: featureProvider.dependencies!, + dependenciesPriority: featureProvider.dependenciesPriority!, + dependenciesSoft: featureProvider.dependenciesSoft!, key: featureProvider.key, order: loaded, }) diff --git a/packages/richtext-lexical/src/lexical/config/server/sanitize.ts b/packages/richtext-lexical/src/lexical/config/server/sanitize.ts index a57c694a66..7c035ce670 100644 --- a/packages/richtext-lexical/src/lexical/config/server/sanitize.ts +++ b/packages/richtext-lexical/src/lexical/config/server/sanitize.ts @@ -51,18 +51,20 @@ export const sanitizeServerFeatures = ( } if (feature?.hooks?.beforeValidate?.length) { - sanitized.hooks.beforeValidate = sanitized.hooks.beforeValidate.concat( + sanitized.hooks.beforeValidate = sanitized.hooks.beforeValidate?.concat( feature.hooks.beforeValidate, ) } if (feature?.hooks?.beforeChange?.length) { - sanitized.hooks.beforeChange = sanitized.hooks.beforeChange.concat(feature.hooks.beforeChange) + sanitized.hooks.beforeChange = sanitized.hooks.beforeChange?.concat( + feature.hooks.beforeChange, + ) } if (feature?.hooks?.afterRead?.length) { - sanitized.hooks.afterRead = sanitized.hooks.afterRead.concat(feature.hooks.afterRead) + sanitized.hooks.afterRead = sanitized.hooks.afterRead?.concat(feature.hooks.afterRead) } if (feature?.hooks?.afterChange?.length) { - sanitized.hooks.afterChange = sanitized.hooks.afterChange.concat(feature.hooks.afterChange) + sanitized.hooks.afterChange = sanitized.hooks.afterChange?.concat(feature.hooks.afterChange) } if (feature?.i18n) { @@ -90,22 +92,22 @@ export const sanitizeServerFeatures = ( sanitized.converters.html.push(node.converters.html) } if (node?.hooks?.afterChange) { - sanitized.nodeHooks.afterChange.set(nodeType, node.hooks.afterChange) + sanitized.nodeHooks?.afterChange?.set(nodeType, node.hooks.afterChange) } if (node?.hooks?.afterRead) { - sanitized.nodeHooks.afterRead.set(nodeType, node.hooks.afterRead) + sanitized.nodeHooks?.afterRead?.set(nodeType, node.hooks.afterRead) } if (node?.hooks?.beforeChange) { - sanitized.nodeHooks.beforeChange.set(nodeType, node.hooks.beforeChange) + sanitized.nodeHooks?.beforeChange?.set(nodeType, node.hooks.beforeChange) } if (node?.hooks?.beforeValidate) { - sanitized.nodeHooks.beforeValidate.set(nodeType, node.hooks.beforeValidate) + sanitized.nodeHooks?.beforeValidate?.set(nodeType, node.hooks.beforeValidate) } if (node?.getSubFields) { - sanitized.getSubFields.set(nodeType, node.getSubFields) + sanitized.getSubFields?.set(nodeType, node.getSubFields) } if (node?.getSubFieldsData) { - sanitized.getSubFieldsData.set(nodeType, node.getSubFieldsData) + sanitized.getSubFieldsData?.set(nodeType, node.getSubFieldsData) } }) } @@ -129,13 +131,13 @@ export async function sanitizeServerEditorConfig( ): Promise { const resolvedFeatureMap = await loadFeatures({ config, - parentIsLocalized, + parentIsLocalized: parentIsLocalized!, unSanitizedEditorConfig: editorConfig, }) return { features: sanitizeServerFeatures(resolvedFeatureMap), - lexical: editorConfig.lexical, + lexical: editorConfig.lexical!, resolvedFeatureMap, } } diff --git a/packages/richtext-lexical/src/lexical/plugins/SlashMenu/LexicalTypeaheadMenuPlugin/index.tsx b/packages/richtext-lexical/src/lexical/plugins/SlashMenu/LexicalTypeaheadMenuPlugin/index.tsx index c172341575..175ef3631b 100644 --- a/packages/richtext-lexical/src/lexical/plugins/SlashMenu/LexicalTypeaheadMenuPlugin/index.tsx +++ b/packages/richtext-lexical/src/lexical/plugins/SlashMenu/LexicalTypeaheadMenuPlugin/index.tsx @@ -64,8 +64,8 @@ function tryToPositionRange(leadOffset: number, range: Range, editorWindow: Wind return true } -function getQueryTextForSearch(editor: LexicalEditor): null | string { - let text = null +function getQueryTextForSearch(editor: LexicalEditor): string | undefined { + let text editor.getEditorState().read(() => { const selection = $getSelection() if (!$isRangeSelection(selection)) { @@ -207,7 +207,7 @@ export function LexicalTypeaheadMenuPlugin({ if ( !$isRangeSelection(selection) || !selection.isCollapsed() || - text === null || + text === undefined || range === null ) { closeTypeahead() diff --git a/packages/richtext-lexical/src/lexical/plugins/SlashMenu/LexicalTypeaheadMenuPlugin/types.ts b/packages/richtext-lexical/src/lexical/plugins/SlashMenu/LexicalTypeaheadMenuPlugin/types.ts index 93a6bfff98..0b7dadf66c 100644 --- a/packages/richtext-lexical/src/lexical/plugins/SlashMenu/LexicalTypeaheadMenuPlugin/types.ts +++ b/packages/richtext-lexical/src/lexical/plugins/SlashMenu/LexicalTypeaheadMenuPlugin/types.ts @@ -18,7 +18,7 @@ export type SlashMenuItem = { label?: | ((args: { i18n: I18nClient<{}, string> - richTextComponentMap: Map + richTextComponentMap?: Map schemaPath: string }) => string) | string diff --git a/packages/richtext-lexical/src/lexical/plugins/SlashMenu/index.tsx b/packages/richtext-lexical/src/lexical/plugins/SlashMenu/index.tsx index 47ba4a8515..e0c79924d1 100644 --- a/packages/richtext-lexical/src/lexical/plugins/SlashMenu/index.tsx +++ b/packages/richtext-lexical/src/lexical/plugins/SlashMenu/index.tsx @@ -101,11 +101,13 @@ export function SlashMenuPlugin({ let groupWithItems: Array = [] for (const dynamicItem of editorConfig.features.slashMenu.dynamicGroups) { - const dynamicGroupWithItems = dynamicItem({ - editor, - queryString, - }) - groupWithItems = groupWithItems.concat(dynamicGroupWithItems) + if (queryString) { + const dynamicGroupWithItems = dynamicItem({ + editor, + queryString, + }) + groupWithItems = groupWithItems.concat(dynamicGroupWithItems) + } } return groupWithItems @@ -119,6 +121,7 @@ export function SlashMenuPlugin({ if (queryString) { // Filter current groups first + // @ts-expect-error - TODO: fix this groupsWithItems = groupsWithItems.map((group) => { const filteredItems = group.items.filter((item) => { let itemTitle = item.key @@ -207,7 +210,7 @@ export function SlashMenuPlugin({
{groups.map((group) => { let groupTitle = group.key - if (group.label) { + if (group.label && richTextComponentMap) { groupTitle = typeof group.label === 'function' ? group.label({ i18n, richTextComponentMap, schemaPath }) diff --git a/packages/richtext-lexical/src/lexical/plugins/handles/AddBlockHandlePlugin/index.tsx b/packages/richtext-lexical/src/lexical/plugins/handles/AddBlockHandlePlugin/index.tsx index fff5f28cf1..8017c24a18 100644 --- a/packages/richtext-lexical/src/lexical/plugins/handles/AddBlockHandlePlugin/index.tsx +++ b/packages/richtext-lexical/src/lexical/plugins/handles/AddBlockHandlePlugin/index.tsx @@ -93,7 +93,10 @@ function useAddBlockHandle( if (!_emptyBlockElem) { return } - if (hoveredElement?.node !== blockNode || hoveredElement?.elem !== _emptyBlockElem) { + if ( + blockNode && + (hoveredElement?.node !== blockNode || hoveredElement?.elem !== _emptyBlockElem) + ) { setHoveredElement({ elem: _emptyBlockElem, node: blockNode, @@ -134,7 +137,7 @@ function useAddBlockHandle( // Check if blockNode is an empty text node let isEmptyParagraph = true if ( - hoveredElementToUse.node.getType() !== 'paragraph' || + hoveredElementToUse?.node.getType() !== 'paragraph' || hoveredElementToUse.node.getTextContent() !== '' ) { isEmptyParagraph = false @@ -142,11 +145,11 @@ function useAddBlockHandle( if (!isEmptyParagraph) { const newParagraph = $createParagraphNode() - hoveredElementToUse.node.insertAfter(newParagraph) + hoveredElementToUse?.node.insertAfter(newParagraph) setTimeout(() => { hoveredElementToUse = { - elem: editor.getElementByKey(newParagraph.getKey()), + elem: editor.getElementByKey(newParagraph.getKey())!, node: newParagraph, } setHoveredElement(hoveredElementToUse) @@ -160,7 +163,7 @@ function useAddBlockHandle( editor.focus() if ( - hoveredElementToUse.node && + hoveredElementToUse?.node && 'select' in hoveredElementToUse.node && typeof hoveredElementToUse.node.select === 'function' ) { @@ -173,7 +176,7 @@ function useAddBlockHandle( // Otherwise, this won't work setTimeout(() => { editor.dispatchCommand(ENABLE_SLASH_MENU_COMMAND, { - node: hoveredElementToUse.node as ParagraphNode, + node: hoveredElementToUse?.node as ParagraphNode, }) }, 2) diff --git a/packages/richtext-lexical/src/lexical/plugins/handles/DraggableBlockPlugin/debounce.ts b/packages/richtext-lexical/src/lexical/plugins/handles/DraggableBlockPlugin/debounce.ts index 74d9c8ce7f..13ff4f6c27 100644 --- a/packages/richtext-lexical/src/lexical/plugins/handles/DraggableBlockPlugin/debounce.ts +++ b/packages/richtext-lexical/src/lexical/plugins/handles/DraggableBlockPlugin/debounce.ts @@ -1,6 +1,6 @@ 'use client' -export function debounce(func: Function, wait: number) { - let timeout: null | number = null +export function debounce(func: (...args: any[]) => void, wait: number) { + let timeout return function (...args: any[]) { const later = () => { clearTimeout(timeout) diff --git a/packages/richtext-lexical/src/lexical/plugins/handles/DraggableBlockPlugin/getBoundingRectWithoutTransform.ts b/packages/richtext-lexical/src/lexical/plugins/handles/DraggableBlockPlugin/getBoundingRectWithoutTransform.ts index 3033fbd85b..40e0950b25 100644 --- a/packages/richtext-lexical/src/lexical/plugins/handles/DraggableBlockPlugin/getBoundingRectWithoutTransform.ts +++ b/packages/richtext-lexical/src/lexical/plugins/handles/DraggableBlockPlugin/getBoundingRectWithoutTransform.ts @@ -9,7 +9,7 @@ export function getBoundingClientRectWithoutTransform(elem: HTMLElement): DOMRec } const lastNumberOfTransformValue = transformValue.split(',').pop() - rect.y = rect.y - Number(lastNumberOfTransformValue.replace(')', '')) + rect.y = rect.y - Number(lastNumberOfTransformValue?.replace(')', '')) // Return the original bounding rect if no translation is applied return rect diff --git a/packages/richtext-lexical/src/lexical/plugins/handles/DraggableBlockPlugin/index.tsx b/packages/richtext-lexical/src/lexical/plugins/handles/DraggableBlockPlugin/index.tsx index 86fdc4f5a5..041425d273 100644 --- a/packages/richtext-lexical/src/lexical/plugins/handles/DraggableBlockPlugin/index.tsx +++ b/packages/richtext-lexical/src/lexical/plugins/handles/DraggableBlockPlugin/index.tsx @@ -80,7 +80,7 @@ function useDraggableBlockMenu( boundingBox?: DOMRect elem: HTMLElement | null isBelow: boolean - }>(null) + } | null>(null) const { editorConfig } = useEditorConfigContext() @@ -223,7 +223,7 @@ function useDraggableBlockMenu( : -(menuRef?.current?.getBoundingClientRect()?.width ?? 0)), targetLineElem, targetBlockElem, - lastTargetBlock, + lastTargetBlock!, pageY, anchorElem, event, @@ -243,8 +243,8 @@ function useDraggableBlockMenu( isBelow, }) } - } else { - hideTargetLine(targetLineElem, lastTargetBlock?.elem) + } else if (lastTargetBlock?.elem) { + hideTargetLine(targetLineElem, lastTargetBlock.elem) setLastTargetBlock({ boundingBox: targetBlockElem.getBoundingClientRect(), elem: targetBlockElem, @@ -343,8 +343,10 @@ function useDraggableBlockMenu( setTimeout(() => { // add new temp html element to newInsertedElem with the same height and width and the class block-selected // to highlight the new inserted element - const newInsertedElemRect = newInsertedElem.getBoundingClientRect() - + const newInsertedElemRect = newInsertedElem?.getBoundingClientRect() + if (!newInsertedElemRect) { + return + } const highlightElem = document.createElement('div') highlightElem.className = 'lexical-block-highlighter' @@ -414,7 +416,9 @@ function useDraggableBlockMenu( function onDragEnd(): void { isDraggingBlockRef.current = false - hideTargetLine(targetLineRef.current, lastTargetBlock?.elem) + if (lastTargetBlock?.elem) { + hideTargetLine(targetLineRef.current, lastTargetBlock?.elem) + } } return createPortal( diff --git a/packages/richtext-lexical/src/lexical/plugins/handles/utils/getNodeCloseToPoint.ts b/packages/richtext-lexical/src/lexical/plugins/handles/utils/getNodeCloseToPoint.ts index b5707544a1..6885a7203e 100644 --- a/packages/richtext-lexical/src/lexical/plugins/handles/utils/getNodeCloseToPoint.ts +++ b/packages/richtext-lexical/src/lexical/plugins/handles/utils/getNodeCloseToPoint.ts @@ -63,7 +63,6 @@ export function getNodeCloseToPoint(props: Props): Output { point: { x, y }, startIndex = 0, useEdgeAsDefault = false, - verbose = false, } = props // Use cache @@ -104,12 +103,12 @@ export function getNodeCloseToPoint(props: Props): Output { editor.getElementByKey(topLevelNodeKeys[topLevelNodeKeys.length - 1]), ] - const [firstNodeRect, lastNodeRect] = [ - getBoundingClientRectWithoutTransform(firstNode), - getBoundingClientRectWithoutTransform(lastNode), - ] + if (firstNode && lastNode) { + const [firstNodeRect, lastNodeRect] = [ + getBoundingClientRectWithoutTransform(firstNode), + getBoundingClientRectWithoutTransform(lastNode), + ] - if (firstNodeRect && lastNodeRect) { if (y < firstNodeRect.top) { closestBlockElem.blockElem = firstNode closestBlockElem.distance = firstNodeRect.top - y diff --git a/packages/richtext-lexical/src/lexical/utils/setFloatingElemPosition.ts b/packages/richtext-lexical/src/lexical/utils/setFloatingElemPosition.ts index 173aeed298..78670d9b81 100644 --- a/packages/richtext-lexical/src/lexical/utils/setFloatingElemPosition.ts +++ b/packages/richtext-lexical/src/lexical/utils/setFloatingElemPosition.ts @@ -16,7 +16,7 @@ export function setFloatingElemPosition(args: { specialHandlingForCaret?: boolean targetRect: ClientRect | null verticalGap?: number -}): number { +}): number | undefined { const { alwaysDisplayOnTop = false, anchorElem, diff --git a/packages/richtext-lexical/src/populateGraphQL/populate.ts b/packages/richtext-lexical/src/populateGraphQL/populate.ts index 9f78a0b747..bf208287fc 100644 --- a/packages/richtext-lexical/src/populateGraphQL/populate.ts +++ b/packages/richtext-lexical/src/populateGraphQL/populate.ts @@ -28,7 +28,7 @@ export const populate = async ({ collectionSlug: string id: number | string } & Arguments): Promise => { - const shouldPopulate = depth && currentDepth <= depth + const shouldPopulate = depth && currentDepth! <= depth // usually depth is checked within recursivelyPopulateFieldsForGraphQL. But since this populate function can be called outside of that (in rest afterRead node hooks) we need to check here too if (!shouldPopulate) { return @@ -36,18 +36,18 @@ export const populate = async ({ const dataRef = data as Record - const doc = await req.payloadDataLoader.load( + const doc = await req.payloadDataLoader?.load( createDataloaderCacheKey({ collectionSlug, - currentDepth: currentDepth + 1, + currentDepth: currentDepth! + 1, depth, docID: id as string, draft, - fallbackLocale: req.fallbackLocale, - locale: req.locale, + fallbackLocale: req.fallbackLocale!, + locale: req.locale!, overrideAccess, showHiddenFields, - transactionID: req.transactionID, + transactionID: req.transactionID!, }), ) diff --git a/packages/richtext-lexical/src/populateGraphQL/populateLexicalPopulationPromises.ts b/packages/richtext-lexical/src/populateGraphQL/populateLexicalPopulationPromises.ts index 16615c6626..56f5378c52 100644 --- a/packages/richtext-lexical/src/populateGraphQL/populateLexicalPopulationPromises.ts +++ b/packages/richtext-lexical/src/populateGraphQL/populateLexicalPopulationPromises.ts @@ -8,7 +8,9 @@ import { recurseNodes } from '../utilities/forEachNodeRecursively.js' export type Args = { editorPopulationPromises: Map> -} & Parameters['graphQLPopulationPromises']>[0] +} & Parameters< + NonNullable['graphQLPopulationPromises']> +>[0] /** * Appends all new populationPromises to the populationPromises prop @@ -29,7 +31,7 @@ export const populateLexicalPopulationPromises = ({ showHiddenFields, siblingDoc, }: Args) => { - const shouldPopulate = depth && currentDepth <= depth + const shouldPopulate = depth && currentDepth! <= depth if (!shouldPopulate) { return @@ -37,11 +39,12 @@ export const populateLexicalPopulationPromises = ({ recurseNodes({ callback: (node) => { - if (editorPopulationPromises?.has(node.type)) { - for (const promise of editorPopulationPromises.get(node.type)) { + const editorPopulationPromisesOfNodeType = editorPopulationPromises?.get(node.type) + if (editorPopulationPromisesOfNodeType) { + for (const promise of editorPopulationPromisesOfNodeType) { promise({ context, - currentDepth, + currentDepth: currentDepth!, depth, draft, editorPopulationPromises, @@ -50,7 +53,7 @@ export const populateLexicalPopulationPromises = ({ findMany, flattenLocales, node, - overrideAccess, + overrideAccess: overrideAccess!, populationPromises, req, showHiddenFields, diff --git a/packages/richtext-lexical/src/populateGraphQL/recursivelyPopulateFieldsForGraphQL.ts b/packages/richtext-lexical/src/populateGraphQL/recursivelyPopulateFieldsForGraphQL.ts index b265a7af44..44bcdf0d1a 100644 --- a/packages/richtext-lexical/src/populateGraphQL/recursivelyPopulateFieldsForGraphQL.ts +++ b/packages/richtext-lexical/src/populateGraphQL/recursivelyPopulateFieldsForGraphQL.ts @@ -51,13 +51,13 @@ export const recursivelyPopulateFieldsForGraphQL = ({ depth, doc: data as any, // Looks like it's only needed for hooks and access control, so doesn't matter what we pass here right now draft, - fallbackLocale: req.fallbackLocale, + fallbackLocale: req.fallbackLocale!, fieldPromises, fields, findMany, flattenLocales, global: null, // Pass from core? This is only needed for hooks, so we can leave this null for now - locale: req.locale, + locale: req.locale!, overrideAccess, path: [], populationPromises, // This is not the same as populationPromises passed into this recurseNestedFields. These are just promises resolved at the very end. diff --git a/packages/richtext-lexical/src/utilities/fieldsDrawer/DrawerContent.tsx b/packages/richtext-lexical/src/utilities/fieldsDrawer/DrawerContent.tsx index 714752e120..b62b1d3090 100644 --- a/packages/richtext-lexical/src/utilities/fieldsDrawer/DrawerContent.tsx +++ b/packages/richtext-lexical/src/utilities/fieldsDrawer/DrawerContent.tsx @@ -1,5 +1,4 @@ 'use client' -import type { FormProps } from '@payloadcms/ui' import type { ClientField, FormState } from 'payload' import { @@ -42,14 +41,14 @@ export const DrawerContent: React.FC { const awaitInitialState = async () => { const { state } = await getFormState({ apiRoute: config.routes.api, body: { - id, + id: id!, data: data ?? {}, operation: 'update', schemaPath: schemaFieldsPath, @@ -63,12 +62,12 @@ export const DrawerContent: React.FC { const { state } = await getFormState({ apiRoute: config.routes.api, body: { - id, + id: id!, formState: prevFormState, operation: 'update', schemaPath: schemaFieldsPath, diff --git a/packages/richtext-lexical/src/utilities/generateComponentMap.tsx b/packages/richtext-lexical/src/utilities/generateComponentMap.tsx index b720a9f07d..d5cc9c2cf4 100644 --- a/packages/richtext-lexical/src/utilities/generateComponentMap.tsx +++ b/packages/richtext-lexical/src/utilities/generateComponentMap.tsx @@ -42,6 +42,7 @@ export const getGenerateComponentMap = for (const componentKey in components) { const payloadComponent = components[componentKey] + // @ts-expect-error - TODO: fix this const mappedComponent: MappedComponent = createMappedComponent( payloadComponent, { diff --git a/packages/richtext-lexical/src/utilities/migrateSlateToLexical/migrateDocumentFieldsRecursively.ts b/packages/richtext-lexical/src/utilities/migrateSlateToLexical/migrateDocumentFieldsRecursively.ts index 1e7f262313..522c6d1921 100644 --- a/packages/richtext-lexical/src/utilities/migrateSlateToLexical/migrateDocumentFieldsRecursively.ts +++ b/packages/richtext-lexical/src/utilities/migrateSlateToLexical/migrateDocumentFieldsRecursively.ts @@ -2,13 +2,16 @@ import type { Field } from 'payload' import { fieldAffectsData, fieldHasSubFields, fieldIsArrayType, tabHasName } from 'payload/shared' -import type { SlateNodeConverter } from '../../features/migrations/slateToLexical/converter/types.js' +import type { + SlateNode, + SlateNodeConverter, +} from '../../features/migrations/slateToLexical/converter/types.js' import type { LexicalRichTextAdapter } from '../../types.js' import { convertSlateToLexical } from '../../features/migrations/slateToLexical/converter/index.js' type NestedRichTextFieldsArgs = { - data: unknown + data: Record fields: Field[] found: number @@ -23,7 +26,7 @@ export const migrateDocumentFieldsRecursively = ({ if (fieldHasSubFields(field) && !fieldIsArrayType(field)) { if (fieldAffectsData(field) && typeof data[field.name] === 'object') { found += migrateDocumentFieldsRecursively({ - data: data[field.name], + data: data[field.name] as Record, fields: field.fields, found, }) @@ -37,18 +40,18 @@ export const migrateDocumentFieldsRecursively = ({ } else if (field.type === 'tabs') { field.tabs.forEach((tab) => { found += migrateDocumentFieldsRecursively({ - data: tabHasName(tab) ? data[tab.name] : data, + data: (tabHasName(tab) ? data[tab.name] : data) as Record, fields: tab.fields, found, }) }) } else if (Array.isArray(data[field.name])) { if (field.type === 'blocks') { - data[field.name].forEach((row, i) => { + ;(data[field.name] as Array>).forEach((row, i) => { const block = field.blocks.find(({ slug }) => slug === row?.blockType) if (block) { found += migrateDocumentFieldsRecursively({ - data: data[field.name][i], + data: (data[field.name] as Array>)[i], fields: block.fields, found, }) @@ -57,9 +60,9 @@ export const migrateDocumentFieldsRecursively = ({ } if (field.type === 'array') { - data[field.name].forEach((_, i) => { + ;(data[field.name] as Array>).forEach((_, i) => { found += migrateDocumentFieldsRecursively({ - data: data[field.name][i], + data: (data[field.name] as Array>)[i], fields: field.fields, found, }) @@ -82,8 +85,8 @@ export const migrateDocumentFieldsRecursively = ({ } data[field.name] = convertSlateToLexical({ - converters, - slateData: data[field.name], + converters: converters!, + slateData: data[field.name] as SlateNode[], }) found++ diff --git a/packages/richtext-lexical/src/utilities/upgradeLexicalData/upgradeDocumentFieldsRecursively.ts b/packages/richtext-lexical/src/utilities/upgradeLexicalData/upgradeDocumentFieldsRecursively.ts index 28652bced3..6aeef41a38 100644 --- a/packages/richtext-lexical/src/utilities/upgradeLexicalData/upgradeDocumentFieldsRecursively.ts +++ b/packages/richtext-lexical/src/utilities/upgradeLexicalData/upgradeDocumentFieldsRecursively.ts @@ -9,7 +9,7 @@ import type { LexicalRichTextAdapter } from '../../types.js' import { getEnabledNodes } from '../../lexical/nodes/index.js' type NestedRichTextFieldsArgs = { - data: unknown + data: Record fields: Field[] found: number @@ -24,7 +24,7 @@ export const upgradeDocumentFieldsRecursively = ({ if (fieldHasSubFields(field) && !fieldIsArrayType(field)) { if (fieldAffectsData(field) && typeof data[field.name] === 'object') { found += upgradeDocumentFieldsRecursively({ - data: data[field.name], + data: data[field.name] as Record, fields: field.fields, found, }) @@ -38,18 +38,18 @@ export const upgradeDocumentFieldsRecursively = ({ } else if (field.type === 'tabs') { field.tabs.forEach((tab) => { found += upgradeDocumentFieldsRecursively({ - data: tabHasName(tab) ? data[tab.name] : data, + data: (tabHasName(tab) ? data[tab.name] : data) as Record, fields: tab.fields, found, }) }) } else if (Array.isArray(data[field.name])) { if (field.type === 'blocks') { - data[field.name].forEach((row, i) => { + ;(data[field.name] as Record[]).forEach((row, i) => { const block = field.blocks.find(({ slug }) => slug === row?.blockType) if (block) { found += upgradeDocumentFieldsRecursively({ - data: data[field.name][i], + data: (data[field.name] as Record[])[i], fields: block.fields, found, }) @@ -58,9 +58,9 @@ export const upgradeDocumentFieldsRecursively = ({ } if (field.type === 'array') { - data[field.name].forEach((_, i) => { + ;(data[field.name] as Record[]).forEach((_, i) => { found += upgradeDocumentFieldsRecursively({ - data: data[field.name][i], + data: (data[field.name] as Record[])[i], fields: field.fields, found, }) @@ -72,14 +72,14 @@ export const upgradeDocumentFieldsRecursively = ({ field.type === 'richText' && data[field.name] && !Array.isArray(data[field.name]) && - 'root' in data[field.name] + 'root' in (data[field.name] as Record) ) { // Lexical richText const editor: LexicalRichTextAdapter = field.editor as LexicalRichTextAdapter if (editor && typeof editor === 'object') { if ('features' in editor && editor.features?.length) { // Load lexical editor into lexical, then save it immediately - const editorState: SerializedEditorState = data[field.name] + const editorState = data[field.name] as SerializedEditorState const headlessEditor = createHeadlessEditor({ nodes: getEnabledNodes({ diff --git a/packages/richtext-lexical/src/validate/validateNodes.ts b/packages/richtext-lexical/src/validate/validateNodes.ts index df5942d584..109ae89a5c 100644 --- a/packages/richtext-lexical/src/validate/validateNodes.ts +++ b/packages/richtext-lexical/src/validate/validateNodes.ts @@ -17,12 +17,8 @@ export async function validateNodes({ }): Promise { for (const node of nodes) { // Validate node - if ( - nodeValidations && - typeof nodeValidations?.has === 'function' && - nodeValidations?.has(node.type) - ) { - const validations = nodeValidations.get(node.type) + const validations = nodeValidations.get(node.type) + if (validations) { for (const validation of validations) { const validationResult = await validation({ node, diff --git a/packages/richtext-lexical/tsconfig.json b/packages/richtext-lexical/tsconfig.json index b213f9876f..79ab3f11ad 100644 --- a/packages/richtext-lexical/tsconfig.json +++ b/packages/richtext-lexical/tsconfig.json @@ -5,6 +5,7 @@ "noEmit": false /* Do not emit outputs. */, "emitDeclarationOnly": true, "esModuleInterop": true, + "strictNullChecks": true, "outDir": "./dist" /* Specify an output folder for all emitted files. */, "rootDir": "./src" /* Specify the root folder within your source files. */, }, diff --git a/packages/ui/src/forms/NullifyField/index.tsx b/packages/ui/src/forms/NullifyField/index.tsx index 90e9bf9fa9..ce128f9e8b 100644 --- a/packages/ui/src/forms/NullifyField/index.tsx +++ b/packages/ui/src/forms/NullifyField/index.tsx @@ -55,6 +55,7 @@ export const NullifyLocaleField: React.FC = ({