diff --git a/package.json b/package.json index 1e57a872822..e6c5c73a557 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,7 @@ "remark-emoji": "^2.1.0", "remark-highlight.js": "^5.2.0", "remark-parse": "^7.0.2", - "remark-rehype": "^6.0.0", + "remark-rehype": "^7.0.0", "resize-observer-polyfill": "^1.5.0", "tabbable": "^3.0.0", "text-diff": "^1.0.1", diff --git a/scripts/babel/fetch-i18n-strings.js b/scripts/babel/fetch-i18n-strings.js index cfca578162a..0ee92a3f56e 100644 --- a/scripts/babel/fetch-i18n-strings.js +++ b/scripts/babel/fetch-i18n-strings.js @@ -85,7 +85,7 @@ const files = glob.sync( '**/*.@(js|ts|tsx)', { cwd: srcDir, realpath: true }, ).filter(filepath => { - if (filepath.endsWith('index.d.ts')) return false; + if (filepath.endsWith('.d.ts')) return false; if (filepath.endsWith('test.ts')) return false; if (filepath.endsWith('test.tsx')) return false; if (filepath.endsWith('test.js')) return false; diff --git a/src/components/markdown_editor/index.ts b/src/components/markdown_editor/index.ts index 49cda3bc240..5c1f4b4a7f4 100644 --- a/src/components/markdown_editor/index.ts +++ b/src/components/markdown_editor/index.ts @@ -24,3 +24,11 @@ export { defaultProcessingPlugins, } from './markdown_editor'; export { EuiMarkdownContext } from './markdown_context'; +export { + EuiMarkdownParseError, + EuiMarkdownAstNode, + EuiMarkdownAstNodePosition, + EuiMarkdownFormatting, + EuiMarkdownEditorUiPlugin, + RemarkRehypeHandler, +} from './markdown_types'; diff --git a/src/components/markdown_editor/markdown_actions.ts b/src/components/markdown_editor/markdown_actions.ts index 2946e245626..2d522fa8ef2 100644 --- a/src/components/markdown_editor/markdown_actions.ts +++ b/src/components/markdown_editor/markdown_actions.ts @@ -166,11 +166,12 @@ class MarkdownActions { ...defaults, ...incomingStyle, }; - const editor = document.getElementById(this.editorID); + const editor = document.getElementById( + this.editorID + ) as HTMLTextAreaElement; if (editor) { editor.focus(); - // @ts-ignore TODO styleSelectedText(editor, outgoingStyle); } } @@ -262,15 +263,11 @@ export function insertText( * https://hustle.bizongo.in/simulate-react-on-change-on-controlled-components-baa336920e04 */ const inputEvent = new Event('input', { bubbles: true }); - const nativeInputValueSetter = - // @ts-ignore TODO - Object.getOwnPropertyDescriptor( - // @ts-ignore TODO - window.HTMLTextAreaElement.prototype, - 'value' - ).set; - // @ts-ignore TODO - nativeInputValueSetter.call(textarea, before + text + after); + const nativeInputValueSetter = Object.getOwnPropertyDescriptor( + window.HTMLTextAreaElement.prototype, + 'value' + )!.set; + nativeInputValueSetter!.call(textarea, before + text + after); textarea.dispatchEvent(inputEvent); } diff --git a/src/components/markdown_editor/markdown_editor.tsx b/src/components/markdown_editor/markdown_editor.tsx index 4318934ad70..1cf1411a92d 100644 --- a/src/components/markdown_editor/markdown_editor.tsx +++ b/src/components/markdown_editor/markdown_editor.tsx @@ -32,12 +32,9 @@ import React, { import unified, { PluggableList, Processor } from 'unified'; import { VFileMessage } from 'vfile-message'; import classNames from 'classnames'; -// @ts-ignore TODO import emoji from 'remark-emoji'; import markdown from 'remark-parse'; -// @ts-ignore TODO import remark2rehype from 'remark-rehype'; -// @ts-ignore TODO import highlight from 'remark-highlight.js'; import rehype2react from 'rehype-react'; @@ -49,9 +46,13 @@ import { EuiMarkdownFormat } from './markdown_format'; import { EuiMarkdownEditorDropZone } from './markdown_editor_drop_zone'; import { htmlIdGenerator } from '../../services/accessibility'; import { EuiLink } from '../link'; -import { EuiCodeBlock } from '../code'; +import { EuiCodeBlock, EuiCodeBlockProps } from '../code'; import { MARKDOWN_MODE, MODE_EDITING, MODE_VIEWING } from './markdown_modes'; -import { EuiMarkdownEditorUiPlugin } from './markdown_types'; +import { + EuiMarkdownAstNode, + EuiMarkdownEditorUiPlugin, + EuiMarkdownParseError, +} from './markdown_types'; import { EuiOverlayMask } from '../overlay_mask'; import { EuiModal } from '../modal'; import { ContextShape, EuiMarkdownContext } from './markdown_context'; @@ -83,7 +84,7 @@ export const defaultProcessingPlugins: PluggableList = [ createElement: createElement, components: { a: EuiLink, - code: (props: any) => + code: (props: EuiCodeBlockProps) => // if has classNames is a codeBlock using highlight js props.className ? ( @@ -121,15 +122,15 @@ type CommonMarkdownEditorProps = HTMLAttributes & /** array of toolbar plugins **/ uiPlugins?: EuiMarkdownEditorUiPlugin[]; - /** Errors to buble up */ - errors?: any; + /** Errors to bubble up */ + errors?: EuiMarkdownParseError[]; /** callback triggered when parsing results are available **/ onParse?: ( - error: any | null, + error: EuiMarkdownParseError | null, data: { messages: VFileMessage[]; - ast: any; + ast: EuiMarkdownAstNode; } ) => void; }; @@ -193,7 +194,7 @@ export const EuiMarkdownEditor: FunctionComponent< }, [parsingPluginList]); const [parsed, parseError] = useMemo< - [any | null, VFileMessage | null] + [any | null, EuiMarkdownParseError | null] >(() => { try { const parsed = parser.processSync(value); @@ -242,7 +243,7 @@ export const EuiMarkdownEditor: FunctionComponent< const getCursorNode = () => { const { selectionStart } = textareaRef.current!; - let node: any = parsed.contents; + let node: EuiMarkdownAstNode = parsed.contents; outer: while (true) { if (node.children) { @@ -335,7 +336,6 @@ export const EuiMarkdownEditor: FunctionComponent< {createElement(pluginEditorPlugin.editor!, { node: selectedNode && - // @ts-ignore TODO selectedNode.type === pluginEditorPlugin.name ? selectedNode : null, @@ -343,13 +343,10 @@ export const EuiMarkdownEditor: FunctionComponent< onSave: markdown => { if ( selectedNode && - // @ts-ignore TODO selectedNode.type === pluginEditorPlugin.name ) { textareaRef.current!.setSelectionRange( - // @ts-ignore TODO selectedNode.position.start.offset, - // @ts-ignore TODO selectedNode.position.end.offset ); } diff --git a/src/components/markdown_editor/markdown_editor_drop_zone.tsx b/src/components/markdown_editor/markdown_editor_drop_zone.tsx index 2cea6739627..86dda34e5c9 100644 --- a/src/components/markdown_editor/markdown_editor_drop_zone.tsx +++ b/src/components/markdown_editor/markdown_editor_drop_zone.tsx @@ -21,11 +21,14 @@ import React, { FunctionComponent } from 'react'; import classNames from 'classnames'; import { useDropzone } from 'react-dropzone'; import { EuiMarkdownEditorFooter } from './markdown_editor_footer'; -import { EuiMarkdownEditorUiPlugin } from './markdown_types'; +import { + EuiMarkdownEditorUiPlugin, + EuiMarkdownParseError, +} from './markdown_types'; interface EuiMarkdownEditorDropZoneProps { uiPlugins: EuiMarkdownEditorUiPlugin[]; - errors: any; + errors: EuiMarkdownParseError[]; } export const EuiMarkdownEditorDropZone: FunctionComponent< diff --git a/src/components/markdown_editor/markdown_editor_footer.tsx b/src/components/markdown_editor/markdown_editor_footer.tsx index fabd5ac45dc..7c78085c21a 100644 --- a/src/components/markdown_editor/markdown_editor_footer.tsx +++ b/src/components/markdown_editor/markdown_editor_footer.tsx @@ -29,7 +29,10 @@ import { EuiOverlayMask } from '../overlay_mask'; import { EuiTitle } from '../title'; import { EuiModal, EuiModalBody, EuiModalHeader } from '../modal'; import { EuiI18n } from '../i18n'; -import { EuiMarkdownEditorUiPlugin } from './markdown_types'; +import { + EuiMarkdownEditorUiPlugin, + EuiMarkdownParseError, +} from './markdown_types'; import { EuiPopover, EuiPopoverTitle } from '../popover'; import { EuiText } from '../text'; import { EuiSpacer } from '../spacer'; @@ -41,7 +44,7 @@ interface EuiMarkdownEditorFooterProps { uiPlugins: EuiMarkdownEditorUiPlugin[]; isUploadingFiles: boolean; openFiles: () => void; - errors: any; + errors: EuiMarkdownParseError[]; } export const EuiMarkdownEditorFooter: FunctionComponent< @@ -97,7 +100,7 @@ export const EuiMarkdownEditorFooter: FunctionComponent< default="Errors" /> - {errors.map((message: any, idx: any) => ( + {errors.map((message, idx) => ( {message.toString()} ))} diff --git a/src/components/markdown_editor/markdown_editor_toolbar.tsx b/src/components/markdown_editor/markdown_editor_toolbar.tsx index 87f090deead..6c4d91d1e70 100644 --- a/src/components/markdown_editor/markdown_editor_toolbar.tsx +++ b/src/components/markdown_editor/markdown_editor_toolbar.tsx @@ -17,7 +17,12 @@ * under the License. */ -import React, { FunctionComponent, HTMLAttributes, useContext } from 'react'; +import React, { + FunctionComponent, + HTMLAttributes, + MouseEventHandler, + useContext, +} from 'react'; import { CommonProps } from '../common'; import { EuiButtonEmpty, EuiButtonIcon } from '../button'; import { EuiFlexItem, EuiFlexGroup } from '../flex'; @@ -27,7 +32,7 @@ import { MARKDOWN_MODE, MODE_VIEWING } from './markdown_modes'; import { EuiMarkdownEditorUiPlugin } from './markdown_types'; import { EuiMarkdownContext } from './markdown_context'; import MarkdownActions from './markdown_actions'; -// @ts-ignore TODO +// @ts-ignore a react svg import MarkdownCheckmarkIcon from './icons/markdown_checkmark'; export type EuiMarkdownEditorToolbarProps = HTMLAttributes & @@ -35,7 +40,7 @@ export type EuiMarkdownEditorToolbarProps = HTMLAttributes & selectedNode?: null | any; markdownActions: MarkdownActions; viewMode: MARKDOWN_MODE; - onClickPreview: any; + onClickPreview: MouseEventHandler; uiPlugins: EuiMarkdownEditorUiPlugin[]; }; diff --git a/src/components/markdown_editor/markdown_types.ts b/src/components/markdown_editor/markdown_types.ts index 8ad9762fb7d..93f6d9f34f0 100644 --- a/src/components/markdown_editor/markdown_types.ts +++ b/src/components/markdown_editor/markdown_types.ts @@ -19,10 +19,14 @@ import { ComponentType, ReactNode } from 'react'; import { VFile } from 'vfile'; +// eslint-disable-next-line import/no-unresolved +import { Node as UnistNode, Position as UnistPosition } from 'unist'; +import { Parser } from 'remark-parse'; +import { VFileMessage } from 'vfile-message'; import { IconType } from '../icon'; export interface RemarkParser { - Parser: any; + Parser: typeof Parser; tokenizeInline: Function; file: VFile; } @@ -38,12 +42,17 @@ export interface RemarkTokenizer { notInLink?: boolean; } -export interface RemarkRehypeHandler { - (h: any, node: any): any; +interface RehypeNode {} +interface RemarkRehypeHandlerCallback { + ( + node: UnistPosition, + tagName: string, + props: Object, + children: RehypeNode[] + ): RehypeNode; } -export interface AstNodePosition { - start: { line: number; column: number; offset: number }; - end: { line: number; column: number; offset: number }; +export interface RemarkRehypeHandler { + (h: RemarkRehypeHandlerCallback, node: UnistNode): RehypeNode; } export interface EuiMarkdownEditorUiPluginEditorProps { @@ -90,3 +99,15 @@ export interface EuiMarkdownFormatting { orderedList?: boolean; trimFirst?: boolean; } + +export interface EuiMarkdownAstNode { + type: string; + children?: EuiMarkdownAstNode[]; + position: EuiMarkdownAstNodePosition; +} +export interface EuiMarkdownAstNodePosition { + start: { line: number; column: number; offset: number }; + end: { line: number; column: number; offset: number }; +} + +export type EuiMarkdownParseError = string | VFileMessage | Error; diff --git a/src/components/markdown_editor/plugins/markdown_checkbox.tsx b/src/components/markdown_editor/plugins/markdown_checkbox.tsx index df592d4eb75..339fb702838 100644 --- a/src/components/markdown_editor/plugins/markdown_checkbox.tsx +++ b/src/components/markdown_editor/plugins/markdown_checkbox.tsx @@ -18,17 +18,16 @@ */ import React, { FunctionComponent, useContext } from 'react'; -// @ts-ignore TODO import all from 'mdast-util-to-hast/lib/all'; import { EuiCheckbox } from '../../form/checkbox'; import { EuiMarkdownContext } from '../markdown_context'; import { htmlIdGenerator } from '../../../services/accessibility'; import { - AstNodePosition, - RemarkParser, + EuiMarkdownAstNodePosition, RemarkRehypeHandler, RemarkTokenizer, } from '../markdown_types'; +import { Plugin } from 'unified'; interface CheckboxNodeDetails { type: 'checkboxPlugin'; @@ -37,7 +36,7 @@ interface CheckboxNodeDetails { isChecked: boolean; } -function CheckboxParser(this: RemarkParser) { +const CheckboxParser: Plugin = function CheckboxParser() { const Parser = this.Parser; const tokenizers = Parser.prototype.blockTokenizers; const methods = Parser.prototype.blockMethods; @@ -80,13 +79,13 @@ function CheckboxParser(this: RemarkParser) { tokenizers.checkbox = tokenizeCheckbox; methods.splice(methods.indexOf('list'), 0, 'checkbox'); // Run it just before default `list` plugin to inject our own idea of checkboxes. -} +}; const checkboxMarkdownHandler: RemarkRehypeHandler = (h, node) => { - return h(node.position, 'checkboxPlugin', node, all(h, node)); + return h(node.position!, 'checkboxPlugin', node, all(h, node)); }; const CheckboxMarkdownRenderer: FunctionComponent< - CheckboxNodeDetails & { position: AstNodePosition } + CheckboxNodeDetails & { position: EuiMarkdownAstNodePosition } > = ({ position, lead, label, isChecked, children }) => { const { replaceNode } = useContext(EuiMarkdownContext); return ( diff --git a/src/components/markdown_editor/plugins/markdown_tooltip.tsx b/src/components/markdown_editor/plugins/markdown_tooltip.tsx index 7eceaaa40d2..9b215ebf861 100644 --- a/src/components/markdown_editor/plugins/markdown_tooltip.tsx +++ b/src/components/markdown_editor/plugins/markdown_tooltip.tsx @@ -18,16 +18,15 @@ */ import React, { FunctionComponent } from 'react'; -// @ts-ignore TODO import all from 'mdast-util-to-hast/lib/all'; import { - AstNodePosition, - RemarkParser, + EuiMarkdownAstNodePosition, RemarkRehypeHandler, RemarkTokenizer, } from '../markdown_types'; import { EuiToolTip } from '../../tool_tip'; import { EuiCodeBlock } from '../../code'; +import { Plugin } from 'unified'; interface TooltipNodeDetails { type: 'tooltipPlugin'; @@ -52,7 +51,7 @@ const tooltipPlugin = { ), }; -function TooltipParser(this: RemarkParser) { +const TooltipParser: Plugin = function TooltipParser() { const Parser = this.Parser; const tokenizers = Parser.prototype.inlineTokenizers; const methods = Parser.prototype.inlineMethods; @@ -135,13 +134,13 @@ function TooltipParser(this: RemarkParser) { tokenizers.tooltip = tokenizeTooltip; methods.splice(methods.indexOf('text'), 0, 'tooltip'); -} +}; const tooltipMarkdownHandler: RemarkRehypeHandler = (h, node) => { - return h(node.position, 'tooltipPlugin', node, all(h, node)); + return h(node.position!, 'tooltipPlugin', node, all(h, node)); }; const tooltipMarkdownRenderer: FunctionComponent< - TooltipNodeDetails & { position: AstNodePosition } + TooltipNodeDetails & { position: EuiMarkdownAstNodePosition } > = ({ content, children }) => { return ( diff --git a/src/components/markdown_editor/unified-plugins.d.ts b/src/components/markdown_editor/unified-plugins.d.ts new file mode 100644 index 00000000000..8f109edecbf --- /dev/null +++ b/src/components/markdown_editor/unified-plugins.d.ts @@ -0,0 +1,49 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +/* eslint-disable import/no-duplicates */ + +declare module 'remark-emoji' { + import { Plugin } from 'unified'; + const RemarkEmoji: Plugin; + export = RemarkEmoji; +} + +declare module 'remark-highlight.js' { + import { Plugin } from 'unified'; + const RemarkHighlight: Plugin; + export = RemarkHighlight; +} + +declare module 'mdast-util-to-hast/lib/all' { + // eslint-disable-next-line import/no-unresolved + import { Node as UnistNode, Position as UnistPosition } from 'unist'; + + interface RehypeNode {} + interface RemarkRehypeHandlerCallback { + ( + node: UnistPosition, + tagName: string, + props: Object, + children: RehypeNode[] + ): RehypeNode; + } + + const all: (h: RemarkRehypeHandlerCallback, node: UnistNode) => RehypeNode[]; + export = all; +} diff --git a/yarn.lock b/yarn.lock index eff09ee2e1b..7dbbde717ec 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1255,6 +1255,13 @@ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.118.tgz#247bab39bfcc6d910d4927c6e06cbc70ec376f27" integrity sha512-iiJbKLZbhSa6FYRip/9ZDX6HXhayXLDGY2Fqws9cOkEQ6XeKfaxB0sC541mowZJueYyMnVUmmG+al5/4fCDrgw== +"@types/mdast@^3.0.0": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.3.tgz#2d7d671b1cd1ea3deb306ea75036c2a0407d2deb" + integrity sha512-SXPBMnFVQg1s00dlMCc/jCdvPqdE4mXaMMCeRlxLDmTAEoegHT53xKtkDnzDTOcmMHUfcjyf36/YYZ6SxRdnsw== + dependencies: + "@types/unist" "*" + "@types/minimatch@*": version "3.0.3" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" @@ -1339,7 +1346,7 @@ resolved "https://registry.yarnpkg.com/@types/tabbable/-/tabbable-3.1.0.tgz#540d4c2729872560badcc220e73c9412c1d2bffe" integrity sha512-LL0q/bTlzseaXQ8j91eZ+Z8FQUzo0nwkng00B8365qULvFyiSOWylxV8m31Gmee3QuidkDqR72a9NRfR8s4qTw== -"@types/unist@^2.0.0", "@types/unist@^2.0.2", "@types/unist@^2.0.3": +"@types/unist@*", "@types/unist@^2.0.0", "@types/unist@^2.0.2", "@types/unist@^2.0.3": version "2.0.3" resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.3.tgz#9c088679876f374eb5983f150d4787aa6fb32d7e" integrity sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ== @@ -10388,21 +10395,23 @@ mdast-util-compact@^1.0.0: dependencies: unist-util-visit "^1.1.0" -mdast-util-definitions@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/mdast-util-definitions/-/mdast-util-definitions-2.0.1.tgz#2c931d8665a96670639f17f98e32c3afcfee25f3" - integrity sha512-Co+DQ6oZlUzvUR7JCpP249PcexxygiaKk9axJh+eRzHDZJk2julbIdKB4PXHVxdBuLzvJ1Izb+YDpj2deGMOuA== +mdast-util-definitions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/mdast-util-definitions/-/mdast-util-definitions-3.0.1.tgz#06af6c49865fc63d6d7d30125569e2f7ae3d0a86" + integrity sha512-BAv2iUm/e6IK/b2/t+Fx69EL/AGcq/IG2S+HxHjDJGfLJtd6i9SZUS76aC9cig+IEucsqxKTR0ot3m933R3iuA== dependencies: unist-util-visit "^2.0.0" -mdast-util-to-hast@^8.0.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-8.2.0.tgz#adf9f824defcd382e53dd7bace4282a45602ac67" - integrity sha512-WjH/KXtqU66XyTJQ7tg7sjvTw1OQcVV0hKdFh3BgHPwZ96fSBCQ/NitEHsN70Mmnggt+5eUUC7pCnK+2qGQnCA== +mdast-util-to-hast@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-9.1.0.tgz#6ef121dd3cd3b006bf8650b1b9454da0faf79ffe" + integrity sha512-Akl2Vi9y9cSdr19/Dfu58PVwifPXuFt1IrHe7l+Crme1KvgUT+5z+cHLVcQVGCiNTZZcdqjnuv9vPkGsqWytWA== dependencies: + "@types/mdast" "^3.0.0" + "@types/unist" "^2.0.3" collapse-white-space "^1.0.0" detab "^2.0.0" - mdast-util-definitions "^2.0.0" + mdast-util-definitions "^3.0.0" mdurl "^1.0.0" trim-lines "^1.0.0" unist-builder "^2.0.0" @@ -14440,12 +14449,12 @@ remark-parse@^7.0.2: vfile-location "^2.0.0" xtend "^4.0.1" -remark-rehype@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/remark-rehype/-/remark-rehype-6.0.0.tgz#dcd340ebee412709a81b15a69f4ae474a38aa72a" - integrity sha512-dt7cHCD2NbbmXoSnnNolk+MnWzylsOIEU07pyhZSM71Xy08xX07+yuCh+4rddyrB/a1hebygeteVEJieyCeDzg== +remark-rehype@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/remark-rehype/-/remark-rehype-7.0.0.tgz#8e106e49806c69b2e9523b76d24965119e2da67b" + integrity sha512-uqQ/VbaTdxyu/da6npHAso6hA00cMqhA3a59RziQdOLN2KEIkPykAVy52IcmZEVTuauXO0VtpxkyCey4phtHzQ== dependencies: - mdast-util-to-hast "^8.0.0" + mdast-util-to-hast "^9.1.0" remark-stringify@^4.0.0: version "4.0.0"