@doist/typist 10.0.0 → 10.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/CONTRIBUTING.md +12 -10
- package/dist/components/typist-editor.d.ts +2 -2
- package/dist/components/typist-editor.d.ts.map +1 -1
- package/dist/components/typist-editor.helper.js.map +1 -1
- package/dist/components/typist-editor.js.map +1 -1
- package/dist/extensions/core/extra-editor-commands/commands/create-paragraph-end.js.map +1 -1
- package/dist/extensions/core/extra-editor-commands/commands/extend-word-range.js.map +1 -1
- package/dist/extensions/core/extra-editor-commands/commands/insert-markdown-content-at.js.map +1 -1
- package/dist/extensions/core/extra-editor-commands/commands/insert-markdown-content.js.map +1 -1
- package/dist/extensions/core/extra-editor-commands/extra-editor-commands.js.map +1 -1
- package/dist/extensions/core/view-event-handlers.js.map +1 -1
- package/dist/extensions/plain-text/paste-multiline-text.js.map +1 -1
- package/dist/extensions/plain-text/plain-text-document.js.map +1 -1
- package/dist/extensions/plain-text/plain-text-kit.js +1 -1
- package/dist/extensions/plain-text/plain-text-kit.js.map +1 -1
- package/dist/extensions/plain-text/plain-text-paragraph.js.map +1 -1
- package/dist/extensions/plain-text/smart-markdown-typing/plugins/smart-lists.js +2 -2
- package/dist/extensions/plain-text/smart-markdown-typing/plugins/smart-lists.js.map +1 -1
- package/dist/extensions/plain-text/smart-markdown-typing/plugins/smart-select-wrap.js.map +1 -1
- package/dist/extensions/plain-text/smart-markdown-typing/plugins/smart-url-pasting.js.map +1 -1
- package/dist/extensions/plain-text/smart-markdown-typing/smart-markdown-typing.js.map +1 -1
- package/dist/extensions/rich-text/bold-and-italics.js.map +1 -1
- package/dist/extensions/rich-text/curvenote-codemark.js.map +1 -1
- package/dist/extensions/rich-text/paste-emojis.js.map +1 -1
- package/dist/extensions/rich-text/paste-markdown.js.map +1 -1
- package/dist/extensions/rich-text/rich-text-bullet-list.js +1 -1
- package/dist/extensions/rich-text/rich-text-bullet-list.js.map +1 -1
- package/dist/extensions/rich-text/rich-text-code.js.map +1 -1
- package/dist/extensions/rich-text/rich-text-document.js.map +1 -1
- package/dist/extensions/rich-text/rich-text-heading.js.map +1 -1
- package/dist/extensions/rich-text/rich-text-image.js.map +1 -1
- package/dist/extensions/rich-text/rich-text-kit.js.map +1 -1
- package/dist/extensions/rich-text/rich-text-link.js.map +1 -1
- package/dist/extensions/rich-text/rich-text-ordered-list.js +1 -1
- package/dist/extensions/rich-text/rich-text-ordered-list.js.map +1 -1
- package/dist/extensions/rich-text/rich-text-strikethrough.js.map +1 -1
- package/dist/extensions/shared/copy-markdown-source.js.map +1 -1
- package/dist/extensions/shared/paste-html-table-as-string.js +2 -2
- package/dist/extensions/shared/paste-html-table-as-string.js.map +1 -1
- package/dist/extensions/shared/paste-singleline-text.js.map +1 -1
- package/dist/factories/create-suggestion-extension.d.ts.map +1 -1
- package/dist/factories/create-suggestion-extension.js +4 -4
- package/dist/factories/create-suggestion-extension.js.map +1 -1
- package/dist/helpers/dom.js.map +1 -1
- package/dist/helpers/schema.js.map +1 -1
- package/dist/helpers/serializer.js +10 -7
- package/dist/helpers/serializer.js.map +1 -1
- package/dist/helpers/unified.js.map +1 -1
- package/dist/hooks/use-editor.js.map +1 -1
- package/dist/serializers/html/html.js +1 -1
- package/dist/serializers/html/html.js.map +1 -1
- package/dist/serializers/html/plugins/rehype-code-block.js.map +1 -1
- package/dist/serializers/html/plugins/rehype-image.js.map +1 -1
- package/dist/serializers/html/plugins/rehype-suggestions.js +11 -9
- package/dist/serializers/html/plugins/rehype-suggestions.js.map +1 -1
- package/dist/serializers/html/plugins/rehype-task-list.js.map +1 -1
- package/dist/serializers/html/plugins/remark-autolink-literal.js.map +1 -1
- package/dist/serializers/html/plugins/remark-disable-constructs.js.map +1 -1
- package/dist/serializers/html/plugins/remark-strikethrough.js.map +1 -1
- package/dist/serializers/markdown/markdown.d.ts.map +1 -1
- package/dist/serializers/markdown/markdown.js +5 -0
- package/dist/serializers/markdown/markdown.js.map +1 -1
- package/dist/serializers/markdown/plugins/image.js +1 -1
- package/dist/serializers/markdown/plugins/image.js.map +1 -1
- package/dist/serializers/markdown/plugins/list-item.js +1 -0
- package/dist/serializers/markdown/plugins/list-item.js.map +1 -1
- package/dist/serializers/markdown/plugins/paragraph.js.map +1 -1
- package/dist/serializers/markdown/plugins/strikethrough.js.map +1 -1
- package/dist/serializers/markdown/plugins/suggestion.js.map +1 -1
- package/dist/serializers/markdown/plugins/task-item.js +1 -0
- package/dist/serializers/markdown/plugins/task-item.js.map +1 -1
- package/dist/utilities/can-insert-node-at.d.ts +1 -1
- package/dist/utilities/can-insert-node-at.js +1 -1
- package/dist/utilities/can-insert-node-at.js.map +1 -1
- package/dist/utilities/can-insert-suggestion.d.ts +1 -1
- package/dist/utilities/can-insert-suggestion.js +1 -1
- package/dist/utilities/can-insert-suggestion.js.map +1 -1
- package/package.json +25 -38
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"html.js","names":[],"sources":["../../../src/serializers/html/html.ts"],"sourcesContent":["import { escape, kebabCase } from 'lodash-es'\nimport rehypeMinifyWhitespace from 'rehype-minify-whitespace'\nimport rehypeStringify from 'rehype-stringify'\nimport remarkBreaks from 'remark-breaks'\nimport remarkParse from 'remark-parse'\nimport remarkRehype from 'remark-rehype'\nimport { unified } from 'unified'\n\nimport { computeSchemaId, isPlainTextDocument } from '../../helpers/schema'\n\nimport { rehypeCodeBlock } from './plugins/rehype-code-block'\nimport { rehypeImage } from './plugins/rehype-image'\nimport { rehypeSuggestions } from './plugins/rehype-suggestions'\nimport { rehypeTaskList } from './plugins/rehype-task-list'\nimport { remarkAutolinkLiteral } from './plugins/remark-autolink-literal'\nimport { remarkDisableConstructs } from './plugins/remark-disable-constructs'\nimport { remarkStrikethrough } from './plugins/remark-strikethrough'\n\nimport type { Schema } from '@tiptap/pm/model'\n\n/**\n * The return type for the `createHTMLSerializer` function.\n */\ntype HTMLSerializerReturnType = {\n /**\n * Serializes an input Markdown string to an output HTML string.\n *\n * @param markdown The Markdown string to serialize.\n *\n * @returns The serialized HTML.\n */\n serialize: (markdown: string) => string\n}\n\n/**\n * The type for the object that holds multiple HTML serializer instances.\n */\ntype HTMLSerializerInstanceById = {\n [id: string]: HTMLSerializerReturnType\n}\n\n/**\n * Create a custom Markdown to HTML serializer for plain-text editors only.\n *\n * @param schema The editor schema to be used for nodes and marks detection.\n *\n * @returns A normalized object for the HTML serializer.\n */\nfunction createHTMLSerializerForPlainTextEditor(schema: Schema) {\n return {\n serialize(markdown: string) {\n // Converts special characters (i.e. `&`, `<`, `>`, `\"`, and `'`) to their corresponding\n // HTML entities because we need to output the full content as valid HTML (i.e. the\n // editor should not drop invalid HTML).\n let htmlResult = escape(markdown)\n\n // Serialize all suggestion links if any suggestion node exists in the schema\n Object.values(schema.nodes)\n .filter((node) => node.name.endsWith('Suggestion'))\n .forEach((suggestionNode) => {\n const linkSchema = kebabCase(suggestionNode.name.replace(/Suggestion$/, ''))\n\n htmlResult = htmlResult.replace(\n new RegExp(`\\\\[([^\\\\[]+)\\\\]\\\\((?:${linkSchema}):\\\\/\\\\/(
|
|
1
|
+
{"version":3,"file":"html.js","names":[],"sources":["../../../src/serializers/html/html.ts"],"sourcesContent":["import { escape, kebabCase } from 'lodash-es'\nimport rehypeMinifyWhitespace from 'rehype-minify-whitespace'\nimport rehypeStringify from 'rehype-stringify'\nimport remarkBreaks from 'remark-breaks'\nimport remarkParse from 'remark-parse'\nimport remarkRehype from 'remark-rehype'\nimport { unified } from 'unified'\n\nimport { computeSchemaId, isPlainTextDocument } from '../../helpers/schema'\n\nimport { rehypeCodeBlock } from './plugins/rehype-code-block'\nimport { rehypeImage } from './plugins/rehype-image'\nimport { rehypeSuggestions } from './plugins/rehype-suggestions'\nimport { rehypeTaskList } from './plugins/rehype-task-list'\nimport { remarkAutolinkLiteral } from './plugins/remark-autolink-literal'\nimport { remarkDisableConstructs } from './plugins/remark-disable-constructs'\nimport { remarkStrikethrough } from './plugins/remark-strikethrough'\n\nimport type { Schema } from '@tiptap/pm/model'\n\n/**\n * The return type for the `createHTMLSerializer` function.\n */\ntype HTMLSerializerReturnType = {\n /**\n * Serializes an input Markdown string to an output HTML string.\n *\n * @param markdown The Markdown string to serialize.\n *\n * @returns The serialized HTML.\n */\n serialize: (markdown: string) => string\n}\n\n/**\n * The type for the object that holds multiple HTML serializer instances.\n */\ntype HTMLSerializerInstanceById = {\n [id: string]: HTMLSerializerReturnType\n}\n\n/**\n * Create a custom Markdown to HTML serializer for plain-text editors only.\n *\n * @param schema The editor schema to be used for nodes and marks detection.\n *\n * @returns A normalized object for the HTML serializer.\n */\nfunction createHTMLSerializerForPlainTextEditor(schema: Schema) {\n return {\n serialize(markdown: string) {\n // Converts special characters (i.e. `&`, `<`, `>`, `\"`, and `'`) to their corresponding\n // HTML entities because we need to output the full content as valid HTML (i.e. the\n // editor should not drop invalid HTML).\n let htmlResult = escape(markdown)\n\n // Serialize all suggestion links if any suggestion node exists in the schema\n Object.values(schema.nodes)\n .filter((node) => node.name.endsWith('Suggestion'))\n .forEach((suggestionNode) => {\n const linkSchema = kebabCase(suggestionNode.name.replace(/Suggestion$/, ''))\n\n htmlResult = htmlResult.replace(\n new RegExp(`\\\\[([^\\\\[]+)\\\\]\\\\((?:${linkSchema}):\\\\/\\\\/([^\\\\s)]+)\\\\)`, 'gm'),\n `<span data-${linkSchema} data-id=\"$2\" data-label=\"$1\"></span>`,\n )\n })\n\n // Return the serialized HTML with every line wrapped in a paragraph element\n return htmlResult.replace(/^([^\\n]+)\\n?|\\n+/gm, `<p>$1</p>`)\n },\n }\n}\n\n/**\n * Create a Markdown to HTML serializer with the unified ecosystem for a rich-text editor, or use a\n * custom serializer for a plain-text editor. The editor schema is used to detect which nodes and\n * marks are available in the editor, and only parses the input with the minimal required plugins.\n *\n * @param schema The editor schema to be used for nodes and marks detection.\n *\n * @returns A normalized object for the HTML serializer.\n */\nfunction createHTMLSerializer(schema: Schema): HTMLSerializerReturnType {\n // Returns a custom HTML serializer for plain-text editors\n if (isPlainTextDocument(schema)) {\n return createHTMLSerializerForPlainTextEditor(schema)\n }\n\n // Initialize a unified processor with a remark plugin for parsing Markdown\n const unifiedProcessor = unified().use(remarkParse)\n\n // Configure the unified processor to use a custom plugin to disable constructs based on the\n // supported extensions that are enabled in the editor schema\n unifiedProcessor.use(remarkDisableConstructs, schema)\n\n // Configure the unified processor to use a third-party plugin to turn soft line endings into\n // hard breaks (i.e. `<br>`), which will display user content closer to how it was authored\n // (although not CommonMark compliant, this resembles the behaviour we always supported)\n if (schema.nodes.hardBreak) {\n unifiedProcessor.use(remarkBreaks)\n }\n\n // Configure the unified processor to use a custom plugin to add support for the strikethrough\n // extension from the GitHub Flavored Markdown (GFM) specification\n if (schema.marks.strike) {\n unifiedProcessor.use(remarkStrikethrough, { singleTilde: false })\n }\n\n // Configure the unified processor to use a custom plugin to add support for the autolink\n // literals extension from the GitHub Flavored Markdown (GFM) specification\n if (schema.marks.link) {\n unifiedProcessor.use(remarkAutolinkLiteral)\n }\n\n // Configure the unified processor with an official plugin to convert Markdown into HTML to\n // support rehype (a tool that transforms HTML with plugins), followed by another official\n // plugin to minify whitespace between tags (prevents line feeds from appearing as blank)\n unifiedProcessor\n .use(remarkRehype, {\n // Persist raw HTML (disables support for custom elements/tags)\n // ref: https://github.com/Doist/Issues/issues/5689\n allowDangerousHtml: true,\n })\n // This must come before all rehype plugins that transform the HTML output\n .use(rehypeMinifyWhitespace, {\n // Preserve line breaks when collapsing whitespace (e.g., line feeds)\n newlines: true,\n })\n\n // Configure the unified processor with a custom plugin to remove the trailing newline from code\n // blocks (i.e. the newline between the last code line and `</code></pre>`)\n if (schema.nodes.codeBlock) {\n unifiedProcessor.use(rehypeCodeBlock)\n }\n\n // Configure the unified processor with a custom plugin to remove the wrapping paragraph from\n // images and to remove all inline images based on inline images support in the editor schema\n if (schema.nodes.paragraph && schema.nodes.image) {\n unifiedProcessor.use(rehypeImage, schema)\n }\n\n // Configure the unified processor with a custom plugin to add support Tiptap task lists\n if (schema.nodes.taskList && schema.nodes.taskItem) {\n unifiedProcessor.use(rehypeTaskList)\n }\n\n // Configure the unified processor with a custom plugin to add support for suggestions nodes\n unifiedProcessor.use(rehypeSuggestions, schema)\n\n // Configure the unified processor with an official plugin that defines how to take a syntax\n // tree as input and turn it into serialized HTML\n unifiedProcessor.use(rehypeStringify, {\n characterReferences: {\n // Compatibility with the previous implementation in Marked\n useNamedReferences: true,\n },\n })\n\n return {\n serialize(markdown: string) {\n return unifiedProcessor.processSync(markdown).toString()\n },\n }\n}\n\n/**\n * Object that holds multiple HTML serializer instances based on a given ID.\n */\nconst htmlSerializerInstanceById: HTMLSerializerInstanceById = {}\n\n/**\n * Returns a singleton instance of a HTML serializer based on the provided editor schema.\n *\n * @param schema The editor schema connected to the HTML serializer instance.\n *\n * @returns The HTML serializer instance for the given editor schema.\n */\nfunction getHTMLSerializerInstance(schema: Schema) {\n const id = computeSchemaId(schema)\n\n if (!htmlSerializerInstanceById[id]) {\n htmlSerializerInstanceById[id] = createHTMLSerializer(schema)\n }\n\n return htmlSerializerInstanceById[id]\n}\n\nexport { createHTMLSerializer, getHTMLSerializerInstance }\n\nexport type { HTMLSerializerReturnType }\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAgDA,SAAS,uCAAuC,QAAgB;CAC5D,OAAO,EACH,UAAU,UAAkB;EAIxB,IAAI,aAAa,OAAO,SAAS;EAGjC,OAAO,OAAO,OAAO,MAAM,CACtB,QAAQ,SAAS,KAAK,KAAK,SAAS,aAAa,CAAC,CAClD,SAAS,mBAAmB;GACzB,MAAM,aAAa,UAAU,eAAe,KAAK,QAAQ,eAAe,GAAG,CAAC;GAE5E,aAAa,WAAW,QACpB,IAAI,OAAO,wBAAwB,WAAW,wBAAwB,KAAK,EAC3E,cAAc,WAAW,uCAC5B;IACH;EAGN,OAAO,WAAW,QAAQ,sBAAsB,YAAY;IAEnE;;;;;;;;;;;AAYL,SAAS,qBAAqB,QAA0C;CAEpE,IAAI,oBAAoB,OAAO,EAC3B,OAAO,uCAAuC,OAAO;CAIzD,MAAM,mBAAmB,SAAS,CAAC,IAAI,YAAY;CAInD,iBAAiB,IAAI,yBAAyB,OAAO;CAKrD,IAAI,OAAO,MAAM,WACb,iBAAiB,IAAI,aAAa;CAKtC,IAAI,OAAO,MAAM,QACb,iBAAiB,IAAI,qBAAqB,EAAE,aAAa,OAAO,CAAC;CAKrE,IAAI,OAAO,MAAM,MACb,iBAAiB,IAAI,sBAAsB;CAM/C,iBACK,IAAI,cAAc,EAGf,oBAAoB,MACvB,CAAC,CAED,IAAI,wBAAwB,EAEzB,UAAU,MACb,CAAC;CAIN,IAAI,OAAO,MAAM,WACb,iBAAiB,IAAI,gBAAgB;CAKzC,IAAI,OAAO,MAAM,aAAa,OAAO,MAAM,OACvC,iBAAiB,IAAI,aAAa,OAAO;CAI7C,IAAI,OAAO,MAAM,YAAY,OAAO,MAAM,UACtC,iBAAiB,IAAI,eAAe;CAIxC,iBAAiB,IAAI,mBAAmB,OAAO;CAI/C,iBAAiB,IAAI,iBAAiB,EAClC,qBAAqB,EAEjB,oBAAoB,MACvB,EACJ,CAAC;CAEF,OAAO,EACH,UAAU,UAAkB;EACxB,OAAO,iBAAiB,YAAY,SAAS,CAAC,UAAU;IAE/D;;;;;AAML,MAAM,6BAAyD,EAAE;;;;;;;;AASjE,SAAS,0BAA0B,QAAgB;CAC/C,MAAM,KAAK,gBAAgB,OAAO;CAElC,IAAI,CAAC,2BAA2B,KAC5B,2BAA2B,MAAM,qBAAqB,OAAO;CAGjE,OAAO,2BAA2B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rehype-code-block.js","names":[],"sources":["../../../../src/serializers/html/plugins/rehype-code-block.ts"],"sourcesContent":["import { visit } from 'unist-util-visit'\n\nimport { isHastElementNode, isHastTextNode } from '../../../helpers/unified'\n\nimport type { Node as HastNode } from 'hast'\nimport type { Transformer } from 'unified'\n\n/**\n * A rehype plugin to remove the trailing newline from code blocks (i.e. the newline between the\n * last code line and `</code></pre>`). Although that newline is part of the CommonMark\n * specification, this custom plugin is required to prevent Tiptap from rendering a blank line at\n * the end of the code block.\n */\nfunction rehypeCodeBlock(): Transformer {\n return (...[tree]: Parameters<Transformer>): ReturnType<Transformer> => {\n visit(tree, 'element', (node: HastNode) => {\n if (\n isHastElementNode(node, 'pre') &&\n isHastElementNode(node.children[0], 'code') &&\n isHastTextNode(node.children[0].children[0])\n ) {\n node.children[0].children[0].value = node.children[0].children[0].value.replace(\n /\\n$/,\n '',\n )\n }\n })\n\n return tree\n }\n}\n\nexport { rehypeCodeBlock }\n"],"mappings":";;;;;;;;;AAaA,SAAS,kBAA+B;
|
|
1
|
+
{"version":3,"file":"rehype-code-block.js","names":[],"sources":["../../../../src/serializers/html/plugins/rehype-code-block.ts"],"sourcesContent":["import { visit } from 'unist-util-visit'\n\nimport { isHastElementNode, isHastTextNode } from '../../../helpers/unified'\n\nimport type { Node as HastNode } from 'hast'\nimport type { Transformer } from 'unified'\n\n/**\n * A rehype plugin to remove the trailing newline from code blocks (i.e. the newline between the\n * last code line and `</code></pre>`). Although that newline is part of the CommonMark\n * specification, this custom plugin is required to prevent Tiptap from rendering a blank line at\n * the end of the code block.\n */\nfunction rehypeCodeBlock(): Transformer {\n return (...[tree]: Parameters<Transformer>): ReturnType<Transformer> => {\n visit(tree, 'element', (node: HastNode) => {\n if (\n isHastElementNode(node, 'pre') &&\n isHastElementNode(node.children[0], 'code') &&\n isHastTextNode(node.children[0].children[0])\n ) {\n node.children[0].children[0].value = node.children[0].children[0].value.replace(\n /\\n$/,\n '',\n )\n }\n })\n\n return tree\n }\n}\n\nexport { rehypeCodeBlock }\n"],"mappings":";;;;;;;;;AAaA,SAAS,kBAA+B;CACpC,QAAQ,GAAG,CAAC,UAA4D;EACpE,MAAM,MAAM,YAAY,SAAmB;GACvC,IACI,kBAAkB,MAAM,MAAM,IAC9B,kBAAkB,KAAK,SAAS,IAAI,OAAO,IAC3C,eAAe,KAAK,SAAS,GAAG,SAAS,GAAG,EAE5C,KAAK,SAAS,GAAG,SAAS,GAAG,QAAQ,KAAK,SAAS,GAAG,SAAS,GAAG,MAAM,QACpE,OACA,GACH;IAEP;EAEF,OAAO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rehype-image.js","names":[],"sources":["../../../../src/serializers/html/plugins/rehype-image.ts"],"sourcesContent":["import { remove } from 'unist-util-remove'\nimport { visit } from 'unist-util-visit'\n\nimport { isHastElementNode } from '../../../helpers/unified'\n\nimport type { Schema } from '@tiptap/pm/model'\nimport type { Node as HastNode, Parent as HastParent } from 'hast'\nimport type { Transformer } from 'unified'\n\n/**\n * A rehype plugin to remove the wrapping paragraph from images and to remove all inline images if\n * the editor was configured without inline image support (Tiptap default).\n *\n * @param schema The editor schema to be used for nodes and marks detection.\n */\nfunction rehypeImage(schema: Schema): Transformer {\n const allowInlineImages = schema.nodes.image ? schema.nodes.image.spec.inline : false\n\n // Return the tree as-is if the editor does not support inline images\n if (allowInlineImages) {\n return (tree) => tree\n }\n\n return (...[tree]: Parameters<Transformer>): ReturnType<Transformer> => {\n visit(tree, 'element', (node: HastNode, index: number, parent: HastParent) => {\n if (isHastElementNode(node, 'p')) {\n const areAllChildrenImages = node.children.every((c) => isHastElementNode(c, 'img'))\n\n // Replace the paragraph with the image children if all children are images, or\n // remove all images from the paragraph if it contains non-image children since the\n // editor does not support inline images\n if (areAllChildrenImages) {\n parent.children.splice(index, 1, ...node.children)\n } else {\n remove(node, (n) => isHastElementNode(n, 'img'))\n }\n }\n })\n\n return tree\n }\n}\n\nexport { rehypeImage }\n"],"mappings":";;;;;;;;;;AAeA,SAAS,YAAY,QAA6B;
|
|
1
|
+
{"version":3,"file":"rehype-image.js","names":[],"sources":["../../../../src/serializers/html/plugins/rehype-image.ts"],"sourcesContent":["import { remove } from 'unist-util-remove'\nimport { visit } from 'unist-util-visit'\n\nimport { isHastElementNode } from '../../../helpers/unified'\n\nimport type { Schema } from '@tiptap/pm/model'\nimport type { Node as HastNode, Parent as HastParent } from 'hast'\nimport type { Transformer } from 'unified'\n\n/**\n * A rehype plugin to remove the wrapping paragraph from images and to remove all inline images if\n * the editor was configured without inline image support (Tiptap default).\n *\n * @param schema The editor schema to be used for nodes and marks detection.\n */\nfunction rehypeImage(schema: Schema): Transformer {\n const allowInlineImages = schema.nodes.image ? schema.nodes.image.spec.inline : false\n\n // Return the tree as-is if the editor does not support inline images\n if (allowInlineImages) {\n return (tree) => tree\n }\n\n return (...[tree]: Parameters<Transformer>): ReturnType<Transformer> => {\n visit(tree, 'element', (node: HastNode, index: number, parent: HastParent) => {\n if (isHastElementNode(node, 'p')) {\n const areAllChildrenImages = node.children.every((c) => isHastElementNode(c, 'img'))\n\n // Replace the paragraph with the image children if all children are images, or\n // remove all images from the paragraph if it contains non-image children since the\n // editor does not support inline images\n if (areAllChildrenImages) {\n parent.children.splice(index, 1, ...node.children)\n } else {\n remove(node, (n) => isHastElementNode(n, 'img'))\n }\n }\n })\n\n return tree\n }\n}\n\nexport { rehypeImage }\n"],"mappings":";;;;;;;;;;AAeA,SAAS,YAAY,QAA6B;CAI9C,IAH0B,OAAO,MAAM,QAAQ,OAAO,MAAM,MAAM,KAAK,SAAS,OAI5E,QAAQ,SAAS;CAGrB,QAAQ,GAAG,CAAC,UAA4D;EACpE,MAAM,MAAM,YAAY,MAAgB,OAAe,WAAuB;GAC1E,IAAI,kBAAkB,MAAM,IAAI,EAM5B,IAL6B,KAAK,SAAS,OAAO,MAAM,kBAAkB,GAAG,MAAM,CAK3D,EACpB,OAAO,SAAS,OAAO,OAAO,GAAG,GAAG,KAAK,SAAS;QAElD,OAAO,OAAO,MAAM,kBAAkB,GAAG,MAAM,CAAC;IAG1D;EAEF,OAAO"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isHastElementNode, isHastTextNode } from "../../../helpers/unified.js";
|
|
2
|
-
import {
|
|
2
|
+
import { buildSuggestionSchemaInfo } from "../../../helpers/serializer.js";
|
|
3
3
|
import { visit } from "unist-util-visit";
|
|
4
4
|
//#region src/serializers/html/plugins/rehype-suggestions.ts
|
|
5
5
|
/**
|
|
@@ -8,21 +8,23 @@ import { visit } from "unist-util-visit";
|
|
|
8
8
|
* @param schema The editor schema to be used for suggestion nodes detection.
|
|
9
9
|
*/
|
|
10
10
|
function rehypeSuggestions(schema) {
|
|
11
|
-
const
|
|
12
|
-
if (!
|
|
11
|
+
const suggestionSchemaInfo = buildSuggestionSchemaInfo(schema);
|
|
12
|
+
if (!suggestionSchemaInfo) return (tree) => tree;
|
|
13
13
|
return (...[tree]) => {
|
|
14
|
-
const suggestionSchemaRegex = new RegExp(`^${
|
|
14
|
+
const suggestionSchemaRegex = new RegExp(`^${suggestionSchemaInfo.urlSchemeRegex}`);
|
|
15
15
|
visit(tree, "element", (node) => {
|
|
16
16
|
if (isHastElementNode(node, "a") && suggestionSchemaRegex.test(String(node.properties?.href))) {
|
|
17
|
-
const [,
|
|
18
|
-
if (
|
|
17
|
+
const [, urlScheme, id] = /^([a-z-]+):\/\/(\S+)$/i.exec(String(node.properties?.href)) || [];
|
|
18
|
+
if (urlScheme && id && isHastTextNode(node.children[0])) {
|
|
19
|
+
const label = node.children[0].value;
|
|
20
|
+
const triggerChar = suggestionSchemaInfo.triggerCharByScheme.get(urlScheme);
|
|
19
21
|
node.tagName = "span";
|
|
20
22
|
node.properties = {
|
|
21
|
-
[`data-${
|
|
23
|
+
[`data-${urlScheme}`]: "",
|
|
22
24
|
"data-id": id,
|
|
23
|
-
"data-label":
|
|
25
|
+
"data-label": label
|
|
24
26
|
};
|
|
25
|
-
node.children =
|
|
27
|
+
node.children[0].value = `${triggerChar}${label}`;
|
|
26
28
|
}
|
|
27
29
|
}
|
|
28
30
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rehype-suggestions.js","names":[],"sources":["../../../../src/serializers/html/plugins/rehype-suggestions.ts"],"sourcesContent":["import { visit } from 'unist-util-visit'\n\nimport {
|
|
1
|
+
{"version":3,"file":"rehype-suggestions.js","names":[],"sources":["../../../../src/serializers/html/plugins/rehype-suggestions.ts"],"sourcesContent":["import { visit } from 'unist-util-visit'\n\nimport { buildSuggestionSchemaInfo } from '../../../helpers/serializer'\nimport { isHastElementNode, isHastTextNode } from '../../../helpers/unified'\n\nimport type { Schema } from '@tiptap/pm/model'\nimport type { Node as HastNode } from 'hast'\nimport type { Transformer } from 'unified'\n\n/**\n * A rehype plugin to add support for suggestions nodes (e.g., `@username` or `#channel).\n *\n * @param schema The editor schema to be used for suggestion nodes detection.\n */\nfunction rehypeSuggestions(schema: Schema): Transformer {\n const suggestionSchemaInfo = buildSuggestionSchemaInfo(schema)\n\n // Return the tree as-is if the editor does not support suggestions\n if (!suggestionSchemaInfo) {\n return (tree: HastNode) => tree\n }\n\n return (...[tree]: Parameters<Transformer>): ReturnType<Transformer> => {\n const suggestionSchemaRegex = new RegExp(`^${suggestionSchemaInfo.urlSchemeRegex}`)\n\n visit(tree, 'element', (node: HastNode) => {\n if (\n isHastElementNode(node, 'a') &&\n suggestionSchemaRegex.test(String(node.properties?.href))\n ) {\n const [, urlScheme, id] =\n /^([a-z-]+):\\/\\/(\\S+)$/i.exec(String(node.properties?.href)) || []\n\n // Replace the link element with a span containing the suggestion attributes,\n // keeping the visible label (prefixed with the trigger character) as text content\n // so the span renders correctly when used outside of an editor.\n if (urlScheme && id && isHastTextNode(node.children[0])) {\n const label = node.children[0].value\n\n // The URL scheme was matched against the regex built from the same map of\n // suggestion nodes, so the trigger character is guaranteed to exist\n const triggerChar = suggestionSchemaInfo.triggerCharByScheme.get(\n urlScheme,\n ) as string\n\n node.tagName = 'span'\n node.properties = {\n [`data-${urlScheme}`]: '',\n 'data-id': id,\n 'data-label': label,\n }\n node.children[0].value = `${triggerChar}${label}`\n }\n }\n })\n\n return tree\n }\n}\n\nexport { rehypeSuggestions }\n"],"mappings":";;;;;;;;;AAcA,SAAS,kBAAkB,QAA6B;CACpD,MAAM,uBAAuB,0BAA0B,OAAO;CAG9D,IAAI,CAAC,sBACD,QAAQ,SAAmB;CAG/B,QAAQ,GAAG,CAAC,UAA4D;EACpE,MAAM,wBAAwB,IAAI,OAAO,IAAI,qBAAqB,iBAAiB;EAEnF,MAAM,MAAM,YAAY,SAAmB;GACvC,IACI,kBAAkB,MAAM,IAAI,IAC5B,sBAAsB,KAAK,OAAO,KAAK,YAAY,KAAK,CAAC,EAC3D;IACE,MAAM,GAAG,WAAW,MAChB,yBAAyB,KAAK,OAAO,KAAK,YAAY,KAAK,CAAC,IAAI,EAAE;IAKtE,IAAI,aAAa,MAAM,eAAe,KAAK,SAAS,GAAG,EAAE;KACrD,MAAM,QAAQ,KAAK,SAAS,GAAG;KAI/B,MAAM,cAAc,qBAAqB,oBAAoB,IACzD,UACH;KAED,KAAK,UAAU;KACf,KAAK,aAAa;OACb,QAAQ,cAAc;MACvB,WAAW;MACX,cAAc;MACjB;KACD,KAAK,SAAS,GAAG,QAAQ,GAAG,cAAc;;;IAGpD;EAEF,OAAO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rehype-task-list.js","names":[],"sources":["../../../../src/serializers/html/plugins/rehype-task-list.ts"],"sourcesContent":["import { visit } from 'unist-util-visit'\n\nimport { isHastElementNode, isHastTextNode } from '../../../helpers/unified'\n\nimport type { Node as HastNode } from 'hast'\nimport type { Transformer } from 'unified'\n\n/**\n * A rehype plugin to add support for Tiptap task lists (i.e., `* [ ] Task`).\n */\nfunction rehypeTaskList(): Transformer {\n return (...[tree]: Parameters<Transformer>): ReturnType<Transformer> => {\n visit(tree, 'element', (node: HastNode) => {\n if (isHastElementNode(node, 'ul')) {\n const areAllChildrenTaskItems = node.children.every(\n (c) =>\n isHastElementNode(c, 'li') &&\n isHastTextNode(c.children[0]) &&\n /^\\[[ x]\\] /i.test(c.children[0].value),\n )\n\n // Add the required attributes to the list and list items if all children are tasks,\n // removing the `[ ] ` or `[x] ` at the beginning of the task item text\n if (areAllChildrenTaskItems) {\n node.properties = {\n ...node.properties,\n 'data-type': 'taskList',\n }\n\n node.children.forEach((c) => {\n if (isHastElementNode(c, 'li') && isHastTextNode(c.children[0])) {\n c.properties = {\n ...c.properties,\n 'data-type': 'taskItem',\n 'data-checked': String(/^\\[x\\]/i.test(c.children[0].value)),\n }\n\n c.children[0].value = c.children[0].value.substring(4).trim()\n }\n })\n }\n }\n })\n\n return tree\n }\n}\n\nexport { rehypeTaskList }\n"],"mappings":";;;;;;AAUA,SAAS,iBAA8B;
|
|
1
|
+
{"version":3,"file":"rehype-task-list.js","names":[],"sources":["../../../../src/serializers/html/plugins/rehype-task-list.ts"],"sourcesContent":["import { visit } from 'unist-util-visit'\n\nimport { isHastElementNode, isHastTextNode } from '../../../helpers/unified'\n\nimport type { Node as HastNode } from 'hast'\nimport type { Transformer } from 'unified'\n\n/**\n * A rehype plugin to add support for Tiptap task lists (i.e., `* [ ] Task`).\n */\nfunction rehypeTaskList(): Transformer {\n return (...[tree]: Parameters<Transformer>): ReturnType<Transformer> => {\n visit(tree, 'element', (node: HastNode) => {\n if (isHastElementNode(node, 'ul')) {\n const areAllChildrenTaskItems = node.children.every(\n (c) =>\n isHastElementNode(c, 'li') &&\n isHastTextNode(c.children[0]) &&\n /^\\[[ x]\\] /i.test(c.children[0].value),\n )\n\n // Add the required attributes to the list and list items if all children are tasks,\n // removing the `[ ] ` or `[x] ` at the beginning of the task item text\n if (areAllChildrenTaskItems) {\n node.properties = {\n ...node.properties,\n 'data-type': 'taskList',\n }\n\n node.children.forEach((c) => {\n if (isHastElementNode(c, 'li') && isHastTextNode(c.children[0])) {\n c.properties = {\n ...c.properties,\n 'data-type': 'taskItem',\n 'data-checked': String(/^\\[x\\]/i.test(c.children[0].value)),\n }\n\n c.children[0].value = c.children[0].value.substring(4).trim()\n }\n })\n }\n }\n })\n\n return tree\n }\n}\n\nexport { rehypeTaskList }\n"],"mappings":";;;;;;AAUA,SAAS,iBAA8B;CACnC,QAAQ,GAAG,CAAC,UAA4D;EACpE,MAAM,MAAM,YAAY,SAAmB;GACvC,IAAI,kBAAkB,MAAM,KAAK;QACG,KAAK,SAAS,OACzC,MACG,kBAAkB,GAAG,KAAK,IAC1B,eAAe,EAAE,SAAS,GAAG,IAC7B,cAAc,KAAK,EAAE,SAAS,GAAG,MAAM,CAKpB,EAAE;KACzB,KAAK,aAAa;MACd,GAAG,KAAK;MACR,aAAa;MAChB;KAED,KAAK,SAAS,SAAS,MAAM;MACzB,IAAI,kBAAkB,GAAG,KAAK,IAAI,eAAe,EAAE,SAAS,GAAG,EAAE;OAC7D,EAAE,aAAa;QACX,GAAG,EAAE;QACL,aAAa;QACb,gBAAgB,OAAO,UAAU,KAAK,EAAE,SAAS,GAAG,MAAM,CAAC;QAC9D;OAED,EAAE,SAAS,GAAG,QAAQ,EAAE,SAAS,GAAG,MAAM,UAAU,EAAE,CAAC,MAAM;;OAEnE;;;IAGZ;EAEF,OAAO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remark-autolink-literal.js","names":[],"sources":["../../../../src/serializers/html/plugins/remark-autolink-literal.ts"],"sourcesContent":["import {\n gfmAutolinkLiteralFromMarkdown,\n gfmAutolinkLiteralToMarkdown,\n} from 'mdast-util-gfm-autolink-literal'\nimport { gfmAutolinkLiteral } from 'micromark-extension-gfm-autolink-literal'\n\nimport type { Processor } from 'unified'\n\n/**\n * A remark plugin to add support for the autolink literals extension extension from the GitHub\n * Flavored Markdown (GFM) specification.\n *\n * This is an standalone plugin which makes use of both the `mdast-util-gfm-autolink-literal` and\n * `micromark-extension-gfm-autolink-literal` packages, and the implementation is inspired by the\n * third-party `remark-gfm` plugin.\n *\n * The reason why we don't use `remark-gfm` directly is because we don't want to support all other\n * GFM features (footnotes, tables, tagfilter, and tasklists).\n *\n * @param options Configuration options for the plugin.\n */\nfunction remarkAutolinkLiteral(this: Processor) {\n const data = this.data()\n\n const micromarkExtensions = data.micromarkExtensions || (data.micromarkExtensions = [])\n const fromMarkdownExtensions = data.fromMarkdownExtensions || (data.fromMarkdownExtensions = [])\n const toMarkdownExtensions = data.toMarkdownExtensions || (data.toMarkdownExtensions = [])\n\n micromarkExtensions.push(gfmAutolinkLiteral())\n fromMarkdownExtensions.push(gfmAutolinkLiteralFromMarkdown())\n toMarkdownExtensions.push(gfmAutolinkLiteralToMarkdown())\n}\n\nexport { remarkAutolinkLiteral }\n"],"mappings":";;;;;;;;;;;;;;;;AAqBA,SAAS,wBAAuC;CAC5C,MAAM,OAAO,KAAK,MAAM;CAExB,MAAM,sBAAsB,KAAK,wBAAwB,KAAK,sBAAsB,EAAE;CACtF,MAAM,yBAAyB,KAAK,2BAA2B,KAAK,yBAAyB,EAAE;CAC/F,MAAM,uBAAuB,KAAK,yBAAyB,KAAK,uBAAuB,EAAE;
|
|
1
|
+
{"version":3,"file":"remark-autolink-literal.js","names":[],"sources":["../../../../src/serializers/html/plugins/remark-autolink-literal.ts"],"sourcesContent":["import {\n gfmAutolinkLiteralFromMarkdown,\n gfmAutolinkLiteralToMarkdown,\n} from 'mdast-util-gfm-autolink-literal'\nimport { gfmAutolinkLiteral } from 'micromark-extension-gfm-autolink-literal'\n\nimport type { Processor } from 'unified'\n\n/**\n * A remark plugin to add support for the autolink literals extension extension from the GitHub\n * Flavored Markdown (GFM) specification.\n *\n * This is an standalone plugin which makes use of both the `mdast-util-gfm-autolink-literal` and\n * `micromark-extension-gfm-autolink-literal` packages, and the implementation is inspired by the\n * third-party `remark-gfm` plugin.\n *\n * The reason why we don't use `remark-gfm` directly is because we don't want to support all other\n * GFM features (footnotes, tables, tagfilter, and tasklists).\n *\n * @param options Configuration options for the plugin.\n */\nfunction remarkAutolinkLiteral(this: Processor) {\n const data = this.data()\n\n const micromarkExtensions = data.micromarkExtensions || (data.micromarkExtensions = [])\n const fromMarkdownExtensions = data.fromMarkdownExtensions || (data.fromMarkdownExtensions = [])\n const toMarkdownExtensions = data.toMarkdownExtensions || (data.toMarkdownExtensions = [])\n\n micromarkExtensions.push(gfmAutolinkLiteral())\n fromMarkdownExtensions.push(gfmAutolinkLiteralFromMarkdown())\n toMarkdownExtensions.push(gfmAutolinkLiteralToMarkdown())\n}\n\nexport { remarkAutolinkLiteral }\n"],"mappings":";;;;;;;;;;;;;;;;AAqBA,SAAS,wBAAuC;CAC5C,MAAM,OAAO,KAAK,MAAM;CAExB,MAAM,sBAAsB,KAAK,wBAAwB,KAAK,sBAAsB,EAAE;CACtF,MAAM,yBAAyB,KAAK,2BAA2B,KAAK,yBAAyB,EAAE;CAC/F,MAAM,uBAAuB,KAAK,yBAAyB,KAAK,uBAAuB,EAAE;CAEzF,oBAAoB,KAAK,oBAAoB,CAAC;CAC9C,uBAAuB,KAAK,gCAAgC,CAAC;CAC7D,qBAAqB,KAAK,8BAA8B,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remark-disable-constructs.js","names":[],"sources":["../../../../src/serializers/html/plugins/remark-disable-constructs.ts"],"sourcesContent":["import type { Schema } from '@tiptap/pm/model'\nimport type { Processor } from 'unified'\n\n/**\n * A remark plugin to disable multiple language constructs based on the availability of marks and/or\n * nodes in the given editor schema.\n *\n * @param schema The editor schema to be used for nodes and marks detection.\n */\nfunction remarkDisableConstructs(this: Processor, schema: Schema) {\n const data = this.data()\n\n const disabledConstructs: string[] = []\n\n if (!schema.nodes.blockquote) {\n disabledConstructs.push('blockQuote')\n }\n\n if (!schema.marks.bold || !schema.marks.italic) {\n disabledConstructs.push('attention')\n }\n\n if (!schema.nodes.bulletList || !schema.nodes.orderedList) {\n disabledConstructs.push('list')\n }\n\n if (!schema.marks.code) {\n disabledConstructs.push('codeText')\n }\n\n if (!schema.nodes.codeBlock) {\n disabledConstructs.push('codeFenced', 'codeIndented')\n }\n\n if (!schema.nodes.heading) {\n disabledConstructs.push('headingAtx')\n }\n\n if (!schema.nodes.horizontalRule) {\n disabledConstructs.push('thematicBreak')\n }\n\n if (!schema.nodes.image) {\n disabledConstructs.push('labelStartImage')\n }\n\n if (!schema.marks.link) {\n disabledConstructs.push('labelStartLink')\n }\n\n const micromarkExtensions = data.micromarkExtensions || (data.micromarkExtensions = [])\n\n // https://github.com/micromark/micromark#case-turn-off-constructs\n micromarkExtensions.push({\n disable: {\n null: disabledConstructs,\n },\n })\n}\n\nexport { remarkDisableConstructs }\n"],"mappings":";;;;;;;AASA,SAAS,wBAAyC,QAAgB;CAC9D,MAAM,OAAO,KAAK,MAAM;CAExB,MAAM,qBAA+B,EAAE;
|
|
1
|
+
{"version":3,"file":"remark-disable-constructs.js","names":[],"sources":["../../../../src/serializers/html/plugins/remark-disable-constructs.ts"],"sourcesContent":["import type { Schema } from '@tiptap/pm/model'\nimport type { Processor } from 'unified'\n\n/**\n * A remark plugin to disable multiple language constructs based on the availability of marks and/or\n * nodes in the given editor schema.\n *\n * @param schema The editor schema to be used for nodes and marks detection.\n */\nfunction remarkDisableConstructs(this: Processor, schema: Schema) {\n const data = this.data()\n\n const disabledConstructs: string[] = []\n\n if (!schema.nodes.blockquote) {\n disabledConstructs.push('blockQuote')\n }\n\n if (!schema.marks.bold || !schema.marks.italic) {\n disabledConstructs.push('attention')\n }\n\n if (!schema.nodes.bulletList || !schema.nodes.orderedList) {\n disabledConstructs.push('list')\n }\n\n if (!schema.marks.code) {\n disabledConstructs.push('codeText')\n }\n\n if (!schema.nodes.codeBlock) {\n disabledConstructs.push('codeFenced', 'codeIndented')\n }\n\n if (!schema.nodes.heading) {\n disabledConstructs.push('headingAtx')\n }\n\n if (!schema.nodes.horizontalRule) {\n disabledConstructs.push('thematicBreak')\n }\n\n if (!schema.nodes.image) {\n disabledConstructs.push('labelStartImage')\n }\n\n if (!schema.marks.link) {\n disabledConstructs.push('labelStartLink')\n }\n\n const micromarkExtensions = data.micromarkExtensions || (data.micromarkExtensions = [])\n\n // https://github.com/micromark/micromark#case-turn-off-constructs\n micromarkExtensions.push({\n disable: {\n null: disabledConstructs,\n },\n })\n}\n\nexport { remarkDisableConstructs }\n"],"mappings":";;;;;;;AASA,SAAS,wBAAyC,QAAgB;CAC9D,MAAM,OAAO,KAAK,MAAM;CAExB,MAAM,qBAA+B,EAAE;CAEvC,IAAI,CAAC,OAAO,MAAM,YACd,mBAAmB,KAAK,aAAa;CAGzC,IAAI,CAAC,OAAO,MAAM,QAAQ,CAAC,OAAO,MAAM,QACpC,mBAAmB,KAAK,YAAY;CAGxC,IAAI,CAAC,OAAO,MAAM,cAAc,CAAC,OAAO,MAAM,aAC1C,mBAAmB,KAAK,OAAO;CAGnC,IAAI,CAAC,OAAO,MAAM,MACd,mBAAmB,KAAK,WAAW;CAGvC,IAAI,CAAC,OAAO,MAAM,WACd,mBAAmB,KAAK,cAAc,eAAe;CAGzD,IAAI,CAAC,OAAO,MAAM,SACd,mBAAmB,KAAK,aAAa;CAGzC,IAAI,CAAC,OAAO,MAAM,gBACd,mBAAmB,KAAK,gBAAgB;CAG5C,IAAI,CAAC,OAAO,MAAM,OACd,mBAAmB,KAAK,kBAAkB;CAG9C,IAAI,CAAC,OAAO,MAAM,MACd,mBAAmB,KAAK,iBAAiB;CAM7C,CAH4B,KAAK,wBAAwB,KAAK,sBAAsB,EAAE,GAGlE,KAAK,EACrB,SAAS,EACL,MAAM,oBACT,EACJ,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remark-strikethrough.js","names":[],"sources":["../../../../src/serializers/html/plugins/remark-strikethrough.ts"],"sourcesContent":["import {\n gfmStrikethroughFromMarkdown,\n gfmStrikethroughToMarkdown,\n} from 'mdast-util-gfm-strikethrough'\nimport { gfmStrikethrough } from 'micromark-extension-gfm-strikethrough'\n\nimport type { Options } from 'micromark-extension-gfm-strikethrough'\nimport type { Processor } from 'unified'\n\n/**\n * A remark plugin to add support for the strikethrough extension from the GitHub Flavored Markdown\n * (GFM) specification.\n *\n * This is an standalone plugin which makes use of both the `mdast-util-gfm-strikethrough` and\n * `micromark-extension-gfm-strikethrough` packages, and the implementation is inspired by the\n * third-party `remark-gfm` plugin.\n *\n * The reason why we don't use `remark-gfm` directly is because we don't want to support all other\n * GFM features (footnotes, tables, tagfilter, and tasklists).\n *\n * @param options Configuration options for the plugin.\n */\nfunction remarkStrikethrough(this: Processor, options: Options = {}) {\n const data = this.data()\n\n const micromarkExtensions = data.micromarkExtensions || (data.micromarkExtensions = [])\n const fromMarkdownExtensions = data.fromMarkdownExtensions || (data.fromMarkdownExtensions = [])\n const toMarkdownExtensions = data.toMarkdownExtensions || (data.toMarkdownExtensions = [])\n\n micromarkExtensions.push(gfmStrikethrough(options))\n fromMarkdownExtensions.push(gfmStrikethroughFromMarkdown())\n toMarkdownExtensions.push(gfmStrikethroughToMarkdown())\n}\n\nexport { remarkStrikethrough }\n"],"mappings":";;;;;;;;;;;;;;;;AAsBA,SAAS,oBAAqC,UAAmB,EAAE,EAAE;CACjE,MAAM,OAAO,KAAK,MAAM;CAExB,MAAM,sBAAsB,KAAK,wBAAwB,KAAK,sBAAsB,EAAE;CACtF,MAAM,yBAAyB,KAAK,2BAA2B,KAAK,yBAAyB,EAAE;CAC/F,MAAM,uBAAuB,KAAK,yBAAyB,KAAK,uBAAuB,EAAE;
|
|
1
|
+
{"version":3,"file":"remark-strikethrough.js","names":[],"sources":["../../../../src/serializers/html/plugins/remark-strikethrough.ts"],"sourcesContent":["import {\n gfmStrikethroughFromMarkdown,\n gfmStrikethroughToMarkdown,\n} from 'mdast-util-gfm-strikethrough'\nimport { gfmStrikethrough } from 'micromark-extension-gfm-strikethrough'\n\nimport type { Options } from 'micromark-extension-gfm-strikethrough'\nimport type { Processor } from 'unified'\n\n/**\n * A remark plugin to add support for the strikethrough extension from the GitHub Flavored Markdown\n * (GFM) specification.\n *\n * This is an standalone plugin which makes use of both the `mdast-util-gfm-strikethrough` and\n * `micromark-extension-gfm-strikethrough` packages, and the implementation is inspired by the\n * third-party `remark-gfm` plugin.\n *\n * The reason why we don't use `remark-gfm` directly is because we don't want to support all other\n * GFM features (footnotes, tables, tagfilter, and tasklists).\n *\n * @param options Configuration options for the plugin.\n */\nfunction remarkStrikethrough(this: Processor, options: Options = {}) {\n const data = this.data()\n\n const micromarkExtensions = data.micromarkExtensions || (data.micromarkExtensions = [])\n const fromMarkdownExtensions = data.fromMarkdownExtensions || (data.fromMarkdownExtensions = [])\n const toMarkdownExtensions = data.toMarkdownExtensions || (data.toMarkdownExtensions = [])\n\n micromarkExtensions.push(gfmStrikethrough(options))\n fromMarkdownExtensions.push(gfmStrikethroughFromMarkdown())\n toMarkdownExtensions.push(gfmStrikethroughToMarkdown())\n}\n\nexport { remarkStrikethrough }\n"],"mappings":";;;;;;;;;;;;;;;;AAsBA,SAAS,oBAAqC,UAAmB,EAAE,EAAE;CACjE,MAAM,OAAO,KAAK,MAAM;CAExB,MAAM,sBAAsB,KAAK,wBAAwB,KAAK,sBAAsB,EAAE;CACtF,MAAM,yBAAyB,KAAK,2BAA2B,KAAK,yBAAyB,EAAE;CAC/F,MAAM,uBAAuB,KAAK,yBAAyB,KAAK,uBAAuB,EAAE;CAEzF,oBAAoB,KAAK,iBAAiB,QAAQ,CAAC;CACnD,uBAAuB,KAAK,8BAA8B,CAAC;CAC3D,qBAAqB,KAAK,4BAA4B,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown.d.ts","names":[],"sources":["../../../src/serializers/markdown/markdown.ts"],"mappings":";;;;;AAY8C;KAKzC,4BAAA;;;;AAqBmB;;;;EAbpB,SAAA,GAAY,IAAA;AAAA;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"markdown.d.ts","names":[],"sources":["../../../src/serializers/markdown/markdown.ts"],"mappings":";;;;;AAY8C;KAKzC,4BAAA;;;;AAqBmB;;;;EAbpB,SAAA,GAAY,IAAA;AAAA;;;;;;;;;;;;;;;;;iBAuEP,wBAAA,CAAyB,MAAA,EAAQ,MAAA,GAAS,4BAAA;;;;;;;;iBA+G1C,6BAAA,CAA8B,MAAA,EAAQ,MAAA,GAAM,4BAAA"}
|
|
@@ -21,6 +21,11 @@ const INITIAL_TURNDOWN_OPTIONS = {
|
|
|
21
21
|
emDelimiter: "*",
|
|
22
22
|
strongDelimiter: "**",
|
|
23
23
|
linkStyle: "inlined",
|
|
24
|
+
/**
|
|
25
|
+
* Special rule to handle blank elements (overrides EVERY rule).
|
|
26
|
+
*
|
|
27
|
+
* @see https://github.com/mixmark-io/turndown#special-rules
|
|
28
|
+
*/
|
|
24
29
|
blankReplacement(_, node) {
|
|
25
30
|
const parentNode = node.parentNode;
|
|
26
31
|
if (node.nodeName === "UL" || parentNode?.nodeName === "UL" && node.nodeName === "LI") return `- \n`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown.js","names":[],"sources":["../../../src/serializers/markdown/markdown.ts"],"sourcesContent":["import Turndown from 'turndown'\n\nimport { REGEX_PUNCTUATION } from '../../constants/regular-expressions'\nimport { computeSchemaId, isPlainTextDocument } from '../../helpers/schema'\n\nimport { image } from './plugins/image'\nimport { listItem } from './plugins/list-item'\nimport { paragraph } from './plugins/paragraph'\nimport { strikethrough } from './plugins/strikethrough'\nimport { suggestion } from './plugins/suggestion'\nimport { taskItem } from './plugins/task-item'\n\nimport type { Schema } from '@tiptap/pm/model'\n\n/**\n * The return type for the `createMarkdownSerializer` function.\n */\ntype MarkdownSerializerReturnType = {\n /**\n * Serializes an input HTML string to an output Markdown string.\n *\n * @param html The HTML string to serialize.\n *\n * @returns The serialized Markdown.\n */\n serialize: (html: string) => string\n}\n\n/**\n * The type for the object that holds multiple Markdown serializer instances.\n */\ntype MarkdownSerializerInstanceById = {\n [id: string]: MarkdownSerializerReturnType\n}\n\n/**\n * The bullet list marker for both standard and task list items.\n */\nconst BULLET_LIST_MARKER = '-'\n\n/**\n * Sensible default options to initialize the Turndown with.\n *\n * @see https://github.com/mixmark-io/turndown#options\n */\nconst INITIAL_TURNDOWN_OPTIONS: Turndown.Options = {\n headingStyle: 'atx',\n hr: '---',\n bulletListMarker: BULLET_LIST_MARKER,\n codeBlockStyle: 'fenced',\n fence: '```',\n emDelimiter: '*',\n strongDelimiter: '**',\n linkStyle: 'inlined',\n /**\n * Special rule to handle blank elements (overrides EVERY rule).\n *\n * @see https://github.com/mixmark-io/turndown#special-rules\n */\n blankReplacement(_, node) {\n const parentNode = node.parentNode as HTMLElement\n\n // Return the list marker for empty bullet list items\n if (node.nodeName === 'UL' || (parentNode?.nodeName === 'UL' && node.nodeName === 'LI')) {\n return `${BULLET_LIST_MARKER} \\n`\n }\n\n // Return the list marker for empty ordered list items\n if (node.nodeName === 'OL' || (parentNode?.nodeName === 'OL' && node.nodeName === 'LI')) {\n const start =\n node.nodeName === 'LI'\n ? parentNode.getAttribute('start')\n : node.getAttribute('start')\n const index = Array.prototype.indexOf.call(parentNode.children, node)\n\n return `${start ? Number(start) + index : index + 1}. \\n`\n }\n\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore: The `Turndown.Node` type does not include `isBlock`\n return node.isBlock ? '\\n\\n' : ''\n },\n}\n\n/**\n * Create an HTML to Markdown serializer with the Turndown library for both a rich-text editor, and\n * a plain-text editor. The editor schema is used to detect which nodes and marks are available in\n * the editor, and only parses the input with the minimal required rules.\n *\n * **Note:** Unlike the HTML serializer, built-in rules that are not supported by the schema are not\n * disabled because if the schema does not support certain nodes/marks, the parsing rules don't have\n * valid HTML elements to match in the editor HTML output.\n *\n * @param schema The editor schema to be used for nodes and marks detection.\n *\n * @returns A normalized object for the Markdown serializer.\n */\nfunction createMarkdownSerializer(schema: Schema): MarkdownSerializerReturnType {\n // Initialize Turndown with custom options\n const turndown = new Turndown(INITIAL_TURNDOWN_OPTIONS)\n\n // Turndown ensures Markdown characters are escaped (i.e. `\\`) by default, so they are not\n // interpreted as Markdown when the output is compiled back to HTML. However, for plain-text\n // editors, we need to override the `escape` function to return the input as-is (effectively\n // disabling the escaping behaviour), so that all characters are interpreted as Markdown.\n if (isPlainTextDocument(schema)) {\n turndown.escape = (str) => str\n }\n // As for rich-text editors, we need to override the built-in escaping behaviour with a custom\n // implementation to suit our requirements. Please note that the `escape` function takes the\n // text content of each HTML element, with the exception of code elements, so we can be sure\n // that the escaping behaviour will only touch relevant Markdown characters.\n else {\n turndown.escape = (str) => {\n return (\n str\n // Escape all backslash characters that precede any punctuation marks, to\n // prevent the backslash itself from being interpreted as an escape sequence\n // for the subsequent character. It's important to apply this rule first to\n // avoid double escaping.\n .replace(new RegExp(`(\\\\\\\\${REGEX_PUNCTUATION.source})`, 'g'), '\\\\$1')\n\n // Although the CommonMark specification allows for bulleted or ordered lists\n // inside other bulleted or ordered lists (i.e. `- 1. - 1. Item`), the markup\n // generated by Markdown compilers is not supported by Tiptap, and we need to\n // make sure that text context that matches the ordered list syntax is\n // correctly escaped in order to be interpreted as text.\n .replace(/^(\\d+)\\.(\\s.+|$)/, '$1\\\\.$2')\n )\n }\n }\n\n // Overwrite some built-in rules for handling of special behaviours\n // (see documentation for each extension for more details)\n turndown.use(paragraph(schema.nodes.paragraph, isPlainTextDocument(schema)))\n\n // Overwrite the built-in `image` rule if the corresponding node exists in the schema\n if (schema.nodes.image) {\n turndown.use(image(schema.nodes.image))\n }\n\n // Overwrite the built-in `listItem` rule if the corresponding node exists in the schema\n if ((schema.nodes.bulletList || schema.nodes.orderedList) && schema.nodes.listItem) {\n turndown.use(listItem(schema.nodes.listItem))\n }\n\n // Add a rule for `strikethrough` if the corresponding node exists in the schema\n if (schema.marks.strike) {\n turndown.use(strikethrough(schema.marks.strike))\n }\n\n // Add a rule for `taskItem` if the corresponding nodes exists in the schema\n if (schema.nodes.taskList && schema.nodes.taskItem) {\n turndown.use(taskItem(schema.nodes.taskItem))\n }\n\n // Add a custom rule for all suggestion nodes available in the schema\n Object.values(schema.nodes)\n .filter((node) => node.name.endsWith('Suggestion'))\n .forEach((suggestionNode) => {\n turndown.use(suggestion(suggestionNode))\n })\n\n // Return a normalized `serialize` function\n return {\n serialize(html: string) {\n let markdownResult = html\n\n // Turndown was built to convert HTML into Markdown, expecting the input to be\n // standard-compliant HTML. As such, it collapses all whitespace by default, and there's\n // currently no way to opt-out of this behavior. However, for plain-text editors, we\n // need to preserve Markdown whitespace (otherwise we lose syntax like nested lists) by\n // replacing all instances of the space character (but only if it's preceded by another\n // space character) by the non-breaking space character, and after processing the input\n // with Turndown, we restore the original space character.\n if (isPlainTextDocument(schema)) {\n markdownResult = markdownResult.replace(/ {2,}/g, (m) => m.replace(/ /g, '\\u00a0'))\n }\n\n // Get the serialized Markdown parsed with Turndown\n markdownResult = turndown.turndown(markdownResult)\n\n // Restore the original space character for plain-text editors (as mentioned above),\n // after Markdown serialization has been performed\n if (isPlainTextDocument(schema)) {\n markdownResult = markdownResult.replace(/\\u00a0/g, ' ')\n }\n\n // Return the serialized Markdown parsed with Turndown, and with trailing space\n // characters removed\n return markdownResult.replace(/ +$/gm, '')\n },\n }\n}\n\n/**\n * Object that holds multiple Markdown serializer instances based on a given ID.\n */\nconst markdownSerializerInstanceById: MarkdownSerializerInstanceById = {}\n\n/**\n * Returns a singleton instance of a Markdown serializer based on the provided editor schema.\n *\n * @param schema The editor schema connected to the Markdown serializer instance.\n *\n * @returns The Markdown serializer instance for the given editor schema.\n */\nfunction getMarkdownSerializerInstance(schema: Schema) {\n const id = computeSchemaId(schema)\n\n if (!markdownSerializerInstanceById[id]) {\n markdownSerializerInstanceById[id] = createMarkdownSerializer(schema)\n }\n\n return markdownSerializerInstanceById[id]\n}\n\nexport { BULLET_LIST_MARKER, createMarkdownSerializer, getMarkdownSerializerInstance }\n\nexport type { MarkdownSerializerReturnType }\n"],"mappings":";;;;;;;;;;;;;;AA6CA,MAAM,2BAA6C;CAC/C,cAAc;CACd,IAAI;CACJ,kBAAA;CACA,gBAAgB;CAChB,OAAO;CACP,aAAa;CACb,iBAAiB;CACjB,WAAW;CAMX,iBAAiB,GAAG,MAAM;EACtB,MAAM,aAAa,KAAK;AAGxB,MAAI,KAAK,aAAa,QAAS,YAAY,aAAa,QAAQ,KAAK,aAAa,KAC9E,QAAO;AAIX,MAAI,KAAK,aAAa,QAAS,YAAY,aAAa,QAAQ,KAAK,aAAa,MAAO;GACrF,MAAM,QACF,KAAK,aAAa,OACZ,WAAW,aAAa,QAAQ,GAChC,KAAK,aAAa,QAAQ;GACpC,MAAM,QAAQ,MAAM,UAAU,QAAQ,KAAK,WAAW,UAAU,KAAK;AAErE,UAAO,GAAG,QAAQ,OAAO,MAAM,GAAG,QAAQ,QAAQ,EAAE;;AAKxD,SAAO,KAAK,UAAU,SAAS;;CAEtC;;;;;;;;;;;;;;AAeD,SAAS,yBAAyB,QAA8C;CAE5E,MAAM,WAAW,IAAI,SAAS,yBAAyB;AAMvD,KAAI,oBAAoB,OAAO,CAC3B,UAAS,UAAU,QAAQ;KAO3B,UAAS,UAAU,QAAQ;AACvB,SACI,IAKK,QAAQ,IAAI,OAAO,QAAQ,kBAAkB,OAAO,IAAI,IAAI,EAAE,OAAO,CAOrE,QAAQ,oBAAoB,UAAU;;AAOvD,UAAS,IAAI,UAAU,OAAO,MAAM,WAAW,oBAAoB,OAAO,CAAC,CAAC;AAG5E,KAAI,OAAO,MAAM,MACb,UAAS,IAAI,MAAM,OAAO,MAAM,MAAM,CAAC;AAI3C,MAAK,OAAO,MAAM,cAAc,OAAO,MAAM,gBAAgB,OAAO,MAAM,SACtE,UAAS,IAAI,SAAS,OAAO,MAAM,SAAS,CAAC;AAIjD,KAAI,OAAO,MAAM,OACb,UAAS,IAAI,cAAc,OAAO,MAAM,OAAO,CAAC;AAIpD,KAAI,OAAO,MAAM,YAAY,OAAO,MAAM,SACtC,UAAS,IAAI,SAAS,OAAO,MAAM,SAAS,CAAC;AAIjD,QAAO,OAAO,OAAO,MAAM,CACtB,QAAQ,SAAS,KAAK,KAAK,SAAS,aAAa,CAAC,CAClD,SAAS,mBAAmB;AACzB,WAAS,IAAI,WAAW,eAAe,CAAC;GAC1C;AAGN,QAAO,EACH,UAAU,MAAc;EACpB,IAAI,iBAAiB;AASrB,MAAI,oBAAoB,OAAO,CAC3B,kBAAiB,eAAe,QAAQ,WAAW,MAAM,EAAE,QAAQ,MAAM,OAAS,CAAC;AAIvF,mBAAiB,SAAS,SAAS,eAAe;AAIlD,MAAI,oBAAoB,OAAO,CAC3B,kBAAiB,eAAe,QAAQ,WAAW,IAAI;AAK3D,SAAO,eAAe,QAAQ,SAAS,GAAG;IAEjD;;;;;AAML,MAAM,iCAAiE,EAAE;;;;;;;;AASzE,SAAS,8BAA8B,QAAgB;CACnD,MAAM,KAAK,gBAAgB,OAAO;AAElC,KAAI,CAAC,+BAA+B,IAChC,gCAA+B,MAAM,yBAAyB,OAAO;AAGzE,QAAO,+BAA+B"}
|
|
1
|
+
{"version":3,"file":"markdown.js","names":[],"sources":["../../../src/serializers/markdown/markdown.ts"],"sourcesContent":["import Turndown from 'turndown'\n\nimport { REGEX_PUNCTUATION } from '../../constants/regular-expressions'\nimport { computeSchemaId, isPlainTextDocument } from '../../helpers/schema'\n\nimport { image } from './plugins/image'\nimport { listItem } from './plugins/list-item'\nimport { paragraph } from './plugins/paragraph'\nimport { strikethrough } from './plugins/strikethrough'\nimport { suggestion } from './plugins/suggestion'\nimport { taskItem } from './plugins/task-item'\n\nimport type { Schema } from '@tiptap/pm/model'\n\n/**\n * The return type for the `createMarkdownSerializer` function.\n */\ntype MarkdownSerializerReturnType = {\n /**\n * Serializes an input HTML string to an output Markdown string.\n *\n * @param html The HTML string to serialize.\n *\n * @returns The serialized Markdown.\n */\n serialize: (html: string) => string\n}\n\n/**\n * The type for the object that holds multiple Markdown serializer instances.\n */\ntype MarkdownSerializerInstanceById = {\n [id: string]: MarkdownSerializerReturnType\n}\n\n/**\n * The bullet list marker for both standard and task list items.\n */\nconst BULLET_LIST_MARKER = '-'\n\n/**\n * Sensible default options to initialize the Turndown with.\n *\n * @see https://github.com/mixmark-io/turndown#options\n */\nconst INITIAL_TURNDOWN_OPTIONS: Turndown.Options = {\n headingStyle: 'atx',\n hr: '---',\n bulletListMarker: BULLET_LIST_MARKER,\n codeBlockStyle: 'fenced',\n fence: '```',\n emDelimiter: '*',\n strongDelimiter: '**',\n linkStyle: 'inlined',\n /**\n * Special rule to handle blank elements (overrides EVERY rule).\n *\n * @see https://github.com/mixmark-io/turndown#special-rules\n */\n blankReplacement(_, node) {\n const parentNode = node.parentNode as HTMLElement\n\n // Return the list marker for empty bullet list items\n if (node.nodeName === 'UL' || (parentNode?.nodeName === 'UL' && node.nodeName === 'LI')) {\n return `${BULLET_LIST_MARKER} \\n`\n }\n\n // Return the list marker for empty ordered list items\n if (node.nodeName === 'OL' || (parentNode?.nodeName === 'OL' && node.nodeName === 'LI')) {\n const start =\n node.nodeName === 'LI'\n ? parentNode.getAttribute('start')\n : node.getAttribute('start')\n const index = Array.prototype.indexOf.call(parentNode.children, node)\n\n return `${start ? Number(start) + index : index + 1}. \\n`\n }\n\n // @ts-ignore: The `Turndown.Node` type does not include `isBlock`\n return node.isBlock ? '\\n\\n' : ''\n },\n}\n\n/**\n * Create an HTML to Markdown serializer with the Turndown library for both a rich-text editor, and\n * a plain-text editor. The editor schema is used to detect which nodes and marks are available in\n * the editor, and only parses the input with the minimal required rules.\n *\n * **Note:** Unlike the HTML serializer, built-in rules that are not supported by the schema are not\n * disabled because if the schema does not support certain nodes/marks, the parsing rules don't have\n * valid HTML elements to match in the editor HTML output.\n *\n * @param schema The editor schema to be used for nodes and marks detection.\n *\n * @returns A normalized object for the Markdown serializer.\n */\nfunction createMarkdownSerializer(schema: Schema): MarkdownSerializerReturnType {\n // Initialize Turndown with custom options\n const turndown = new Turndown(INITIAL_TURNDOWN_OPTIONS)\n\n // Turndown ensures Markdown characters are escaped (i.e. `\\`) by default, so they are not\n // interpreted as Markdown when the output is compiled back to HTML. However, for plain-text\n // editors, we need to override the `escape` function to return the input as-is (effectively\n // disabling the escaping behaviour), so that all characters are interpreted as Markdown.\n if (isPlainTextDocument(schema)) {\n turndown.escape = (str) => str\n }\n\n // As for rich-text editors, we need to override the built-in escaping behaviour with a custom\n // implementation to suit our requirements. Please note that the `escape` function takes the\n // text content of each HTML element, with the exception of code elements, so we can be sure\n // that the escaping behaviour will only touch relevant Markdown characters.\n else {\n turndown.escape = (str) => {\n return (\n str\n // Escape all backslash characters that precede any punctuation marks, to\n // prevent the backslash itself from being interpreted as an escape sequence\n // for the subsequent character. It's important to apply this rule first to\n // avoid double escaping.\n .replace(new RegExp(`(\\\\\\\\${REGEX_PUNCTUATION.source})`, 'g'), '\\\\$1')\n\n // Although the CommonMark specification allows for bulleted or ordered lists\n // inside other bulleted or ordered lists (i.e. `- 1. - 1. Item`), the markup\n // generated by Markdown compilers is not supported by Tiptap, and we need to\n // make sure that text context that matches the ordered list syntax is\n // correctly escaped in order to be interpreted as text.\n .replace(/^(\\d+)\\.(\\s.+|$)/, '$1\\\\.$2')\n )\n }\n }\n\n // Overwrite some built-in rules for handling of special behaviours\n // (see documentation for each extension for more details)\n turndown.use(paragraph(schema.nodes.paragraph, isPlainTextDocument(schema)))\n\n // Overwrite the built-in `image` rule if the corresponding node exists in the schema\n if (schema.nodes.image) {\n turndown.use(image(schema.nodes.image))\n }\n\n // Overwrite the built-in `listItem` rule if the corresponding node exists in the schema\n if ((schema.nodes.bulletList || schema.nodes.orderedList) && schema.nodes.listItem) {\n turndown.use(listItem(schema.nodes.listItem))\n }\n\n // Add a rule for `strikethrough` if the corresponding node exists in the schema\n if (schema.marks.strike) {\n turndown.use(strikethrough(schema.marks.strike))\n }\n\n // Add a rule for `taskItem` if the corresponding nodes exists in the schema\n if (schema.nodes.taskList && schema.nodes.taskItem) {\n turndown.use(taskItem(schema.nodes.taskItem))\n }\n\n // Add a custom rule for all suggestion nodes available in the schema\n Object.values(schema.nodes)\n .filter((node) => node.name.endsWith('Suggestion'))\n .forEach((suggestionNode) => {\n turndown.use(suggestion(suggestionNode))\n })\n\n // Return a normalized `serialize` function\n return {\n serialize(html: string) {\n let markdownResult = html\n\n // Turndown was built to convert HTML into Markdown, expecting the input to be\n // standard-compliant HTML. As such, it collapses all whitespace by default, and there's\n // currently no way to opt-out of this behavior. However, for plain-text editors, we\n // need to preserve Markdown whitespace (otherwise we lose syntax like nested lists) by\n // replacing all instances of the space character (but only if it's preceded by another\n // space character) by the non-breaking space character, and after processing the input\n // with Turndown, we restore the original space character.\n if (isPlainTextDocument(schema)) {\n markdownResult = markdownResult.replace(/ {2,}/g, (m) => m.replace(/ /g, '\\u00a0'))\n }\n\n // Get the serialized Markdown parsed with Turndown\n markdownResult = turndown.turndown(markdownResult)\n\n // Restore the original space character for plain-text editors (as mentioned above),\n // after Markdown serialization has been performed\n if (isPlainTextDocument(schema)) {\n markdownResult = markdownResult.replace(/\\u00a0/g, ' ')\n }\n\n // Return the serialized Markdown parsed with Turndown, and with trailing space\n // characters removed\n return markdownResult.replace(/ +$/gm, '')\n },\n }\n}\n\n/**\n * Object that holds multiple Markdown serializer instances based on a given ID.\n */\nconst markdownSerializerInstanceById: MarkdownSerializerInstanceById = {}\n\n/**\n * Returns a singleton instance of a Markdown serializer based on the provided editor schema.\n *\n * @param schema The editor schema connected to the Markdown serializer instance.\n *\n * @returns The Markdown serializer instance for the given editor schema.\n */\nfunction getMarkdownSerializerInstance(schema: Schema) {\n const id = computeSchemaId(schema)\n\n if (!markdownSerializerInstanceById[id]) {\n markdownSerializerInstanceById[id] = createMarkdownSerializer(schema)\n }\n\n return markdownSerializerInstanceById[id]\n}\n\nexport { BULLET_LIST_MARKER, createMarkdownSerializer, getMarkdownSerializerInstance }\n\nexport type { MarkdownSerializerReturnType }\n"],"mappings":";;;;;;;;;;;;;;AA6CA,MAAM,2BAA6C;CAC/C,cAAc;CACd,IAAI;CACJ,kBAAA;CACA,gBAAgB;CAChB,OAAO;CACP,aAAa;CACb,iBAAiB;CACjB,WAAW;;;;;;CAMX,iBAAiB,GAAG,MAAM;EACtB,MAAM,aAAa,KAAK;EAGxB,IAAI,KAAK,aAAa,QAAS,YAAY,aAAa,QAAQ,KAAK,aAAa,MAC9E,OAAO;EAIX,IAAI,KAAK,aAAa,QAAS,YAAY,aAAa,QAAQ,KAAK,aAAa,MAAO;GACrF,MAAM,QACF,KAAK,aAAa,OACZ,WAAW,aAAa,QAAQ,GAChC,KAAK,aAAa,QAAQ;GACpC,MAAM,QAAQ,MAAM,UAAU,QAAQ,KAAK,WAAW,UAAU,KAAK;GAErE,OAAO,GAAG,QAAQ,OAAO,MAAM,GAAG,QAAQ,QAAQ,EAAE;;EAIxD,OAAO,KAAK,UAAU,SAAS;;CAEtC;;;;;;;;;;;;;;AAeD,SAAS,yBAAyB,QAA8C;CAE5E,MAAM,WAAW,IAAI,SAAS,yBAAyB;CAMvD,IAAI,oBAAoB,OAAO,EAC3B,SAAS,UAAU,QAAQ;MAQ3B,SAAS,UAAU,QAAQ;EACvB,OACI,IAKK,QAAQ,IAAI,OAAO,QAAQ,kBAAkB,OAAO,IAAI,IAAI,EAAE,OAAO,CAOrE,QAAQ,oBAAoB,UAAU;;CAOvD,SAAS,IAAI,UAAU,OAAO,MAAM,WAAW,oBAAoB,OAAO,CAAC,CAAC;CAG5E,IAAI,OAAO,MAAM,OACb,SAAS,IAAI,MAAM,OAAO,MAAM,MAAM,CAAC;CAI3C,KAAK,OAAO,MAAM,cAAc,OAAO,MAAM,gBAAgB,OAAO,MAAM,UACtE,SAAS,IAAI,SAAS,OAAO,MAAM,SAAS,CAAC;CAIjD,IAAI,OAAO,MAAM,QACb,SAAS,IAAI,cAAc,OAAO,MAAM,OAAO,CAAC;CAIpD,IAAI,OAAO,MAAM,YAAY,OAAO,MAAM,UACtC,SAAS,IAAI,SAAS,OAAO,MAAM,SAAS,CAAC;CAIjD,OAAO,OAAO,OAAO,MAAM,CACtB,QAAQ,SAAS,KAAK,KAAK,SAAS,aAAa,CAAC,CAClD,SAAS,mBAAmB;EACzB,SAAS,IAAI,WAAW,eAAe,CAAC;GAC1C;CAGN,OAAO,EACH,UAAU,MAAc;EACpB,IAAI,iBAAiB;EASrB,IAAI,oBAAoB,OAAO,EAC3B,iBAAiB,eAAe,QAAQ,WAAW,MAAM,EAAE,QAAQ,MAAM,OAAS,CAAC;EAIvF,iBAAiB,SAAS,SAAS,eAAe;EAIlD,IAAI,oBAAoB,OAAO,EAC3B,iBAAiB,eAAe,QAAQ,WAAW,IAAI;EAK3D,OAAO,eAAe,QAAQ,SAAS,GAAG;IAEjD;;;;;AAML,MAAM,iCAAiE,EAAE;;;;;;;;AASzE,SAAS,8BAA8B,QAAgB;CACnD,MAAM,KAAK,gBAAgB,OAAO;CAElC,IAAI,CAAC,+BAA+B,KAChC,+BAA+B,MAAM,yBAAyB,OAAO;CAGzE,OAAO,+BAA+B"}
|
|
@@ -19,7 +19,7 @@ function image(nodeType) {
|
|
|
19
19
|
turndown.addRule(nodeType.name, {
|
|
20
20
|
filter: "img",
|
|
21
21
|
replacement(_, node) {
|
|
22
|
-
const src =
|
|
22
|
+
const src = node.getAttribute("src") ?? "";
|
|
23
23
|
const link = src.startsWith("data:") ? `${src.split(",")[0]},NOT_SUPPORTED` : src;
|
|
24
24
|
const alt = cleanAttribute(node.getAttribute("alt"));
|
|
25
25
|
const title = cleanAttribute(node.getAttribute("title"));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"image.js","names":[],"sources":["../../../../src/serializers/markdown/plugins/image.ts"],"sourcesContent":["import type { NodeType } from '@tiptap/pm/model'\nimport type Turndown from 'turndown'\n\n/**\n * Cleans an attribute value by replacing multiple newlines with a single one.\n *\n * @param attribute The attribute value to clean.\n */\nfunction cleanAttribute(attribute: string | null): string {\n return attribute ? attribute.replace(/(\\n+\\s*)+/g, '\\n') : ''\n}\n\n/**\n * A Turndown plugin which adds a custom rule for images. This custom rule is required to disable\n * support for Data URLs (URLs prefixed with the `data: scheme`), while displaying an explicit\n * message in the Markdown output (for debugging and testing).\n *\n * @param nodeType The node object that matches this rule.\n */\nfunction image(nodeType: NodeType): Turndown.Plugin {\n return (turndown: Turndown) => {\n turndown.addRule(nodeType.name, {\n filter: 'img',\n replacement(_, node) {\n const src =
|
|
1
|
+
{"version":3,"file":"image.js","names":[],"sources":["../../../../src/serializers/markdown/plugins/image.ts"],"sourcesContent":["import type { NodeType } from '@tiptap/pm/model'\nimport type Turndown from 'turndown'\n\n/**\n * Cleans an attribute value by replacing multiple newlines with a single one.\n *\n * @param attribute The attribute value to clean.\n */\nfunction cleanAttribute(attribute: string | null): string {\n return attribute ? attribute.replace(/(\\n+\\s*)+/g, '\\n') : ''\n}\n\n/**\n * A Turndown plugin which adds a custom rule for images. This custom rule is required to disable\n * support for Data URLs (URLs prefixed with the `data: scheme`), while displaying an explicit\n * message in the Markdown output (for debugging and testing).\n *\n * @param nodeType The node object that matches this rule.\n */\nfunction image(nodeType: NodeType): Turndown.Plugin {\n return (turndown: Turndown) => {\n turndown.addRule(nodeType.name, {\n filter: 'img',\n replacement(_, node) {\n const src = (node as Element).getAttribute('src') ?? ''\n\n // Preserve Data URL image prefix with message about base64 being unsupported\n const link = src.startsWith('data:') ? `${src.split(',')[0]},NOT_SUPPORTED` : src\n\n const alt = cleanAttribute((node as Element).getAttribute('alt'))\n const title = cleanAttribute((node as Element).getAttribute('title'))\n\n return src ? `` : ''\n },\n })\n }\n}\n\nexport { image }\n"],"mappings":";;;;;;AAQA,SAAS,eAAe,WAAkC;CACtD,OAAO,YAAY,UAAU,QAAQ,cAAc,KAAK,GAAG;;;;;;;;;AAU/D,SAAS,MAAM,UAAqC;CAChD,QAAQ,aAAuB;EAC3B,SAAS,QAAQ,SAAS,MAAM;GAC5B,QAAQ;GACR,YAAY,GAAG,MAAM;IACjB,MAAM,MAAO,KAAiB,aAAa,MAAM,IAAI;IAGrD,MAAM,OAAO,IAAI,WAAW,QAAQ,GAAG,GAAG,IAAI,MAAM,IAAI,CAAC,GAAG,kBAAkB;IAE9E,MAAM,MAAM,eAAgB,KAAiB,aAAa,MAAM,CAAC;IACjE,MAAM,QAAQ,eAAgB,KAAiB,aAAa,QAAQ,CAAC;IAErE,OAAO,MAAM,KAAK,IAAI,IAAI,OAAO,MAAM,SAAS,IAAI,KAAK,MAAM,KAAK,GAAG,KAAK;;GAEnF,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"list-item.js","names":[],"sources":["../../../../src/serializers/markdown/plugins/list-item.ts"],"sourcesContent":["import { extractTagsFromParseRules } from '../../../helpers/serializer'\nimport { BULLET_LIST_MARKER } from '../markdown'\n\nimport type { NodeType } from '@tiptap/pm/model'\nimport type Turndown from 'turndown'\n\n/**\n * A Turndown plugin which adds a custom rule for standard list items (i.e., not task list items),\n * based on the original list item rule. This custom rule is required to avoid conflicts with task\n * list items, and to normalize the Markdown output.\n *\n * @see https://github.com/mixmark-io/turndown/blob/v7.1.1/src/commonmark-rules.js#L61\n *\n * @param nodeType The node object that matches this rule.\n */\nfunction listItem(nodeType: NodeType): Turndown.Plugin {\n const tags = extractTagsFromParseRules(nodeType.spec.parseDOM)\n\n return (turndown: Turndown) => {\n turndown.addRule(nodeType.name, {\n filter(node) {\n return (\n tags.some((tag) => tag.toUpperCase() === node.nodeName) &&\n node.getAttribute('data-type') !== 'taskItem'\n )\n },\n replacement(content, node) {\n const parentNode = node.parentNode as HTMLElement\n let listItemMarker = `${BULLET_LIST_MARKER} `\n\n // Use a sequence of 1–9 digits for the ordered list marker (CommonMark specification)\n if (parentNode?.nodeName === 'OL') {\n const start = parentNode.getAttribute('start')\n const index = Array.prototype.indexOf.call(parentNode.children, node)\n\n listItemMarker = `${start ? Number(start) + index : index + 1}. `\n }\n\n const newContent = content\n // Remove leading newlines\n .replace(/^\\n+/, '')\n // Replace trailing newlines with a single one\n .replace(/\\n+$/, '\\n')\n // Indent list items with 4 spaces\n .replace(/\\n/gm, '\\n ')\n\n return `${listItemMarker}${newContent.trim()}${\n node.nextSibling && !newContent.endsWith('\\n') ? '\\n' : ''\n }`\n },\n })\n }\n}\n\nexport { listItem }\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"list-item.js","names":[],"sources":["../../../../src/serializers/markdown/plugins/list-item.ts"],"sourcesContent":["import { extractTagsFromParseRules } from '../../../helpers/serializer'\nimport { BULLET_LIST_MARKER } from '../markdown'\n\nimport type { NodeType } from '@tiptap/pm/model'\nimport type Turndown from 'turndown'\n\n/**\n * A Turndown plugin which adds a custom rule for standard list items (i.e., not task list items),\n * based on the original list item rule. This custom rule is required to avoid conflicts with task\n * list items, and to normalize the Markdown output.\n *\n * @see https://github.com/mixmark-io/turndown/blob/v7.1.1/src/commonmark-rules.js#L61\n *\n * @param nodeType The node object that matches this rule.\n */\nfunction listItem(nodeType: NodeType): Turndown.Plugin {\n const tags = extractTagsFromParseRules(nodeType.spec.parseDOM)\n\n return (turndown: Turndown) => {\n turndown.addRule(nodeType.name, {\n filter(node) {\n return (\n tags.some((tag) => tag.toUpperCase() === node.nodeName) &&\n node.getAttribute('data-type') !== 'taskItem'\n )\n },\n replacement(content, node) {\n const parentNode = node.parentNode as HTMLElement\n let listItemMarker = `${BULLET_LIST_MARKER} `\n\n // Use a sequence of 1–9 digits for the ordered list marker (CommonMark specification)\n if (parentNode?.nodeName === 'OL') {\n const start = parentNode.getAttribute('start')\n const index = Array.prototype.indexOf.call(parentNode.children, node)\n\n listItemMarker = `${start ? Number(start) + index : index + 1}. `\n }\n\n const newContent = content\n // Remove leading newlines\n .replace(/^\\n+/, '')\n // Replace trailing newlines with a single one\n .replace(/\\n+$/, '\\n')\n // Indent list items with 4 spaces\n .replace(/\\n/gm, '\\n ')\n\n return `${listItemMarker}${newContent.trim()}${\n node.nextSibling && !newContent.endsWith('\\n') ? '\\n' : ''\n }`\n },\n })\n }\n}\n\nexport { listItem }\n"],"mappings":";;;;;;;;;;;;AAeA,SAAS,SAAS,UAAqC;CACnD,MAAM,OAAO,0BAA0B,SAAS,KAAK,SAAS;CAE9D,QAAQ,aAAuB;EAC3B,SAAS,QAAQ,SAAS,MAAM;GAC5B,OAAO,MAAM;IACT,OACI,KAAK,MAAM,QAAQ,IAAI,aAAa,KAAK,KAAK,SAAS,IACvD,KAAK,aAAa,YAAY,KAAK;;GAG3C,YAAY,SAAS,MAAM;IACvB,MAAM,aAAa,KAAK;IACxB,IAAI,iBAAiB;IAGrB,IAAI,YAAY,aAAa,MAAM;KAC/B,MAAM,QAAQ,WAAW,aAAa,QAAQ;KAC9C,MAAM,QAAQ,MAAM,UAAU,QAAQ,KAAK,WAAW,UAAU,KAAK;KAErE,iBAAiB,GAAG,QAAQ,OAAO,MAAM,GAAG,QAAQ,QAAQ,EAAE;;IAGlE,MAAM,aAAa,QAEd,QAAQ,QAAQ,GAAG,CAEnB,QAAQ,QAAQ,KAAK,CAErB,QAAQ,QAAQ,SAAS;IAE9B,OAAO,GAAG,iBAAiB,WAAW,MAAM,GACxC,KAAK,eAAe,CAAC,WAAW,SAAS,KAAK,GAAG,OAAO;;GAGnE,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paragraph.js","names":[],"sources":["../../../../src/serializers/markdown/plugins/paragraph.ts"],"sourcesContent":["import type { NodeType } from '@tiptap/pm/model'\nimport type Turndown from 'turndown'\n\n/**\n * A Turndown plugin which adds a custom rule for paragraphs. This custom rule is required to avoid\n * adding unnecessary blank lines between paragraphs to plain-text documents, and to list items in\n * rich-text documents.\n *\n * @param nodeType The node object that matches this rule.\n * @param isPlainText Specifies if the schema represents a plain-text document.\n */\nfunction paragraph(nodeType: NodeType, isPlainText: boolean): Turndown.Plugin {\n return (turndown: Turndown) => {\n turndown.addRule(nodeType.name, {\n filter: 'p',\n replacement(content, node) {\n const useSingleLineSpacing =\n isPlainText ||\n // Paragraphs within list items should be wrapped with a single line feed to\n // maintain proper list formatting in rich-text documents.\n (!isPlainText && node.parentNode?.nodeName === 'LI')\n\n return useSingleLineSpacing ? `\\n${content}\\n` : `\\n\\n${content}\\n\\n`\n },\n })\n }\n}\n\nexport { paragraph }\n"],"mappings":";;;;;;;;;AAWA,SAAS,UAAU,UAAoB,aAAuC;
|
|
1
|
+
{"version":3,"file":"paragraph.js","names":[],"sources":["../../../../src/serializers/markdown/plugins/paragraph.ts"],"sourcesContent":["import type { NodeType } from '@tiptap/pm/model'\nimport type Turndown from 'turndown'\n\n/**\n * A Turndown plugin which adds a custom rule for paragraphs. This custom rule is required to avoid\n * adding unnecessary blank lines between paragraphs to plain-text documents, and to list items in\n * rich-text documents.\n *\n * @param nodeType The node object that matches this rule.\n * @param isPlainText Specifies if the schema represents a plain-text document.\n */\nfunction paragraph(nodeType: NodeType, isPlainText: boolean): Turndown.Plugin {\n return (turndown: Turndown) => {\n turndown.addRule(nodeType.name, {\n filter: 'p',\n replacement(content, node) {\n const useSingleLineSpacing =\n isPlainText ||\n // Paragraphs within list items should be wrapped with a single line feed to\n // maintain proper list formatting in rich-text documents.\n (!isPlainText && node.parentNode?.nodeName === 'LI')\n\n return useSingleLineSpacing ? `\\n${content}\\n` : `\\n\\n${content}\\n\\n`\n },\n })\n }\n}\n\nexport { paragraph }\n"],"mappings":";;;;;;;;;AAWA,SAAS,UAAU,UAAoB,aAAuC;CAC1E,QAAQ,aAAuB;EAC3B,SAAS,QAAQ,SAAS,MAAM;GAC5B,QAAQ;GACR,YAAY,SAAS,MAAM;IAOvB,OALI,eAGC,CAAC,eAAe,KAAK,YAAY,aAAa,OAErB,KAAK,QAAQ,MAAM,OAAO,QAAQ;;GAEvE,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"strikethrough.js","names":[],"sources":["../../../../src/serializers/markdown/plugins/strikethrough.ts"],"sourcesContent":["import { extractTagsFromParseRules } from '../../../helpers/serializer'\n\nimport type { MarkType } from '@tiptap/pm/model'\nimport type Turndown from 'turndown'\n\n/**\n * A Turndown plugin which adds a rule for strikethrough text, based on the original plugin. This\n * rules forces two tildes instead of one.\n *\n * @see https://github.com/mixmark-io/turndown-plugin-gfm/blob/v1.0.1/src/strikethrough.js\n *\n * @param markType The mark object that matches this rule.\n */\nfunction strikethrough(markType: MarkType): Turndown.Plugin {\n const tags = extractTagsFromParseRules(markType.spec.parseDOM)\n\n return (turndown: Turndown) => {\n turndown.addRule(markType.name, {\n filter(node) {\n return tags.some((tag) => tag.toUpperCase() === node.nodeName)\n },\n replacement(content) {\n return `~~${content}~~`\n },\n })\n }\n}\n\nexport { strikethrough }\n"],"mappings":";;;;;;;;;;AAaA,SAAS,cAAc,UAAqC;CACxD,MAAM,OAAO,0BAA0B,SAAS,KAAK,SAAS;
|
|
1
|
+
{"version":3,"file":"strikethrough.js","names":[],"sources":["../../../../src/serializers/markdown/plugins/strikethrough.ts"],"sourcesContent":["import { extractTagsFromParseRules } from '../../../helpers/serializer'\n\nimport type { MarkType } from '@tiptap/pm/model'\nimport type Turndown from 'turndown'\n\n/**\n * A Turndown plugin which adds a rule for strikethrough text, based on the original plugin. This\n * rules forces two tildes instead of one.\n *\n * @see https://github.com/mixmark-io/turndown-plugin-gfm/blob/v1.0.1/src/strikethrough.js\n *\n * @param markType The mark object that matches this rule.\n */\nfunction strikethrough(markType: MarkType): Turndown.Plugin {\n const tags = extractTagsFromParseRules(markType.spec.parseDOM)\n\n return (turndown: Turndown) => {\n turndown.addRule(markType.name, {\n filter(node) {\n return tags.some((tag) => tag.toUpperCase() === node.nodeName)\n },\n replacement(content) {\n return `~~${content}~~`\n },\n })\n }\n}\n\nexport { strikethrough }\n"],"mappings":";;;;;;;;;;AAaA,SAAS,cAAc,UAAqC;CACxD,MAAM,OAAO,0BAA0B,SAAS,KAAK,SAAS;CAE9D,QAAQ,aAAuB;EAC3B,SAAS,QAAQ,SAAS,MAAM;GAC5B,OAAO,MAAM;IACT,OAAO,KAAK,MAAM,QAAQ,IAAI,aAAa,KAAK,KAAK,SAAS;;GAElE,YAAY,SAAS;IACjB,OAAO,KAAK,QAAQ;;GAE3B,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"suggestion.js","names":[],"sources":["../../../../src/serializers/markdown/plugins/suggestion.ts"],"sourcesContent":["import { kebabCase } from 'lodash-es'\n\nimport type { NodeType } from '@tiptap/pm/model'\nimport type Turndown from 'turndown'\n\n/**\n * A Turndown plugin which adds a custom rule for suggestion nodes created by the suggestion\n * extension factory function.\n *\n * @param nodeType The node object that matches this rule.\n */\nfunction suggestion(nodeType: NodeType): Turndown.Plugin {\n const attributeType = kebabCase(nodeType.name.replace(/Suggestion$/, ''))\n\n return (turndown: Turndown) => {\n turndown.addRule(nodeType.name, {\n filter(node: Element) {\n return node.hasAttribute(`data-${attributeType}`)\n },\n replacement(_, node) {\n const label = String((node as Element).getAttribute('data-label'))\n const id = String((node as Element).getAttribute('data-id'))\n\n return `[${label}](${attributeType}://${id})`\n },\n })\n }\n}\n\nexport { suggestion }\n"],"mappings":";;;;;;;;AAWA,SAAS,WAAW,UAAqC;CACrD,MAAM,gBAAgB,UAAU,SAAS,KAAK,QAAQ,eAAe,GAAG,CAAC;
|
|
1
|
+
{"version":3,"file":"suggestion.js","names":[],"sources":["../../../../src/serializers/markdown/plugins/suggestion.ts"],"sourcesContent":["import { kebabCase } from 'lodash-es'\n\nimport type { NodeType } from '@tiptap/pm/model'\nimport type Turndown from 'turndown'\n\n/**\n * A Turndown plugin which adds a custom rule for suggestion nodes created by the suggestion\n * extension factory function.\n *\n * @param nodeType The node object that matches this rule.\n */\nfunction suggestion(nodeType: NodeType): Turndown.Plugin {\n const attributeType = kebabCase(nodeType.name.replace(/Suggestion$/, ''))\n\n return (turndown: Turndown) => {\n turndown.addRule(nodeType.name, {\n filter(node: Element) {\n return node.hasAttribute(`data-${attributeType}`)\n },\n replacement(_, node) {\n const label = String((node as Element).getAttribute('data-label'))\n const id = String((node as Element).getAttribute('data-id'))\n\n return `[${label}](${attributeType}://${id})`\n },\n })\n }\n}\n\nexport { suggestion }\n"],"mappings":";;;;;;;;AAWA,SAAS,WAAW,UAAqC;CACrD,MAAM,gBAAgB,UAAU,SAAS,KAAK,QAAQ,eAAe,GAAG,CAAC;CAEzE,QAAQ,aAAuB;EAC3B,SAAS,QAAQ,SAAS,MAAM;GAC5B,OAAO,MAAe;IAClB,OAAO,KAAK,aAAa,QAAQ,gBAAgB;;GAErD,YAAY,GAAG,MAAM;IAIjB,OAAO,IAHO,OAAQ,KAAiB,aAAa,aAAa,CAGjD,CAAC,IAAI,cAAc,KAFxB,OAAQ,KAAiB,aAAa,UAAU,CAEjB,CAAC;;GAElD,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task-item.js","names":[],"sources":["../../../../src/serializers/markdown/plugins/task-item.ts"],"sourcesContent":["import { extractTagsFromParseRules } from '../../../helpers/serializer'\nimport { BULLET_LIST_MARKER } from '../markdown'\n\nimport type { NodeType } from '@tiptap/pm/model'\nimport type Turndown from 'turndown'\n\n/**\n * A Turndown plugin which adds a rule for task list items (i.e., `* [ ] Task`), based on the\n * original list item rule. This rule not only avoids conflicts with standard list items, but also\n * normalizes the Markdown output.\n *\n * @see https://github.com/mixmark-io/turndown/blob/v7.1.1/src/commonmark-rules.js#L61\n *\n * @param nodeType The node object that matches this rule.\n */\nfunction taskItem(nodeType: NodeType): Turndown.Plugin {\n const tags = extractTagsFromParseRules(nodeType.spec.parseDOM)\n\n return (turndown: Turndown) => {\n turndown.addRule(nodeType.name, {\n filter(node) {\n return (\n tags.some((tag) => tag.toUpperCase().startsWith(node.nodeName)) &&\n node.getAttribute('data-type') === 'taskItem'\n )\n },\n replacement(content, node) {\n const parentNode = node.parentNode as HTMLElement\n let listItemMarker = `${BULLET_LIST_MARKER} `\n\n if (parentNode?.nodeName === 'UL') {\n const checked = (node as HTMLLIElement).getAttribute('data-checked')\n listItemMarker = `${listItemMarker}${checked === 'true' ? '[x]' : '[ ]'} `\n }\n\n const newContent = content\n // Remove leading newlines\n .replace(/^\\n+/, '')\n // Replace trailing newlines with a single one\n .replace(/\\n+$/, '\\n')\n // Indent list items with 4 spaces\n .replace(/\\s*\\n/gm, '\\n ')\n\n return `${listItemMarker}${newContent.trim()}${\n node.nextSibling && !newContent.endsWith('\\n') ? '\\n' : ''\n }`\n },\n })\n }\n}\n\nexport { taskItem }\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"task-item.js","names":[],"sources":["../../../../src/serializers/markdown/plugins/task-item.ts"],"sourcesContent":["import { extractTagsFromParseRules } from '../../../helpers/serializer'\nimport { BULLET_LIST_MARKER } from '../markdown'\n\nimport type { NodeType } from '@tiptap/pm/model'\nimport type Turndown from 'turndown'\n\n/**\n * A Turndown plugin which adds a rule for task list items (i.e., `* [ ] Task`), based on the\n * original list item rule. This rule not only avoids conflicts with standard list items, but also\n * normalizes the Markdown output.\n *\n * @see https://github.com/mixmark-io/turndown/blob/v7.1.1/src/commonmark-rules.js#L61\n *\n * @param nodeType The node object that matches this rule.\n */\nfunction taskItem(nodeType: NodeType): Turndown.Plugin {\n const tags = extractTagsFromParseRules(nodeType.spec.parseDOM)\n\n return (turndown: Turndown) => {\n turndown.addRule(nodeType.name, {\n filter(node) {\n return (\n tags.some((tag) => tag.toUpperCase().startsWith(node.nodeName)) &&\n node.getAttribute('data-type') === 'taskItem'\n )\n },\n replacement(content, node) {\n const parentNode = node.parentNode as HTMLElement\n let listItemMarker = `${BULLET_LIST_MARKER} `\n\n if (parentNode?.nodeName === 'UL') {\n const checked = (node as HTMLLIElement).getAttribute('data-checked')\n listItemMarker = `${listItemMarker}${checked === 'true' ? '[x]' : '[ ]'} `\n }\n\n const newContent = content\n // Remove leading newlines\n .replace(/^\\n+/, '')\n // Replace trailing newlines with a single one\n .replace(/\\n+$/, '\\n')\n // Indent list items with 4 spaces\n .replace(/\\s*\\n/gm, '\\n ')\n\n return `${listItemMarker}${newContent.trim()}${\n node.nextSibling && !newContent.endsWith('\\n') ? '\\n' : ''\n }`\n },\n })\n }\n}\n\nexport { taskItem }\n"],"mappings":";;;;;;;;;;;;AAeA,SAAS,SAAS,UAAqC;CACnD,MAAM,OAAO,0BAA0B,SAAS,KAAK,SAAS;CAE9D,QAAQ,aAAuB;EAC3B,SAAS,QAAQ,SAAS,MAAM;GAC5B,OAAO,MAAM;IACT,OACI,KAAK,MAAM,QAAQ,IAAI,aAAa,CAAC,WAAW,KAAK,SAAS,CAAC,IAC/D,KAAK,aAAa,YAAY,KAAK;;GAG3C,YAAY,SAAS,MAAM;IACvB,MAAM,aAAa,KAAK;IACxB,IAAI,iBAAiB;IAErB,IAAI,YAAY,aAAa,MAAM;KAC/B,MAAM,UAAW,KAAuB,aAAa,eAAe;KACpE,iBAAiB,GAAG,iBAAiB,YAAY,SAAS,QAAQ,MAAM;;IAG5E,MAAM,aAAa,QAEd,QAAQ,QAAQ,GAAG,CAEnB,QAAQ,QAAQ,KAAK,CAErB,QAAQ,WAAW,SAAS;IAEjC,OAAO,GAAG,iBAAiB,WAAW,MAAM,GACxC,KAAK,eAAe,CAAC,WAAW,SAAS,KAAK,GAAG,OAAO;;GAGnE,CAAC"}
|
|
@@ -4,7 +4,7 @@ import { Editor, Range } from "@tiptap/core";
|
|
|
4
4
|
/**
|
|
5
5
|
* Check if a node of a specific type can be inserted at a specific position in the editor.
|
|
6
6
|
*
|
|
7
|
-
* @
|
|
7
|
+
* @returns True if the node can be inserted, false otherwise.
|
|
8
8
|
*/
|
|
9
9
|
declare function canInsertNodeAt({
|
|
10
10
|
editor,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Check if a node of a specific type can be inserted at a specific position in the editor.
|
|
4
4
|
*
|
|
5
|
-
* @
|
|
5
|
+
* @returns True if the node can be inserted, false otherwise.
|
|
6
6
|
*/
|
|
7
7
|
function canInsertNodeAt({ editor, nodeType, range }) {
|
|
8
8
|
return editor.can().insertContentAt(range, { type: nodeType });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"can-insert-node-at.js","names":[],"sources":["../../src/utilities/can-insert-node-at.ts"],"sourcesContent":["import type { Editor, Range } from '@tiptap/core'\n\n/**\n * Check if a node of a specific type can be inserted at a specific position in the editor.\n *\n * @
|
|
1
|
+
{"version":3,"file":"can-insert-node-at.js","names":[],"sources":["../../src/utilities/can-insert-node-at.ts"],"sourcesContent":["import type { Editor, Range } from '@tiptap/core'\n\n/**\n * Check if a node of a specific type can be inserted at a specific position in the editor.\n *\n * @returns True if the node can be inserted, false otherwise.\n */\nfunction canInsertNodeAt({\n editor,\n nodeType,\n range,\n}: {\n editor: Editor\n nodeType: string\n range: Range\n}) {\n return editor.can().insertContentAt(range, {\n type: nodeType,\n })\n}\n\nexport { canInsertNodeAt }\n"],"mappings":";;;;;;AAOA,SAAS,gBAAgB,EACrB,QACA,UACA,SAKD;CACC,OAAO,OAAO,KAAK,CAAC,gBAAgB,OAAO,EACvC,MAAM,UACT,CAAC"}
|
|
@@ -5,7 +5,7 @@ import { EditorState } from "@tiptap/pm/state";
|
|
|
5
5
|
/**
|
|
6
6
|
* Check if a suggestion can be inserted within the current editor selection.
|
|
7
7
|
*
|
|
8
|
-
* @
|
|
8
|
+
* @returns True if the suggestion can be inserted, false otherwise.
|
|
9
9
|
*/
|
|
10
10
|
declare function canInsertSuggestion({
|
|
11
11
|
editor,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Check if a suggestion can be inserted within the current editor selection.
|
|
4
4
|
*
|
|
5
|
-
* @
|
|
5
|
+
* @returns True if the suggestion can be inserted, false otherwise.
|
|
6
6
|
*/
|
|
7
7
|
function canInsertSuggestion({ editor, state }) {
|
|
8
8
|
const { selection } = state;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"can-insert-suggestion.js","names":[],"sources":["../../src/utilities/can-insert-suggestion.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport type { EditorState } from '@tiptap/pm/state'\n\n/**\n * Check if a suggestion can be inserted within the current editor selection.\n *\n * @
|
|
1
|
+
{"version":3,"file":"can-insert-suggestion.js","names":[],"sources":["../../src/utilities/can-insert-suggestion.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport type { EditorState } from '@tiptap/pm/state'\n\n/**\n * Check if a suggestion can be inserted within the current editor selection.\n *\n * @returns True if the suggestion can be inserted, false otherwise.\n */\nfunction canInsertSuggestion({ editor, state }: { editor: Editor; state: EditorState }) {\n const { selection } = state\n\n const isInsideCodeMark = editor.isActive('code')\n\n const isInsideCodeBlockNode = selection.$from.parent.type.name === 'codeBlock'\n\n const wordsBeforeSelection = (selection.$from.nodeBefore?.text ?? '').split(' ')\n const nodeBeforeSelection = selection.$from.parent.cut(\n selection.$from.parentOffset - wordsBeforeSelection.slice(-1)[0].length - 1,\n selection.$from.parentOffset - 1,\n ).content.firstChild\n\n const hasCodeMarkBefore = (nodeBeforeSelection?.marks ?? []).some(\n (mark) => mark.type.name === 'code',\n )\n\n const isComposingInlineCode = wordsBeforeSelection.some((word) => word.startsWith('`'))\n\n return (\n !isInsideCodeMark && !isInsideCodeBlockNode && !hasCodeMarkBefore && !isComposingInlineCode\n )\n}\n\nexport { canInsertSuggestion }\n"],"mappings":";;;;;;AAQA,SAAS,oBAAoB,EAAE,QAAQ,SAAiD;CACpF,MAAM,EAAE,cAAc;CAEtB,MAAM,mBAAmB,OAAO,SAAS,OAAO;CAEhD,MAAM,wBAAwB,UAAU,MAAM,OAAO,KAAK,SAAS;CAEnE,MAAM,wBAAwB,UAAU,MAAM,YAAY,QAAQ,IAAI,MAAM,IAAI;CAMhF,MAAM,qBALsB,UAAU,MAAM,OAAO,IAC/C,UAAU,MAAM,eAAe,qBAAqB,MAAM,GAAG,CAAC,GAAG,SAAS,GAC1E,UAAU,MAAM,eAAe,EAClC,CAAC,QAAQ,YAEsC,SAAS,EAAE,EAAE,MACxD,SAAS,KAAK,KAAK,SAAS,OAChC;CAED,MAAM,wBAAwB,qBAAqB,MAAM,SAAS,KAAK,WAAW,IAAI,CAAC;CAEvF,OACI,CAAC,oBAAoB,CAAC,yBAAyB,CAAC,qBAAqB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@doist/typist",
|
|
3
3
|
"description": "The mighty Tiptap-based rich-text editor React component that powers Doist products.",
|
|
4
|
-
"version": "10.0.
|
|
4
|
+
"version": "10.0.2",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://typist.doist.dev/",
|
|
7
7
|
"repository": {
|
|
@@ -39,22 +39,21 @@
|
|
|
39
39
|
"build:storybook": "storybook build",
|
|
40
40
|
"build:typist": "tsdown",
|
|
41
41
|
"check": "run-p check:* -c -l -t",
|
|
42
|
-
"check:
|
|
43
|
-
"check:
|
|
42
|
+
"check:format": "oxfmt --check",
|
|
43
|
+
"check:lint": "oxlint",
|
|
44
44
|
"check:types": "tsc --project tsconfig.json",
|
|
45
45
|
"clean": "run-s clean:*",
|
|
46
46
|
"clean:cache": "rimraf .cache/",
|
|
47
47
|
"clean:dist": "rimraf dist/",
|
|
48
48
|
"clean:storybook-static": "rimraf storybook-static/",
|
|
49
|
-
"fix": "run-
|
|
50
|
-
"fix:
|
|
51
|
-
"
|
|
49
|
+
"fix": "run-s fix:*",
|
|
50
|
+
"fix:format": "oxfmt",
|
|
51
|
+
"fix:lint": "oxlint --fix",
|
|
52
|
+
"prepare": "lefthook install",
|
|
52
53
|
"start": "run-s clean:* start:storybook",
|
|
53
54
|
"start:storybook": "storybook dev --port 6006 --ci",
|
|
54
55
|
"test": "vitest run",
|
|
55
|
-
"test:watch": "vitest watch"
|
|
56
|
-
"validate:pre-commit": "lint-staged",
|
|
57
|
-
"validate:pre-push": "run-s test"
|
|
56
|
+
"test:watch": "vitest watch"
|
|
58
57
|
},
|
|
59
58
|
"dependencies": {
|
|
60
59
|
"@tiptap/core": "2.27.2",
|
|
@@ -91,16 +90,14 @@
|
|
|
91
90
|
"prosemirror-codemark": "0.4.2"
|
|
92
91
|
},
|
|
93
92
|
"devDependencies": {
|
|
94
|
-
"@doist/
|
|
95
|
-
"@doist/prettier-config": "4.0.1",
|
|
96
|
-
"@doist/reactist": "30.0.1",
|
|
93
|
+
"@doist/reactist": "30.1.4",
|
|
97
94
|
"@mdx-js/react": "3.1.1",
|
|
98
95
|
"@semantic-release/changelog": "6.0.3",
|
|
99
96
|
"@semantic-release/exec": "7.1.0",
|
|
100
97
|
"@semantic-release/git": "10.0.1",
|
|
101
|
-
"@storybook/addon-a11y": "10.
|
|
102
|
-
"@storybook/addon-docs": "10.
|
|
103
|
-
"@storybook/react-vite": "10.
|
|
98
|
+
"@storybook/addon-a11y": "10.4.0",
|
|
99
|
+
"@storybook/addon-docs": "10.4.0",
|
|
100
|
+
"@storybook/react-vite": "10.4.0",
|
|
104
101
|
"@testing-library/dom": "10.4.1",
|
|
105
102
|
"@testing-library/jest-dom": "6.9.1",
|
|
106
103
|
"@testing-library/react": "16.3.2",
|
|
@@ -111,28 +108,18 @@
|
|
|
111
108
|
"@types/react-syntax-highlighter": "15.5.13",
|
|
112
109
|
"@types/turndown": "5.0.6",
|
|
113
110
|
"@types/unist": "3.0.3",
|
|
114
|
-
"@vitejs/plugin-react": "
|
|
111
|
+
"@vitejs/plugin-react": "6.0.2",
|
|
115
112
|
"boring-avatars": "2.0.4",
|
|
116
113
|
"classnames": "2.5.1",
|
|
117
|
-
"conventional-changelog-conventionalcommits": "9.3.
|
|
114
|
+
"conventional-changelog-conventionalcommits": "9.3.1",
|
|
118
115
|
"emoji-regex": "10.6.0",
|
|
119
|
-
"eslint": "8.57.1",
|
|
120
|
-
"eslint-formatter-codeframe": "7.32.2",
|
|
121
|
-
"eslint-import-resolver-typescript": "3.10.1",
|
|
122
|
-
"eslint-plugin-simple-import-sort": "12.1.1",
|
|
123
|
-
"eslint-plugin-storybook": "10.2.10",
|
|
124
|
-
"eslint-plugin-unicorn": "56.0.1",
|
|
125
|
-
"eslint-plugin-vitest": "0.4.1",
|
|
126
|
-
"eslint-plugin-vitest-globals": "1.5.0",
|
|
127
116
|
"github-markdown-css": "5.9.0",
|
|
128
|
-
"
|
|
129
|
-
"
|
|
130
|
-
"
|
|
131
|
-
"
|
|
132
|
-
"
|
|
133
|
-
"
|
|
134
|
-
"prettier": "3.8.1",
|
|
135
|
-
"publint": "0.3.18",
|
|
117
|
+
"jsdom": "29.1.1",
|
|
118
|
+
"lefthook": "2.1.6",
|
|
119
|
+
"npm-run-all-next": "1.4.2",
|
|
120
|
+
"oxfmt": "0.50.0",
|
|
121
|
+
"oxlint": "1.65.0",
|
|
122
|
+
"publint": "0.3.21",
|
|
136
123
|
"react": "18.3.1",
|
|
137
124
|
"react-dom": "18.3.1",
|
|
138
125
|
"react-icons": "5.6.0",
|
|
@@ -143,14 +130,14 @@
|
|
|
143
130
|
"remark-gfm": "4.0.1",
|
|
144
131
|
"rimraf": "6.1.3",
|
|
145
132
|
"semantic-release": "25.0.3",
|
|
146
|
-
"storybook": "10.
|
|
133
|
+
"storybook": "10.4.0",
|
|
147
134
|
"storybook-css-modules": "1.0.8",
|
|
148
135
|
"tippy.js": "6.3.7",
|
|
149
|
-
"tsdown": "0.
|
|
150
|
-
"type-fest": "5.
|
|
151
|
-
"typescript": "
|
|
136
|
+
"tsdown": "0.22.0",
|
|
137
|
+
"type-fest": "5.6.0",
|
|
138
|
+
"typescript": "6.0.3",
|
|
152
139
|
"typescript-plugin-css-modules": "5.2.0",
|
|
153
|
-
"vitest": "4.
|
|
140
|
+
"vitest": "4.1.6"
|
|
154
141
|
},
|
|
155
142
|
"peerDependencies": {
|
|
156
143
|
"emoji-regex": "^10.2.1",
|