@doist/typist 1.2.8 → 1.2.9

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.
Files changed (47) hide show
  1. package/CHANGELOG.md +8 -2
  2. package/dist/helpers/unified.d.ts +11 -0
  3. package/dist/helpers/unified.d.ts.map +1 -0
  4. package/dist/helpers/unified.js +12 -0
  5. package/dist/serializers/html/html.d.ts +3 -10
  6. package/dist/serializers/html/html.d.ts.map +1 -1
  7. package/dist/serializers/html/html.js +66 -48
  8. package/dist/serializers/html/plugins/rehype-code-block.d.ts +10 -0
  9. package/dist/serializers/html/plugins/rehype-code-block.d.ts.map +1 -0
  10. package/dist/serializers/html/plugins/rehype-code-block.js +22 -0
  11. package/dist/serializers/html/plugins/rehype-image.d.ts +11 -0
  12. package/dist/serializers/html/plugins/rehype-image.d.ts.map +1 -0
  13. package/dist/serializers/html/plugins/rehype-image.js +34 -0
  14. package/dist/serializers/html/plugins/rehype-suggestions.d.ts +10 -0
  15. package/dist/serializers/html/plugins/rehype-suggestions.d.ts.map +1 -0
  16. package/dist/serializers/html/plugins/rehype-suggestions.js +36 -0
  17. package/dist/serializers/html/plugins/rehype-task-list.d.ts +7 -0
  18. package/dist/serializers/html/plugins/rehype-task-list.d.ts.map +1 -0
  19. package/dist/serializers/html/plugins/rehype-task-list.js +37 -0
  20. package/dist/serializers/html/plugins/remark-disable-constructs.d.ts +11 -0
  21. package/dist/serializers/html/plugins/remark-disable-constructs.d.ts.map +1 -0
  22. package/dist/serializers/html/plugins/remark-disable-constructs.js +48 -0
  23. package/dist/serializers/html/plugins/remark-strikethrough.d.ts +18 -0
  24. package/dist/serializers/html/plugins/remark-strikethrough.d.ts.map +1 -0
  25. package/dist/serializers/html/plugins/remark-strikethrough.js +26 -0
  26. package/package.json +26 -14
  27. package/dist/serializers/html/extensions/checkbox.d.ts +0 -8
  28. package/dist/serializers/html/extensions/checkbox.d.ts.map +0 -1
  29. package/dist/serializers/html/extensions/checkbox.js +0 -12
  30. package/dist/serializers/html/extensions/code.d.ts +0 -9
  31. package/dist/serializers/html/extensions/code.d.ts.map +0 -1
  32. package/dist/serializers/html/extensions/code.js +0 -20
  33. package/dist/serializers/html/extensions/disabled.d.ts +0 -13
  34. package/dist/serializers/html/extensions/disabled.d.ts.map +0 -1
  35. package/dist/serializers/html/extensions/disabled.js +0 -125
  36. package/dist/serializers/html/extensions/html.d.ts +0 -10
  37. package/dist/serializers/html/extensions/html.d.ts.map +0 -1
  38. package/dist/serializers/html/extensions/html.js +0 -15
  39. package/dist/serializers/html/extensions/link.d.ts +0 -11
  40. package/dist/serializers/html/extensions/link.d.ts.map +0 -1
  41. package/dist/serializers/html/extensions/link.js +0 -25
  42. package/dist/serializers/html/extensions/paragraph.d.ts +0 -12
  43. package/dist/serializers/html/extensions/paragraph.d.ts.map +0 -1
  44. package/dist/serializers/html/extensions/paragraph.js +0 -51
  45. package/dist/serializers/html/extensions/task-list.d.ts +0 -9
  46. package/dist/serializers/html/extensions/task-list.d.ts.map +0 -1
  47. package/dist/serializers/html/extensions/task-list.js +0 -31
package/CHANGELOG.md CHANGED
@@ -1,8 +1,14 @@
1
+ ## [1.2.9](https://github.com/Doist/typist/compare/v1.2.8...v1.2.9) (2023-06-01)
2
+
3
+ ### Bug Fixes
4
+
5
+ - **html-serializer:** Don't share instances between editors ([#275](https://github.com/Doist/typist/issues/275)) ([3aba8c7](https://github.com/Doist/typist/commit/3aba8c7b00b44e22a14ea2bc6d6dcad0f4f5ed80))
6
+
1
7
  ## [1.2.8](https://github.com/Doist/typist/compare/v1.2.7...v1.2.8) (2023-05-30)
2
8
 
3
- ### Reverts
9
+ ### Notes
4
10
 
5
- - Revert "ci: Add support to publish experimental releases" (#279) ([dc57964](https://github.com/Doist/typist/commit/dc57964c08f34606fa5070898f0415b4b8340190)), closes [#279](https://github.com/Doist/typist/issues/279) [#278](https://github.com/Doist/typist/issues/278)
11
+ - This version was published by mistake because we were unware that a `revert:` commit would publish a new version. There's no difference between `v1.2.7` and `v1.2.8`, the distributed code is exactly the same.
6
12
 
7
13
  ## [1.2.7](https://github.com/Doist/typist/compare/v1.2.6...v1.2.7) (2023-05-22)
8
14
 
@@ -0,0 +1,11 @@
1
+ import type { Node, Text } from 'hast';
2
+ /**
3
+ * Check if a given node is a unist text node.
4
+ *
5
+ * @param node The node to check.
6
+ *
7
+ * @returns `true` if the node is a unist text node, `false` otherwise.
8
+ */
9
+ declare function isTextNode(node: Node): node is Text;
10
+ export { isTextNode };
11
+ //# sourceMappingURL=unified.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unified.d.ts","sourceRoot":"","sources":["../../src/helpers/unified.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAEtC;;;;;;GAMG;AACH,iBAAS,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,IAAI,IAAI,CAE5C;AAED,OAAO,EAAE,UAAU,EAAE,CAAA"}
@@ -0,0 +1,12 @@
1
+ import { is } from 'unist-util-is';
2
+ /**
3
+ * Check if a given node is a unist text node.
4
+ *
5
+ * @param node The node to check.
6
+ *
7
+ * @returns `true` if the node is a unist text node, `false` otherwise.
8
+ */
9
+ function isTextNode(node) {
10
+ return is(node, { type: 'text' });
11
+ }
12
+ export { isTextNode };
@@ -1,4 +1,3 @@
1
- import { marked } from 'marked';
2
1
  import type { Schema } from 'prosemirror-model';
3
2
  /**
4
3
  * The return type for the `createHTMLSerializer` function.
@@ -14,15 +13,9 @@ type HTMLSerializerReturnType = {
14
13
  serialize: (markdown: string) => string;
15
14
  };
16
15
  /**
17
- * Sensible default options to initialize the Marked parser with.
18
- *
19
- * @see https://marked.js.org/using_advanced#options
20
- */
21
- declare const INITIAL_MARKED_OPTIONS: marked.MarkedOptions;
22
- /**
23
- * Create a Markdown to HTML serializer with the Marked library for a rich-text editor, or use a
16
+ * Create a Markdown to HTML serializer with the unified ecosystem for a rich-text editor, or use a
24
17
  * custom serializer for a plain-text editor. The editor schema is used to detect which nodes and
25
- * marks are available in the editor, and only parses the input with the minimal required rules.
18
+ * marks are available in the editor, and only parses the input with the minimal required plugins.
26
19
  *
27
20
  * @param schema The editor schema to be used for nodes and marks detection.
28
21
  *
@@ -37,6 +30,6 @@ declare function createHTMLSerializer(schema: Schema): HTMLSerializerReturnType;
37
30
  * @returns The HTML serializer instance for the given editor schema.
38
31
  */
39
32
  declare function getHTMLSerializerInstance(schema: Schema): HTMLSerializerReturnType;
40
- export { createHTMLSerializer, getHTMLSerializerInstance, INITIAL_MARKED_OPTIONS };
33
+ export { createHTMLSerializer, getHTMLSerializerInstance };
41
34
  export type { HTMLSerializerReturnType };
42
35
  //# sourceMappingURL=html.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../../src/serializers/html/html.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAc/B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAE/C;;GAEG;AACH,KAAK,wBAAwB,GAAG;IAC5B;;;;;;OAMG;IACH,SAAS,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAA;CAC1C,CAAA;AASD;;;;GAIG;AACH,QAAA,MAAM,sBAAsB,EAAE,MAAM,CAAC,aAMpC,CAAA;AAmCD;;;;;;;;GAQG;AACH,iBAAS,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,wBAAwB,CAiDtE;AAOD;;;;;;GAMG;AACH,iBAAS,yBAAyB,CAAC,MAAM,EAAE,MAAM,4BAQhD;AAED,OAAO,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,sBAAsB,EAAE,CAAA;AAElF,YAAY,EAAE,wBAAwB,EAAE,CAAA"}
1
+ {"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../../src/serializers/html/html.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAE/C;;GAEG;AACH,KAAK,wBAAwB,GAAG;IAC5B;;;;;;OAMG;IACH,SAAS,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAA;CAC1C,CAAA;AA0CD;;;;;;;;GAQG;AACH,iBAAS,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,wBAAwB,CA2EtE;AAOD;;;;;;GAMG;AACH,iBAAS,yBAAyB,CAAC,MAAM,EAAE,MAAM,4BAQhD;AAED,OAAO,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,CAAA;AAE1D,YAAY,EAAE,wBAAwB,EAAE,CAAA"}
@@ -1,27 +1,17 @@
1
1
  import { escape, kebabCase } from 'lodash-es';
2
- import { marked } from 'marked';
3
- import { REGEX_LINE_BREAKS } from '../../constants/regular-expressions';
2
+ import rehypeMinifyWhitespace from 'rehype-minify-whitespace';
3
+ import rehypeStringify from 'rehype-stringify';
4
+ import remarkBreaks from 'remark-breaks';
5
+ import remarkParse from 'remark-parse';
6
+ import remarkRehype from 'remark-rehype';
7
+ import { unified } from 'unified';
4
8
  import { computeSchemaId, isPlainTextDocument } from '../../helpers/schema';
5
- import { buildSuggestionSchemaPartialRegex } from '../../helpers/serializer';
6
- import { checkbox } from './extensions/checkbox';
7
- import { code } from './extensions/code';
8
- import { disabled } from './extensions/disabled';
9
- import { html } from './extensions/html';
10
- import { link } from './extensions/link';
11
- import { paragraph } from './extensions/paragraph';
12
- import { taskList } from './extensions/task-list';
13
- /**
14
- * Sensible default options to initialize the Marked parser with.
15
- *
16
- * @see https://marked.js.org/using_advanced#options
17
- */
18
- const INITIAL_MARKED_OPTIONS = {
19
- ...marked.getDefaults(),
20
- breaks: true,
21
- gfm: true,
22
- headerIds: false,
23
- silent: true,
24
- };
9
+ import { rehypeCodeBlock } from './plugins/rehype-code-block';
10
+ import { rehypeImage } from './plugins/rehype-image';
11
+ import { rehypeSuggestions } from './plugins/rehype-suggestions';
12
+ import { rehypeTaskList } from './plugins/rehype-task-list';
13
+ import { remarkDisableConstructs } from './plugins/remark-disable-constructs';
14
+ import { remarkStrikethrough } from './plugins/remark-strikethrough';
25
15
  /**
26
16
  * Create a custom Markdown to HTML serializer for plain-text editors only.
27
17
  *
@@ -49,9 +39,9 @@ function createHTMLSerializerForPlainTextEditor(schema) {
49
39
  };
50
40
  }
51
41
  /**
52
- * Create a Markdown to HTML serializer with the Marked library for a rich-text editor, or use a
42
+ * Create a Markdown to HTML serializer with the unified ecosystem for a rich-text editor, or use a
53
43
  * custom serializer for a plain-text editor. The editor schema is used to detect which nodes and
54
- * marks are available in the editor, and only parses the input with the minimal required rules.
44
+ * marks are available in the editor, and only parses the input with the minimal required plugins.
55
45
  *
56
46
  * @param schema The editor schema to be used for nodes and marks detection.
57
47
  *
@@ -62,35 +52,63 @@ function createHTMLSerializer(schema) {
62
52
  if (isPlainTextDocument(schema)) {
63
53
  return createHTMLSerializerForPlainTextEditor(schema);
64
54
  }
65
- // Reset Marked instance to the initial options
66
- marked.setOptions(INITIAL_MARKED_OPTIONS);
67
- // Disable built-in rules that are not supported by the schema
68
- marked.use(disabled(schema));
69
- // Overwrite some built-in rules for handling of special behaviours
70
- // (see documentation for each extension for more details)
71
- marked.use(checkbox, html, paragraph(schema.nodes.image));
72
- // Overwrite the built-in `code` rule if the corresponding node exists in the schema
55
+ // Initialize a unified processor with a remark plugin for parsing Markdown
56
+ const unifiedProcessor = unified().use(remarkParse);
57
+ // Configure the unified processor to use a custom plugin to disable constructs based on the
58
+ // supported extensions that are enabled in the editor schema
59
+ unifiedProcessor.use(remarkDisableConstructs, schema);
60
+ // Configure the unified processor to use a third-party plugin to turn soft line endings into
61
+ // hard breaks (i.e. `<br>`), which will display user content closer to how it was authored
62
+ // (although not CommonMark compliant, this resembles the behaviour we always supported)
63
+ if (schema.nodes.hardBreak) {
64
+ unifiedProcessor.use(remarkBreaks);
65
+ }
66
+ // Configure the unified processor to use a custom plugin to add support for the strikethrough
67
+ // extension from the GitHub Flavored Markdown (GFM) specification
68
+ if (schema.marks.strike) {
69
+ unifiedProcessor.use(remarkStrikethrough);
70
+ }
71
+ // Configure the unified processor with an official plugin to convert Markdown into HTML to
72
+ // support rehype (a tool that transforms HTML with plugins), followed by another official
73
+ // plugin to minify whitespace between tags (prevents line feeds from appearing as blank)
74
+ unifiedProcessor
75
+ .use(remarkRehype, {
76
+ // Persist raw HTML (disables support for custom elements/tags)
77
+ // ref: https://github.com/Doist/Issues/issues/5689
78
+ allowDangerousHtml: true,
79
+ })
80
+ // This must come before all rehype plugins that transform the HTML output
81
+ .use(rehypeMinifyWhitespace, {
82
+ // Preserve line breaks when collapsing whitespace (e.g., line feeds)
83
+ newlines: true,
84
+ });
85
+ // Configure the unified processor with a custom plugin to remove the trailing newline from code
86
+ // blocks (i.e. the newline between the last code line and `</code></pre>`)
73
87
  if (schema.nodes.codeBlock) {
74
- marked.use(code);
88
+ unifiedProcessor.use(rehypeCodeBlock);
75
89
  }
76
- // Add a rule for a task list if the corresponding nodes exists in the schema
77
- if (schema.nodes.taskList && schema.nodes.taskItem) {
78
- marked.use(taskList);
90
+ // Configure the unified processor with a custom plugin to remove the wrapping paragraph from
91
+ // images and to remove all inline images based on inline images support in the editor schema
92
+ if (schema.nodes.paragraph && schema.nodes.image) {
93
+ unifiedProcessor.use(rehypeImage, schema);
79
94
  }
80
- // Build a regular expression with all the available suggestion nodes from the schema
81
- const suggestionSchemaPartialRegex = buildSuggestionSchemaPartialRegex(schema);
82
- // Overwrite the built-in link rule if any suggestion node exists in the schema
83
- if (suggestionSchemaPartialRegex) {
84
- marked.use(link(new RegExp(`^${suggestionSchemaPartialRegex}`)));
95
+ // Configure the unified processor with a custom plugin to add support Tiptap task lists
96
+ if (schema.nodes.taskList && schema.nodes.taskItem) {
97
+ unifiedProcessor.use(rehypeTaskList);
85
98
  }
99
+ // Configure the unified processor with a custom plugin to add support for suggestions nodes
100
+ unifiedProcessor.use(rehypeSuggestions, schema);
101
+ // Configure the unified processor with an official plugin that defines how to take a syntax
102
+ // tree as input and turn it into serialized HTML
103
+ unifiedProcessor.use(rehypeStringify, {
104
+ entities: {
105
+ // Compatibility with the previous implementation in Marked
106
+ useNamedReferences: true,
107
+ },
108
+ });
86
109
  return {
87
110
  serialize(markdown) {
88
- return (marked
89
- .parse(markdown)
90
- // Removes line breaks after HTML tags from the HTML output with a specially
91
- // crafted RegExp (this is needed to prevent the editor from converting newline
92
- // control characters to blank paragraphs).
93
- .replace(new RegExp(`>${REGEX_LINE_BREAKS.source}`, REGEX_LINE_BREAKS.flags), '>'));
111
+ return unifiedProcessor.processSync(markdown).toString();
94
112
  },
95
113
  };
96
114
  }
@@ -112,4 +130,4 @@ function getHTMLSerializerInstance(schema) {
112
130
  }
113
131
  return htmlSerializerInstanceById[id];
114
132
  }
115
- export { createHTMLSerializer, getHTMLSerializerInstance, INITIAL_MARKED_OPTIONS };
133
+ export { createHTMLSerializer, getHTMLSerializerInstance };
@@ -0,0 +1,10 @@
1
+ import type { Transformer } from 'unified';
2
+ /**
3
+ * A rehype plugin to remove the trailing newline from code blocks (i.e. the newline between the
4
+ * last code line and `</code></pre>`). Although that newline is part of the CommonMark
5
+ * specification, this custom plugin is required to prevent Tiptap from rendering a blank line at
6
+ * the end of the code block.
7
+ */
8
+ declare function rehypeCodeBlock(): Transformer;
9
+ export { rehypeCodeBlock };
10
+ //# sourceMappingURL=rehype-code-block.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rehype-code-block.d.ts","sourceRoot":"","sources":["../../../../src/serializers/html/plugins/rehype-code-block.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAE1C;;;;;GAKG;AACH,iBAAS,eAAe,IAAI,WAAW,CAiBtC;AAED,OAAO,EAAE,eAAe,EAAE,CAAA"}
@@ -0,0 +1,22 @@
1
+ import { isElement } from 'hast-util-is-element';
2
+ import { visit } from 'unist-util-visit';
3
+ import { isTextNode } from '../../../helpers/unified';
4
+ /**
5
+ * A rehype plugin to remove the trailing newline from code blocks (i.e. the newline between the
6
+ * last code line and `</code></pre>`). Although that newline is part of the CommonMark
7
+ * specification, this custom plugin is required to prevent Tiptap from rendering a blank line at
8
+ * the end of the code block.
9
+ */
10
+ function rehypeCodeBlock() {
11
+ return (...[tree]) => {
12
+ visit(tree, 'element', (node) => {
13
+ if (isElement(node, 'pre') &&
14
+ isElement(node.children[0], 'code') &&
15
+ isTextNode(node.children[0].children[0])) {
16
+ node.children[0].children[0].value = node.children[0].children[0].value.replace(/\n$/, '');
17
+ }
18
+ });
19
+ return tree;
20
+ };
21
+ }
22
+ export { rehypeCodeBlock };
@@ -0,0 +1,11 @@
1
+ import type { Schema } from 'prosemirror-model';
2
+ import type { Transformer } from 'unified';
3
+ /**
4
+ * A rehype plugin to remove the wrapping paragraph from images and to remove all inline images if
5
+ * the editor was configured without inline image support (Tiptap default).
6
+ *
7
+ * @param schema The editor schema to be used for nodes and marks detection.
8
+ */
9
+ declare function rehypeImage(schema: Schema): Transformer;
10
+ export { rehypeImage };
11
+ //# sourceMappingURL=rehype-image.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rehype-image.d.ts","sourceRoot":"","sources":["../../../../src/serializers/html/plugins/rehype-image.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAE1C;;;;;GAKG;AACH,iBAAS,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,CA0BhD;AAED,OAAO,EAAE,WAAW,EAAE,CAAA"}
@@ -0,0 +1,34 @@
1
+ import { isElement } from 'hast-util-is-element';
2
+ import { remove } from 'unist-util-remove';
3
+ import { visit } from 'unist-util-visit';
4
+ /**
5
+ * A rehype plugin to remove the wrapping paragraph from images and to remove all inline images if
6
+ * the editor was configured without inline image support (Tiptap default).
7
+ *
8
+ * @param schema The editor schema to be used for nodes and marks detection.
9
+ */
10
+ function rehypeImage(schema) {
11
+ const allowInlineImages = schema.nodes.image ? schema.nodes.image.spec.inline : false;
12
+ // Return the tree as-is if the editor does not support inline images
13
+ if (allowInlineImages) {
14
+ return (tree) => tree;
15
+ }
16
+ return (...[tree]) => {
17
+ visit(tree, 'element', (node, index, parent) => {
18
+ if (isElement(node, 'p')) {
19
+ const areAllChildrenImages = node.children.every((c) => isElement(c, 'img'));
20
+ // Replace the paragraph with the image children if all children are images, or
21
+ // remove all images from the paragraph if it contains non-image children since the
22
+ // editor does not support inline images
23
+ if (areAllChildrenImages) {
24
+ parent.children.splice(index, 1, ...node.children);
25
+ }
26
+ else {
27
+ remove(node, (n) => isElement(n, 'img'));
28
+ }
29
+ }
30
+ });
31
+ return tree;
32
+ };
33
+ }
34
+ export { rehypeImage };
@@ -0,0 +1,10 @@
1
+ import type { Schema } from 'prosemirror-model';
2
+ import type { Transformer } from 'unified';
3
+ /**
4
+ * A rehype plugin to add support for suggestions nodes (e.g., `@username` or `#channel).
5
+ *
6
+ * @param schema The editor schema to be used for suggestion nodes detection.
7
+ */
8
+ declare function rehypeSuggestions(schema: Schema): Transformer;
9
+ export { rehypeSuggestions };
10
+ //# sourceMappingURL=rehype-suggestions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rehype-suggestions.d.ts","sourceRoot":"","sources":["../../../../src/serializers/html/plugins/rehype-suggestions.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAE1C;;;;GAIG;AACH,iBAAS,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,CA+BtD;AAED,OAAO,EAAE,iBAAiB,EAAE,CAAA"}
@@ -0,0 +1,36 @@
1
+ import { isElement } from 'hast-util-is-element';
2
+ import { visit } from 'unist-util-visit';
3
+ import { buildSuggestionSchemaPartialRegex } from '../../../helpers/serializer';
4
+ import { isTextNode } from '../../../helpers/unified';
5
+ /**
6
+ * A rehype plugin to add support for suggestions nodes (e.g., `@username` or `#channel).
7
+ *
8
+ * @param schema The editor schema to be used for suggestion nodes detection.
9
+ */
10
+ function rehypeSuggestions(schema) {
11
+ const suggestionSchemaPartialRegex = buildSuggestionSchemaPartialRegex(schema);
12
+ // Return the tree as-is if the editor does not support suggestions
13
+ if (!suggestionSchemaPartialRegex) {
14
+ return (tree) => tree;
15
+ }
16
+ return (...[tree]) => {
17
+ const suggestionSchemaRegex = new RegExp(`^${suggestionSchemaPartialRegex}`);
18
+ visit(tree, 'element', (node) => {
19
+ if (isElement(node, 'a') && suggestionSchemaRegex.test(String(node.properties?.href))) {
20
+ const [, schema, id] = /^([a-z-]+):\/\/(\S+)$/i.exec(String(node.properties?.href)) || [];
21
+ // Replace the link element with a span containing the suggestion attributes
22
+ if (schema && id && isTextNode(node.children[0])) {
23
+ node.tagName = 'span';
24
+ node.properties = {
25
+ [`data-${schema}`]: '',
26
+ 'data-id': id,
27
+ 'data-label': node.children[0].value,
28
+ };
29
+ node.children = [];
30
+ }
31
+ }
32
+ });
33
+ return tree;
34
+ };
35
+ }
36
+ export { rehypeSuggestions };
@@ -0,0 +1,7 @@
1
+ import type { Transformer } from 'unified';
2
+ /**
3
+ * A rehype plugin to add support for Tiptap task lists (i.e., `* [ ] Task`).
4
+ */
5
+ declare function rehypeTaskList(): Transformer;
6
+ export { rehypeTaskList };
7
+ //# sourceMappingURL=rehype-task-list.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rehype-task-list.d.ts","sourceRoot":"","sources":["../../../../src/serializers/html/plugins/rehype-task-list.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAE1C;;GAEG;AACH,iBAAS,cAAc,IAAI,WAAW,CAoCrC;AAED,OAAO,EAAE,cAAc,EAAE,CAAA"}
@@ -0,0 +1,37 @@
1
+ import { isElement } from 'hast-util-is-element';
2
+ import { visit } from 'unist-util-visit';
3
+ import { isTextNode } from '../../../helpers/unified';
4
+ /**
5
+ * A rehype plugin to add support for Tiptap task lists (i.e., `* [ ] Task`).
6
+ */
7
+ function rehypeTaskList() {
8
+ return (...[tree]) => {
9
+ visit(tree, 'element', (node) => {
10
+ if (isElement(node, 'ul')) {
11
+ const areAllChildrenTaskItems = node.children.every((c) => isElement(c, 'li') &&
12
+ isTextNode(c.children[0]) &&
13
+ /^\[[ x]\] /i.test(c.children[0].value));
14
+ // Add the required attributes to the list and list items if all children are tasks,
15
+ // removing the `[ ] ` or `[x] ` at the beginning of the task item text
16
+ if (areAllChildrenTaskItems) {
17
+ node.properties = {
18
+ ...node.properties,
19
+ 'data-type': 'taskList',
20
+ };
21
+ node.children.forEach((c) => {
22
+ if (isElement(c, 'li') && isTextNode(c.children[0])) {
23
+ c.properties = {
24
+ ...c.properties,
25
+ 'data-type': 'taskItem',
26
+ 'data-checked': String(/^\[x\]/i.test(c.children[0].value)),
27
+ };
28
+ c.children[0].value = c.children[0].value.substring(4).trim();
29
+ }
30
+ });
31
+ }
32
+ }
33
+ });
34
+ return tree;
35
+ };
36
+ }
37
+ export { rehypeTaskList };
@@ -0,0 +1,11 @@
1
+ import type { Schema } from 'prosemirror-model';
2
+ import type { Processor } from 'unified';
3
+ /**
4
+ * A remark plugin to disable multiple language constructs based on the availability of marks and/or
5
+ * nodes in the given editor schema.
6
+ *
7
+ * @param schema The editor schema to be used for nodes and marks detection.
8
+ */
9
+ declare function remarkDisableConstructs(this: Processor, schema: Schema): void;
10
+ export { remarkDisableConstructs };
11
+ //# sourceMappingURL=remark-disable-constructs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remark-disable-constructs.d.ts","sourceRoot":"","sources":["../../../../src/serializers/html/plugins/remark-disable-constructs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAExC;;;;;GAKG;AACH,iBAAS,uBAAuB,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,QAqD/D;AAED,OAAO,EAAE,uBAAuB,EAAE,CAAA"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * A remark plugin to disable multiple language constructs based on the availability of marks and/or
3
+ * nodes in the given editor schema.
4
+ *
5
+ * @param schema The editor schema to be used for nodes and marks detection.
6
+ */
7
+ function remarkDisableConstructs(schema) {
8
+ const data = this.data();
9
+ function add(field, value) {
10
+ const list = (data[field] ? data[field] : (data[field] = []));
11
+ list.push(value);
12
+ }
13
+ const disabledConstructs = [];
14
+ if (!schema.nodes.blockquote) {
15
+ disabledConstructs.push('blockQuote');
16
+ }
17
+ if (!schema.marks.bold || !schema.marks.italic) {
18
+ disabledConstructs.push('attention');
19
+ }
20
+ if (!schema.nodes.bulletList || !schema.nodes.orderedList) {
21
+ disabledConstructs.push('list');
22
+ }
23
+ if (!schema.marks.code) {
24
+ disabledConstructs.push('codeText');
25
+ }
26
+ if (!schema.nodes.codeBlock) {
27
+ disabledConstructs.push('codeFenced', 'codeIndented');
28
+ }
29
+ if (!schema.nodes.heading) {
30
+ disabledConstructs.push('headingAtx');
31
+ }
32
+ if (!schema.nodes.horizontalRule) {
33
+ disabledConstructs.push('thematicBreak');
34
+ }
35
+ if (!schema.nodes.image) {
36
+ disabledConstructs.push('labelStartImage');
37
+ }
38
+ if (!schema.marks.link) {
39
+ disabledConstructs.push('labelStartLink');
40
+ }
41
+ // https://github.com/micromark/micromark#case-turn-off-constructs
42
+ add('micromarkExtensions', {
43
+ disable: {
44
+ null: disabledConstructs,
45
+ },
46
+ });
47
+ }
48
+ export { remarkDisableConstructs };
@@ -0,0 +1,18 @@
1
+ import type { Options } from 'micromark-extension-gfm-strikethrough';
2
+ import type { Processor } from 'unified';
3
+ /**
4
+ * A remark plugin to add support for the strikethrough extension from the GitHub Flavored Markdown
5
+ * (GFM) specification.
6
+ *
7
+ * This is an standalone plugin which makes use of both the `mdast-util-gfm-strikethrough` and
8
+ * `micromark-extension-gfm-strikethrough` packages, and the implementation is inspired by the
9
+ * third-party `remark-gfm` plugin.
10
+ *
11
+ * The reason why we don't use `remark-gfm` directly is because we don't want to support all other
12
+ * GFM features (autolink literals, footnotes, tables, and tasklists).
13
+ *
14
+ * @param options Configuration options for the plugin.
15
+ */
16
+ declare function remarkStrikethrough(this: Processor, options?: Options): void;
17
+ export { remarkStrikethrough };
18
+ //# sourceMappingURL=remark-strikethrough.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remark-strikethrough.d.ts","sourceRoot":"","sources":["../../../../src/serializers/html/plugins/remark-strikethrough.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uCAAuC,CAAA;AACpE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAExC;;;;;;;;;;;;GAYG;AACH,iBAAS,mBAAmB,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,GAAE,OAAY,QAYlE;AAED,OAAO,EAAE,mBAAmB,EAAE,CAAA"}
@@ -0,0 +1,26 @@
1
+ import { gfmStrikethroughFromMarkdown, gfmStrikethroughToMarkdown, } from 'mdast-util-gfm-strikethrough';
2
+ import { gfmStrikethrough } from 'micromark-extension-gfm-strikethrough';
3
+ /**
4
+ * A remark plugin to add support for the strikethrough extension from the GitHub Flavored Markdown
5
+ * (GFM) specification.
6
+ *
7
+ * This is an standalone plugin which makes use of both the `mdast-util-gfm-strikethrough` and
8
+ * `micromark-extension-gfm-strikethrough` packages, and the implementation is inspired by the
9
+ * third-party `remark-gfm` plugin.
10
+ *
11
+ * The reason why we don't use `remark-gfm` directly is because we don't want to support all other
12
+ * GFM features (autolink literals, footnotes, tables, and tasklists).
13
+ *
14
+ * @param options Configuration options for the plugin.
15
+ */
16
+ function remarkStrikethrough(options = {}) {
17
+ const data = this.data();
18
+ function add(field, value) {
19
+ const list = (data[field] ? data[field] : (data[field] = []));
20
+ list.push(value);
21
+ }
22
+ add('micromarkExtensions', gfmStrikethrough(options));
23
+ add('fromMarkdownExtensions', gfmStrikethroughFromMarkdown);
24
+ add('toMarkdownExtensions', gfmStrikethroughToMarkdown);
25
+ }
26
+ export { remarkStrikethrough };
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": "1.2.8",
4
+ "version": "1.2.9",
5
5
  "license": "MIT",
6
6
  "homepage": "https://typist.doist.dev/",
7
7
  "repository": "https://github.com/Doist/typist",
@@ -86,21 +86,21 @@
86
86
  "@semantic-release/changelog": "6.0.3",
87
87
  "@semantic-release/exec": "6.0.3",
88
88
  "@semantic-release/git": "10.0.1",
89
- "@storybook/addon-a11y": "7.0.17",
90
- "@storybook/addon-essentials": "7.0.17",
91
- "@storybook/addons": "7.0.17",
89
+ "@storybook/addon-a11y": "7.0.18",
90
+ "@storybook/addon-essentials": "7.0.18",
91
+ "@storybook/addons": "7.0.18",
92
92
  "@storybook/csf": "0.1.0",
93
93
  "@storybook/mdx2-csf": "1.1.0",
94
- "@storybook/react": "7.0.17",
95
- "@storybook/react-vite": "7.0.17",
94
+ "@storybook/react": "7.0.18",
95
+ "@storybook/react-vite": "7.0.18",
96
96
  "@testing-library/dom": "9.3.0",
97
97
  "@testing-library/jest-dom": "5.16.5",
98
98
  "@testing-library/react": "14.0.0",
99
+ "@types/hast": "2.3.4",
99
100
  "@types/lodash-es": "4.17.7",
100
- "@types/marked": "4.3.1",
101
101
  "@types/react": "18.2.7",
102
102
  "@types/react-dom": "18.2.4",
103
- "@types/react-syntax-highlighter": "15.5.6",
103
+ "@types/react-syntax-highlighter": "15.5.7",
104
104
  "@types/turndown": "5.0.1",
105
105
  "boring-avatars": "1.7.0",
106
106
  "classnames": "2.3.2",
@@ -112,7 +112,7 @@
112
112
  "eslint-plugin-simple-import-sort": "10.0.0",
113
113
  "eslint-plugin-storybook": "0.6.12",
114
114
  "eslint-plugin-unicorn": "47.0.0",
115
- "eslint-plugin-vitest": "0.2.3",
115
+ "eslint-plugin-vitest": "0.2.5",
116
116
  "eslint-plugin-vitest-globals": "1.3.1",
117
117
  "github-markdown-css": "5.2.0",
118
118
  "husky": "8.0.3",
@@ -127,11 +127,9 @@
127
127
  "react-icons": "4.8.0",
128
128
  "react-markdown": "8.0.7",
129
129
  "react-syntax-highlighter": "15.5.0",
130
- "rehype-raw": "6.1.1",
131
- "remark-gfm": "3.0.1",
132
130
  "rimraf": "5.0.1",
133
131
  "semantic-release": "21.0.2",
134
- "storybook": "7.0.17",
132
+ "storybook": "7.0.18",
135
133
  "storybook-css-modules": "1.0.8",
136
134
  "type-fest": "3.11.0",
137
135
  "typescript": "5.0.4",
@@ -141,10 +139,24 @@
141
139
  "peerDependencies": {
142
140
  "@react-hookz/web": "^14.2.3 || >=15.x",
143
141
  "emoji-regex": "^10.2.1",
142
+ "hast-util-is-element": "^2.1.0",
144
143
  "lodash-es": "^4.17.21",
145
- "marked": "^4.1.1",
144
+ "mdast-util-gfm-strikethrough": "^1.0.0",
145
+ "micromark-extension-gfm-strikethrough": "^1.0.0",
146
146
  "react": "^17.0.0 || ^18.0.0",
147
147
  "react-dom": "^17.0.0 || ^18.0.0",
148
- "turndown": "^7.1.1"
148
+ "rehype": "^12.0.0",
149
+ "rehype-minify-whitespace": "^5.0.0",
150
+ "rehype-raw": "^6.1.0",
151
+ "rehype-stringify": "^9.0.0",
152
+ "remark": "^14.0.0",
153
+ "remark-breaks": "^3.0.0",
154
+ "remark-gfm": "^3.0.0",
155
+ "remark-rehype": "^10.1.0",
156
+ "turndown": "^7.1.0",
157
+ "unified": "^10.1.0",
158
+ "unist-util-is": "^5.2.0",
159
+ "unist-util-remove": "^3.1.0",
160
+ "unist-util-visit": "^4.1.0"
149
161
  }
150
162
  }
@@ -1,8 +0,0 @@
1
- import { marked } from 'marked';
2
- /**
3
- * A Marked extension which tweaks the `checkbox` renderer to prevent rendering of task lists with
4
- * GitHub's Flavored Markdown syntax, which is unsupported by Tiptap.
5
- */
6
- declare const checkbox: marked.MarkedExtension;
7
- export { checkbox };
8
- //# sourceMappingURL=checkbox.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"checkbox.d.ts","sourceRoot":"","sources":["../../../../src/serializers/html/extensions/checkbox.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAE/B;;;GAGG;AACH,QAAA,MAAM,QAAQ,EAAE,MAAM,CAAC,eAMtB,CAAA;AAED,OAAO,EAAE,QAAQ,EAAE,CAAA"}
@@ -1,12 +0,0 @@
1
- /**
2
- * A Marked extension which tweaks the `checkbox` renderer to prevent rendering of task lists with
3
- * GitHub's Flavored Markdown syntax, which is unsupported by Tiptap.
4
- */
5
- const checkbox = {
6
- renderer: {
7
- checkbox(checked) {
8
- return `[${checked ? 'x' : ' '}] `;
9
- },
10
- },
11
- };
12
- export { checkbox };
@@ -1,9 +0,0 @@
1
- import { marked } from 'marked';
2
- /**
3
- * A Marked extension which tweaks the `code` renderer to remove the newline between the last code
4
- * line and `</code></pre>`. Although that newline is part of the CommonMark spec, this custom rule
5
- * is required to prevent Tiptap from rendering a blank line at the end of the code block.
6
- */
7
- declare const code: marked.MarkedExtension;
8
- export { code };
9
- //# sourceMappingURL=code.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"code.d.ts","sourceRoot":"","sources":["../../../../src/serializers/html/extensions/code.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAO/B;;;;GAIG;AACH,QAAA,MAAM,IAAI,EAAE,MAAM,CAAC,eAQlB,CAAA;AAED,OAAO,EAAE,IAAI,EAAE,CAAA"}
@@ -1,20 +0,0 @@
1
- import { marked } from 'marked';
2
- /**
3
- * Initialize a new instance of the original renderer to be used by the extension.
4
- */
5
- const markedRenderer = new marked.Renderer();
6
- /**
7
- * A Marked extension which tweaks the `code` renderer to remove the newline between the last code
8
- * line and `</code></pre>`. Although that newline is part of the CommonMark spec, this custom rule
9
- * is required to prevent Tiptap from rendering a blank line at the end of the code block.
10
- */
11
- const code = {
12
- renderer: {
13
- code(code, infostring, escaped) {
14
- return markedRenderer.code
15
- .apply(this, [code, infostring, escaped])
16
- .replace(/\n(<\/code><\/pre>)$/m, '$1');
17
- },
18
- },
19
- };
20
- export { code };
@@ -1,13 +0,0 @@
1
- import { marked } from 'marked';
2
- import type { Schema } from 'prosemirror-model';
3
- /**
4
- * A Marked extension which disables multiple parsing rules by disabling the rules respective
5
- * tokenizers based on the availability of marks and/or nodes in the editor schema.
6
- *
7
- * @param schema The editor schema to be used for nodes and marks detection.
8
- */
9
- declare function disabled(schema: Schema): {
10
- tokenizer: Partial<Omit<marked.Tokenizer<false>, "constructor" | "options">>;
11
- };
12
- export { disabled };
13
- //# sourceMappingURL=disabled.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"disabled.d.ts","sourceRoot":"","sources":["../../../../src/serializers/html/extensions/disabled.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAK/B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAS/C;;;;;GAKG;AACH,iBAAS,QAAQ,CAAC,MAAM,EAAE,MAAM;;EAyI/B;AAED,OAAO,EAAE,QAAQ,EAAE,CAAA"}
@@ -1,125 +0,0 @@
1
- import { marked } from 'marked';
2
- import { buildSuggestionSchemaPartialRegex } from '../../../helpers/serializer';
3
- import { INITIAL_MARKED_OPTIONS } from '../html';
4
- /**
5
- * A Marked extension which disables multiple parsing rules by disabling the rules respective
6
- * tokenizers based on the availability of marks and/or nodes in the editor schema.
7
- *
8
- * @param schema The editor schema to be used for nodes and marks detection.
9
- */
10
- function disabled(schema) {
11
- const markedTokenizer = new marked.Tokenizer(INITIAL_MARKED_OPTIONS);
12
- const tokenizer = {};
13
- if (!schema.nodes.blockquote) {
14
- Object.assign(tokenizer, {
15
- blockquote() {
16
- return undefined;
17
- },
18
- });
19
- }
20
- if (!schema.marks.bold || !schema.marks.italic) {
21
- Object.assign(tokenizer, {
22
- emStrong() {
23
- return undefined;
24
- },
25
- });
26
- }
27
- // Given that there isn't a one to one mapping between the bullet/ordered list nodes and Marked
28
- // tokenizers, we need to conditionally disable the `list` tokenizer based on the input
29
- if (!schema.nodes.bulletList || !schema.nodes.orderedList) {
30
- Object.assign(tokenizer, {
31
- list(src) {
32
- const isOrdered = /^\d+/.test(src);
33
- if ((isOrdered && schema.nodes.orderedList) ||
34
- (!isOrdered && schema.nodes.bulletList)) {
35
- return markedTokenizer.list.apply(this, [src]);
36
- }
37
- return undefined;
38
- },
39
- });
40
- }
41
- if (!schema.marks.code) {
42
- Object.assign(tokenizer, {
43
- codespan() {
44
- return undefined;
45
- },
46
- });
47
- }
48
- if (!schema.nodes.codeBlock) {
49
- Object.assign(tokenizer, {
50
- code() {
51
- return undefined;
52
- },
53
- fences() {
54
- return undefined;
55
- },
56
- });
57
- }
58
- if (!schema.nodes.hardBreak) {
59
- Object.assign(tokenizer, {
60
- br() {
61
- return undefined;
62
- },
63
- });
64
- }
65
- if (!schema.nodes.heading) {
66
- Object.assign(tokenizer, {
67
- heading() {
68
- return undefined;
69
- },
70
- });
71
- }
72
- if (!schema.nodes.horizontalRule) {
73
- Object.assign(tokenizer, {
74
- hr() {
75
- return undefined;
76
- },
77
- });
78
- }
79
- if (!schema.marks.link) {
80
- Object.assign(tokenizer, {
81
- url() {
82
- return undefined;
83
- },
84
- });
85
- }
86
- // Given that there isn't a one to one mapping between the link/image mark/node and Marked
87
- // tokenizers, nor Marked supports our custom Markdown syntax for suggestions, we need to
88
- // conditionally disable the `link` tokenizer based on the input
89
- if (!schema.marks.link || !schema.nodes.image) {
90
- const suggestionSchemaPartialRegex = buildSuggestionSchemaPartialRegex(schema);
91
- const suggestionSchemaRegex = suggestionSchemaPartialRegex
92
- ? new RegExp(`^\\[[^\\]]*\\]\\(${suggestionSchemaPartialRegex}`)
93
- : null;
94
- Object.assign(tokenizer, {
95
- link(src) {
96
- const isImage = /^!\[[^\]]*\]\([^)]+\)/.test(src);
97
- const isSuggestion = suggestionSchemaRegex?.test(src);
98
- if ((isImage && schema.nodes.image) ||
99
- (!isImage && schema.marks.link) ||
100
- isSuggestion) {
101
- return markedTokenizer.link.apply(this, [src]);
102
- }
103
- return undefined;
104
- },
105
- });
106
- }
107
- if (!schema.marks.strike) {
108
- Object.assign(tokenizer, {
109
- del() {
110
- return undefined;
111
- },
112
- });
113
- }
114
- if (!schema.nodes.table) {
115
- Object.assign(tokenizer, {
116
- table() {
117
- return undefined;
118
- },
119
- });
120
- }
121
- return {
122
- tokenizer,
123
- };
124
- }
125
- export { disabled };
@@ -1,10 +0,0 @@
1
- import { marked } from 'marked';
2
- /**
3
- * A Marked extension which tweaks the `html` renderer to escape special characters (i.e. `&`, `<`,
4
- * `>`, `"`, and `'`) for unsupported tokens. This custom rule is required because the escaping must
5
- * be performed at the token level to avoid escaping the full input, which would result in unwanted
6
- * escaped output (i.e. valid HTML would be escaped).
7
- */
8
- declare const html: marked.MarkedExtension;
9
- export { html };
10
- //# sourceMappingURL=html.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../../../src/serializers/html/extensions/html.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAE/B;;;;;GAKG;AACH,QAAA,MAAM,IAAI,EAAE,MAAM,CAAC,eAMlB,CAAA;AAED,OAAO,EAAE,IAAI,EAAE,CAAA"}
@@ -1,15 +0,0 @@
1
- import { escape } from 'lodash-es';
2
- /**
3
- * A Marked extension which tweaks the `html` renderer to escape special characters (i.e. `&`, `<`,
4
- * `>`, `"`, and `'`) for unsupported tokens. This custom rule is required because the escaping must
5
- * be performed at the token level to avoid escaping the full input, which would result in unwanted
6
- * escaped output (i.e. valid HTML would be escaped).
7
- */
8
- const html = {
9
- renderer: {
10
- html(html) {
11
- return escape(html);
12
- },
13
- },
14
- };
15
- export { html };
@@ -1,11 +0,0 @@
1
- import { marked } from 'marked';
2
- /**
3
- * A Marked extension which tweaks the `link` renderer to add support for suggestion nodes, while
4
- * preserving the original renderer for standard links.
5
- *
6
- * @param suggestionSchemaRegex A regular expression with valid URL schemas for the available
7
- * suggestion nodes.
8
- */
9
- declare function link(suggestionSchemaRegex: RegExp): marked.MarkedExtension;
10
- export { link };
11
- //# sourceMappingURL=link.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"link.d.ts","sourceRoot":"","sources":["../../../../src/serializers/html/extensions/link.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAI/B;;;;;;GAMG;AACH,iBAAS,IAAI,CAAC,qBAAqB,EAAE,MAAM,GAAG,MAAM,CAAC,eAAe,CAgBnE;AAED,OAAO,EAAE,IAAI,EAAE,CAAA"}
@@ -1,25 +0,0 @@
1
- import { marked } from 'marked';
2
- const markedRenderer = new marked.Renderer();
3
- /**
4
- * A Marked extension which tweaks the `link` renderer to add support for suggestion nodes, while
5
- * preserving the original renderer for standard links.
6
- *
7
- * @param suggestionSchemaRegex A regular expression with valid URL schemas for the available
8
- * suggestion nodes.
9
- */
10
- function link(suggestionSchemaRegex) {
11
- return {
12
- renderer: {
13
- link(href, title, text) {
14
- if (href && suggestionSchemaRegex?.test(href)) {
15
- const [, schema, id] = /^([a-z-]+):\/\/(\S+)$/i.exec(href) || [];
16
- if (schema && id && text) {
17
- return `<span data-${schema} data-id="${id}" data-label="${text}"></span>`;
18
- }
19
- }
20
- return markedRenderer.link.apply(this, [href, title, text]);
21
- },
22
- },
23
- };
24
- }
25
- export { link };
@@ -1,12 +0,0 @@
1
- import { marked } from 'marked';
2
- import type { NodeType } from 'prosemirror-model';
3
- /**
4
- * A Marked extension which tweaks the `paragraph` renderer to remove the surrounding `<p></p>` tag
5
- * when it's wrapping an image element as a single child, or to remove all inline images if the
6
- * editor was configured without inline image support (default).
7
- *
8
- * @param imageNode The node object for the schema image node.
9
- */
10
- declare function paragraph(imageNode?: NodeType): marked.MarkedExtension;
11
- export { paragraph };
12
- //# sourceMappingURL=paragraph.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"paragraph.d.ts","sourceRoot":"","sources":["../../../../src/serializers/html/extensions/paragraph.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAI/B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAOjD;;;;;;GAMG;AACH,iBAAS,SAAS,CAAC,SAAS,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC,eAAe,CA6C/D;AAED,OAAO,EAAE,SAAS,EAAE,CAAA"}
@@ -1,51 +0,0 @@
1
- import { marked } from 'marked';
2
- import { parseHtmlToElement } from '../../../helpers/dom';
3
- /**
4
- * Initialize a new instance of the original renderer to be used by the extension.
5
- */
6
- const markedRenderer = new marked.Renderer();
7
- /**
8
- * A Marked extension which tweaks the `paragraph` renderer to remove the surrounding `<p></p>` tag
9
- * when it's wrapping an image element as a single child, or to remove all inline images if the
10
- * editor was configured without inline image support (default).
11
- *
12
- * @param imageNode The node object for the schema image node.
13
- */
14
- function paragraph(imageNode) {
15
- const isInlineImageSupported = imageNode ? imageNode.spec.inline : false;
16
- return {
17
- renderer: {
18
- paragraph(text) {
19
- const renderedHTML = markedRenderer.paragraph.apply(this, [text]);
20
- const { firstElementChild: renderedElement } = parseHtmlToElement(renderedHTML);
21
- // Return the rendered HTML as a safeguard in the event that the rendered element
22
- // does not exist (just in case, but highly unlikely to happen)
23
- if (!renderedElement) {
24
- return renderedHTML;
25
- }
26
- // Return the rendered element HTML if inline images are supported
27
- if (isInlineImageSupported) {
28
- return renderedElement.outerHTML;
29
- }
30
- const imageChildNodes = Array.from(renderedElement.childNodes).filter((childNode) => childNode.nodeName === 'IMG');
31
- // Return the HTML contained in the rendered element if it only contains images
32
- // (i.e. removes the surrounding `<p></p>` tag from image child elements)
33
- if (renderedElement.childNodes.length === imageChildNodes.length) {
34
- return renderedElement.innerHTML;
35
- }
36
- // Remove all image elements contained in the rendered element since at this point
37
- // we know that the editor does not support inline images
38
- if (renderedElement.childNodes.length > 1) {
39
- renderedElement.childNodes.forEach((childNode) => {
40
- if (childNode.nodeName === 'IMG') {
41
- renderedElement.removeChild(childNode);
42
- }
43
- });
44
- }
45
- // Return the rendered element HTML (stripped of inline images)
46
- return renderedElement.outerHTML;
47
- },
48
- },
49
- };
50
- }
51
- export { paragraph };
@@ -1,9 +0,0 @@
1
- import { marked } from 'marked';
2
- /**
3
- * A Marked extension which tweaks the `checkbox`, `list`, and `listitem` renderers to add support
4
- * for task lists (i.e., `* [ ] Task`), while preserving the original renderers for standard bullet
5
- * and ordered lists.
6
- */
7
- declare const taskList: marked.MarkedExtension;
8
- export { taskList };
9
- //# sourceMappingURL=task-list.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"task-list.d.ts","sourceRoot":"","sources":["../../../../src/serializers/html/extensions/task-list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAO/B;;;;GAIG;AACH,QAAA,MAAM,QAAQ,EAAE,MAAM,CAAC,eAwBtB,CAAA;AAED,OAAO,EAAE,QAAQ,EAAE,CAAA"}
@@ -1,31 +0,0 @@
1
- import { marked } from 'marked';
2
- /**
3
- * Initialize a new instance of the original renderer to be used by the extension.
4
- */
5
- const markedRenderer = new marked.Renderer();
6
- /**
7
- * A Marked extension which tweaks the `checkbox`, `list`, and `listitem` renderers to add support
8
- * for task lists (i.e., `* [ ] Task`), while preserving the original renderers for standard bullet
9
- * and ordered lists.
10
- */
11
- const taskList = {
12
- renderer: {
13
- checkbox(checked) {
14
- return `[${checked ? 'x' : ' '}] `;
15
- },
16
- list(body, ordered, start) {
17
- if (body.includes('data-type="taskItem"')) {
18
- return `<ul data-type="taskList">\n${body}</ul>\n`;
19
- }
20
- return markedRenderer.list.apply(this, [body, ordered, start]);
21
- },
22
- listitem(text) {
23
- const taskItem = /^(\[[ x]\])( [\s\S]*)?/i.exec(text);
24
- if (taskItem) {
25
- return `<li data-type="taskItem" data-checked="${String(taskItem[1] !== '[ ]')}">${taskItem[2].trim() || ''}</li>\n`;
26
- }
27
- return markedRenderer.listitem.apply(this, [text, false, false]);
28
- },
29
- },
30
- };
31
- export { taskList };