chore(richtext-lexical): add noImplicitAny to the richtext-lexical package (#8763)

This commit is contained in:
Germán Jabloñski
2024-11-20 22:46:28 -03:00
committed by GitHub
parent 025d917fa0
commit 1f92b5b412
24 changed files with 82 additions and 48 deletions

View File

@@ -21,11 +21,17 @@ export function parseJSXToAST({
// Remove "position" keys
const parseTree = (tree: object) => {
for (const key in tree) {
// @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve
if (key === 'position' && tree[key].start && tree[key].end) {
// @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve
delete tree[key]
// @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve
} else if (typeof tree[key] === 'object') {
// @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve
parseTree(tree[key])
// @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve
} else if (Array.isArray(tree[key])) {
// @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve
for (const item of tree[key]) {
parseTree(item)
}

View File

@@ -162,7 +162,7 @@ export const BlockComponent: React.FC<Props> = (props) => {
const { i18n, t } = useTranslation<object, string>()
const onChange = useCallback(
async ({ formState: prevFormState, submit }) => {
async ({ formState: prevFormState, submit }: { formState: FormState; submit: boolean }) => {
abortAndIgnore(onChangeAbortControllerRef.current)
const controller = new AbortController()
@@ -246,7 +246,9 @@ export const BlockComponent: React.FC<Props> = (props) => {
})
}, [editor, nodeKey])
// @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve
const CustomLabel = initialState?.['_components']?.customComponents?.BlockLabel
// @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve
const CustomBlock = initialState?.['_components']?.customComponents?.Block
const blockDisplayName = clientBlock?.labels?.singular

View File

@@ -3,7 +3,7 @@ import React from 'react'
import { useInlineBlockComponentContext } from '../index.js'
export const InlineBlockContainer = ({ children }) => {
export const InlineBlockContainer = ({ children }: { children: React.ReactNode }) => {
const { InlineBlockContainer } = useInlineBlockComponentContext()
return InlineBlockContainer ? <InlineBlockContainer>{children}</InlineBlockContainer> : null

View File

@@ -219,7 +219,7 @@ export const InlineBlockComponent: React.FC<Props> = (props) => {
* HANDLE ONCHANGE
*/
const onChange = useCallback(
async ({ formState: prevFormState }) => {
async ({ formState: prevFormState }: { formState: FormState }) => {
abortAndIgnore(onChangeAbortControllerRef.current)
const controller = new AbortController()
@@ -258,7 +258,7 @@ export const InlineBlockComponent: React.FC<Props> = (props) => {
* HANDLE FORM SUBMIT
*/
const onFormSubmit = useCallback(
(formState) => {
(formState: FormState) => {
const newData: any = reduceFieldsToValues(formState)
newData.blockType = formData.blockType
editor.update(() => {
@@ -270,8 +270,9 @@ export const InlineBlockComponent: React.FC<Props> = (props) => {
},
[editor, nodeKey, formData],
)
// @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve
const CustomLabel = initialState?.['_components']?.customComponents?.BlockLabel
// @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve
const CustomBlock = initialState?.['_components']?.customComponents?.Block
const RemoveButton = useMemo(
@@ -314,7 +315,7 @@ export const InlineBlockComponent: React.FC<Props> = (props) => {
const InlineBlockContainer = useMemo(
() =>
({ children }) => (
({ children }: { children: React.ReactNode }) => (
<div
className={[
baseClass,

View File

@@ -1,5 +1,6 @@
'use client'
import type { I18nClient } from '@payloadcms/translations'
import type { BlocksFieldClient, ClientBlock } from 'payload'
import { getTranslation } from '@payloadcms/translations'
@@ -82,7 +83,7 @@ export const BlocksFeatureClient = createClientFeature(
} as SlashMenuItem
}),
key: 'blocks',
label: ({ i18n }) => {
label: ({ i18n }: { i18n: I18nClient<object, 'lexical:blocks:label'> }) => {
return i18n.t('lexical:blocks:label')
},
}
@@ -110,7 +111,11 @@ export const BlocksFeatureClient = createClientFeature(
} as SlashMenuItem
}),
key: 'inlineBlocks',
label: ({ i18n }) => {
label: ({
i18n,
}: {
i18n: I18nClient<object, 'lexical:blocks:inlineBlocks:label'>
}) => {
return i18n.t('lexical:blocks:inlineBlocks:label')
},
}

View File

@@ -1,3 +1,5 @@
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-nocheck - not worth it migrate jsdoc to tsdoc
'use client'
// Copied & modified from https://github.com/lodash/lodash/blob/main/src/debounce.ts
/*
@@ -242,4 +244,5 @@ function debounce(func, wait, options) {
return debounced
}
// eslint-disable-next-line no-restricted-exports
export default debounce

View File

@@ -87,8 +87,7 @@ const Component: React.FC<Props> = (props) => {
})
}, [editor, nodeKey])
const updateRelationship = React.useCallback(
({ doc }) => {
const updateRelationship = React.useCallback(() => {
setParams({
...initialParams,
cacheBust, // do this to get the usePayloadAPI to re-fetch the data even though the URL string hasn't changed
@@ -96,9 +95,7 @@ const Component: React.FC<Props> = (props) => {
closeDocumentDrawer()
dispatchCacheBust()
},
[cacheBust, setParams, closeDocumentDrawer],
)
}, [cacheBust, setParams, closeDocumentDrawer])
const $onDelete = useCallback(
(payload: KeyboardEvent) => {

View File

@@ -68,7 +68,7 @@ const RelationshipDrawerComponent: React.FC<Props> = ({ enabledCollectionSlugs }
}, [editor, openListDrawer])
const onSelect = useCallback(
({ collectionSlug, docID }) => {
({ collectionSlug, docID }: { collectionSlug: string; docID: number | string }) => {
insertRelationship({
editor,
relationTo: collectionSlug,

View File

@@ -209,7 +209,7 @@ function InlineToolbar({
const isLinkEditorVisible =
possibleLinkEditor !== null &&
'style' in possibleLinkEditor &&
possibleLinkEditor?.style?.['opacity'] === '1'
possibleLinkEditor?.style?.['opacity' as keyof typeof possibleLinkEditor.style] === '1'
const rootElement = editor.getRootElement()
if (

View File

@@ -1,5 +1,5 @@
'use client'
import type { ClientCollectionConfig, Data } from 'payload'
import type { ClientCollectionConfig, Data, FormState, JsonObject } from 'payload'
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext.js'
import { useLexicalNodeSelection } from '@lexical/react/useLexicalNodeSelection.js'
@@ -183,7 +183,7 @@ const Component: React.FC<ElementProps> = (props) => {
).collections?.[relatedCollection.slug]?.hasExtraFields
const onExtraFieldsDrawerSubmit = useCallback(
(_, data) => {
(_: FormState, data: JsonObject) => {
// Update lexical node (with key nodeKey) with new data
editor.update(() => {
const uploadNode: null | UploadNode = $getNodeByKey(nodeKey)

View File

@@ -77,7 +77,7 @@ const UploadDrawerComponent: React.FC<Props> = ({ enabledCollectionSlugs }) => {
}, [editor, openListDrawer])
const onSelect = useCallback(
({ collectionSlug, docID }) => {
({ collectionSlug, docID }: { collectionSlug: string; docID: number | string }) => {
closeListDrawer()
insertUpload({
editor,

View File

@@ -1,5 +1,6 @@
'use client'
import type { EditorState, SerializedEditorState } from 'lexical'
import type { Validate } from 'payload'
import { FieldLabel, useEditDepth, useField, withCondition } from '@payloadcms/ui'
import { mergeFieldStyles } from '@payloadcms/ui/shared'
@@ -10,9 +11,9 @@ import type { SanitizedClientEditorConfig } from '../lexical/config/types.js'
import type { LexicalRichTextFieldProps } from '../types.js'
import { LexicalProvider } from '../lexical/LexicalProvider.js'
import '../lexical/theme/EditorTheme.scss'
import './bundled.css'
import './index.scss'
import '../lexical/theme/EditorTheme.scss'
const baseClass = 'rich-text-lexical'
@@ -41,11 +42,13 @@ const RichTextComponent: React.FC<
const editDepth = useEditDepth()
const memoizedValidate = useCallback(
const memoizedValidate = useCallback<Validate>(
(value, validationOptions) => {
if (typeof validate === 'function') {
// @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve
return validate(value, { ...validationOptions, required })
}
return true
},
// Important: do not add props to the dependencies array.
// This would cause an infinite loop and endless re-rendering.
@@ -63,7 +66,6 @@ const RichTextComponent: React.FC<
value,
} = useField<SerializedEditorState>({
path,
// @ts-expect-error: TODO: Fix this
validate: memoizedValidate,
})
@@ -115,7 +117,7 @@ const RichTextComponent: React.FC<
)
}
function fallbackRender({ error }): React.ReactElement {
function fallbackRender({ error }: { error: Error }) {
// Call resetErrorBoundary() to reset the error boundary and retry the render.
return (

View File

@@ -73,8 +73,8 @@ export const RscEntryLexicalField: React.FC<
}
for (const key in props) {
if (!props[key]) {
delete props[key]
if (props[key as keyof LexicalRichTextFieldProps] === undefined) {
delete props[key as keyof LexicalRichTextFieldProps]
}
}

View File

@@ -66,7 +66,7 @@ export function lexicalEditor(props?: LexicalEditorProps): LexicalRichTextAdapte
})
}
let features: FeatureProviderServer<any, any, any>[] = []
let features: FeatureProviderServer<unknown, unknown, unknown>[] = []
let resolvedFeatureMap: ResolvedServerFeatureMap
let finalSanitizedEditorConfig: SanitizedServerEditorConfig // For server only
@@ -124,12 +124,12 @@ export function lexicalEditor(props?: LexicalEditorProps): LexicalRichTextAdapte
const featureI18n = finalSanitizedEditorConfig.features.i18n
for (const lang in i18n) {
if (!featureI18n[lang]) {
featureI18n[lang] = {
if (!featureI18n[lang as keyof typeof featureI18n]) {
featureI18n[lang as keyof typeof featureI18n] = {
lexical: {},
}
}
// @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve
featureI18n[lang].lexical.general = i18n[lang]
}
@@ -207,6 +207,8 @@ export function lexicalEditor(props?: LexicalEditorProps): LexicalRichTextAdapte
) {
return value
}
// TO-DO: We should not use context, as it is intended for external use only
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const context: any = _context
const nodeIDMap: {
[key: string]: SerializedLexicalNode
@@ -439,6 +441,8 @@ export function lexicalEditor(props?: LexicalEditorProps): LexicalRichTextAdapte
return value
}
// TO-DO: We should not use context, as it is intended for external use only
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const context: any = _context
const nodeIDMap: {
[key: string]: SerializedLexicalNode

View File

@@ -26,7 +26,14 @@ export type LexicalProviderProps = {
value: SerializedEditorState
}
const NestProviders = ({ children, providers }) => {
const NestProviders = ({
children,
providers,
}: {
children: React.ReactNode
// eslint-disable-next-line @typescript-eslint/no-explicit-any
providers: any[]
}) => {
if (!providers?.length) {
return children
}

View File

@@ -69,11 +69,12 @@ export const sanitizeServerFeatures = (
if (feature?.i18n) {
for (const lang in feature.i18n) {
if (!sanitized.i18n[lang]) {
sanitized.i18n[lang] = {
if (!sanitized.i18n[lang as keyof typeof sanitized.i18n]) {
sanitized.i18n[lang as keyof typeof sanitized.i18n] = {
lexical: {},
}
}
// @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve
sanitized.i18n[lang].lexical[feature.key] = feature.i18n[lang]
}
}

View File

@@ -126,7 +126,7 @@ function useAddBlockHandle(
}, [anchorElem, hoveredElement, blockHandleHorizontalOffset])
const handleAddClick = useCallback(
(event) => {
(event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
let hoveredElementToUse = hoveredElement
if (!hoveredElementToUse?.node) {
return
@@ -189,6 +189,7 @@ function useAddBlockHandle(
return createPortal(
<React.Fragment>
<button
aria-label="Add block"
className="icon add-block-menu"
onClick={(event) => {
handleAddClick(event)

View File

@@ -1,10 +1,10 @@
'use client'
export function debounce(func: (...args: any[]) => void, wait: number) {
let timeout
return function (...args: any[]) {
export function debounce(func: (...args: undefined[]) => void, wait: number) {
let timeout: number | string | undefined
return function (...args: undefined[]) {
const later = () => {
clearTimeout(timeout)
timeout = null
timeout = undefined
func(...args)
}

View File

@@ -75,7 +75,7 @@ export const DrawerContent: React.FC<Omit<FieldsDrawerProps, 'drawerSlug' | 'dra
}, [schemaFieldsPath, id, data, getFormState, collectionSlug, globalSlug, getDocPreferences])
const onChange = useCallback(
async ({ formState: prevFormState }) => {
async ({ formState: prevFormState }: { formState: FormState }) => {
abortAndIgnore(onChangeAbortControllerRef.current)
const controller = new AbortController()

View File

@@ -6,6 +6,7 @@ import type { ResolvedServerFeatureMap } from '../features/typesServer.js'
export const getGenerateImportMap =
(args: { resolvedFeatureMap: ResolvedServerFeatureMap }): RichTextAdapter['generateImportMap'] =>
// @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve
({ addToImportMap, baseDir, config, importMap, imports }) => {
addToImportMap('@payloadcms/richtext-lexical/rsc#RscEntryLexicalCell')
addToImportMap('@payloadcms/richtext-lexical/rsc#RscEntryLexicalField')
@@ -22,6 +23,7 @@ export const getGenerateImportMap =
imports,
})
} else if (resolvedFeature.componentImports?.length) {
// @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve
resolvedFeature.componentImports.forEach((component) => {
addToImportMap(component)
})

View File

@@ -62,6 +62,7 @@ export function frontmatterToObject(frontmatter: string): Record<string, any> {
const [key, ...valueParts] = line.split(':')
const value = valueParts.join(':').trim()
// @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve
result[key.trim()] = value
}
}

View File

@@ -1,3 +1,4 @@
/* eslint-disable no-console */
import type { CollectionConfig, Field, GlobalConfig, Payload } from 'payload'
import { migrateDocumentFieldsRecursively } from './migrateDocumentFieldsRecursively.js'
@@ -15,7 +16,7 @@ import { migrateDocumentFieldsRecursively } from './migrateDocumentFieldsRecursi
export async function migrateSlateToLexical({ payload }: { payload: Payload }) {
const collections = payload.config.collections
const errors = []
const errors: unknown[] = []
const allLocales = payload.config.localization ? payload.config.localization.localeCodes : [null]
@@ -56,7 +57,7 @@ async function migrateGlobal({
locale,
payload,
}: {
errors: any[]
errors: unknown[]
global: GlobalConfig
locale: null | string
payload: Payload
@@ -105,7 +106,7 @@ async function migrateCollection({
}: {
collection: CollectionConfig
cur: number
errors: any[]
errors: unknown[]
locale: null | string
max: number
payload: Payload

View File

@@ -1,4 +1,4 @@
import type { SerializedEditorState, SerializedParagraphNode } from 'lexical'
import type { SerializedEditorState, SerializedParagraphNode, SerializedTextNode } from 'lexical'
import type { RichTextField, Validate } from 'payload'
import type { SanitizedServerEditorConfig } from '../lexical/config/types.js'
@@ -32,7 +32,7 @@ export const richTextValidateHOC = ({
} else if (paragraphNode?.children?.length === 1) {
const paragraphNodeChild = paragraphNode?.children[0]
if (paragraphNodeChild.type === 'text') {
if (!paragraphNodeChild?.['text']?.length) {
if (!(paragraphNodeChild as SerializedTextNode | undefined)?.['text']?.length) {
hasOnlyEmptyParagraph = true
}
}

View File

@@ -6,6 +6,7 @@
"emitDeclarationOnly": true,
"esModuleInterop": true,
"strictNullChecks": true,
"noImplicitAny": true,
"outDir": "./dist" /* Specify an output folder for all emitted files. */,
"rootDir": "./src" /* Specify the root folder within your source files. */,
},