@lexical/mdast 0.0.0-bootstrap.0 → 0.47.0
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/README.md +207 -2
- package/dist/LexicalMdast.dev.js +2620 -0
- package/dist/LexicalMdast.dev.mjs +2594 -0
- package/dist/LexicalMdast.js +11 -0
- package/dist/LexicalMdast.js.flow +228 -0
- package/dist/LexicalMdast.mjs +36 -0
- package/dist/LexicalMdast.node.mjs +34 -0
- package/dist/LexicalMdast.prod.js +11 -0
- package/dist/LexicalMdast.prod.mjs +11 -0
- package/dist/MdastExport.d.ts +20 -0
- package/dist/MdastExportExtension.d.ts +80 -0
- package/dist/MdastExtension.d.ts +21 -0
- package/dist/MdastGfmExtension.d.ts +20 -0
- package/dist/MdastImport.d.ts +48 -0
- package/dist/MdastImportExtension.d.ts +264 -0
- package/dist/MdastShortcuts.d.ts +27 -0
- package/dist/MdastStream.d.ts +70 -0
- package/dist/MdastTableExtension.d.ts +29 -0
- package/dist/compile.d.ts +18 -0
- package/dist/handlers.d.ts +84 -0
- package/dist/index.d.ts +15 -0
- package/dist/state.d.ts +58 -0
- package/dist/types.d.ts +165 -0
- package/dist/typescript-too-old.d.ts +18 -0
- package/package.json +90 -6
- package/src/MdastExport.ts +545 -0
- package/src/MdastExportExtension.ts +122 -0
- package/src/MdastExtension.ts +30 -0
- package/src/MdastGfmExtension.ts +38 -0
- package/src/MdastImport.ts +236 -0
- package/src/MdastImportExtension.ts +635 -0
- package/src/MdastShortcuts.ts +453 -0
- package/src/MdastStream.ts +187 -0
- package/src/MdastTableExtension.ts +144 -0
- package/src/compile.ts +44 -0
- package/src/handlers.ts +648 -0
- package/src/index.ts +69 -0
- package/src/state.ts +124 -0
- package/src/types.ts +197 -0
|
@@ -0,0 +1,635 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type {
|
|
10
|
+
CompiledMdast,
|
|
11
|
+
FromMarkdownExtension,
|
|
12
|
+
MdastExportHandler,
|
|
13
|
+
MdastExportRule,
|
|
14
|
+
MdastImportHandler,
|
|
15
|
+
MdastImportRule,
|
|
16
|
+
MicromarkExtension,
|
|
17
|
+
ToMarkdownExtension,
|
|
18
|
+
} from './types';
|
|
19
|
+
import type {ElementNode, LexicalNode} from 'lexical';
|
|
20
|
+
import type {Root, ThematicBreak} from 'mdast';
|
|
21
|
+
|
|
22
|
+
import {CodeNode} from '@lexical/code-core';
|
|
23
|
+
import {
|
|
24
|
+
$createHorizontalRuleNode,
|
|
25
|
+
$getExtensionOutput,
|
|
26
|
+
$isHorizontalRuleNode,
|
|
27
|
+
effect,
|
|
28
|
+
getExtensionDependencyFromEditor,
|
|
29
|
+
HorizontalRuleExtension,
|
|
30
|
+
namedSignals,
|
|
31
|
+
} from '@lexical/extension';
|
|
32
|
+
import {LinkNode} from '@lexical/link';
|
|
33
|
+
import {ListItemNode, ListNode} from '@lexical/list';
|
|
34
|
+
import {HeadingNode, QuoteNode} from '@lexical/rich-text';
|
|
35
|
+
import {
|
|
36
|
+
$getState,
|
|
37
|
+
$setState,
|
|
38
|
+
configExtension,
|
|
39
|
+
defineExtension,
|
|
40
|
+
safeCast,
|
|
41
|
+
shallowMergeConfig,
|
|
42
|
+
} from 'lexical';
|
|
43
|
+
import {
|
|
44
|
+
gfmAutolinkLiteralFromMarkdown,
|
|
45
|
+
gfmAutolinkLiteralToMarkdown,
|
|
46
|
+
} from 'mdast-util-gfm-autolink-literal';
|
|
47
|
+
import {
|
|
48
|
+
gfmStrikethroughFromMarkdown,
|
|
49
|
+
gfmStrikethroughToMarkdown,
|
|
50
|
+
} from 'mdast-util-gfm-strikethrough';
|
|
51
|
+
import {
|
|
52
|
+
gfmTaskListItemFromMarkdown,
|
|
53
|
+
gfmTaskListItemToMarkdown,
|
|
54
|
+
} from 'mdast-util-gfm-task-list-item';
|
|
55
|
+
import {gfmAutolinkLiteral} from 'micromark-extension-gfm-autolink-literal';
|
|
56
|
+
import {gfmStrikethrough} from 'micromark-extension-gfm-strikethrough';
|
|
57
|
+
import {gfmTaskListItem} from 'micromark-extension-gfm-task-list-item';
|
|
58
|
+
|
|
59
|
+
import {compileMdast} from './compile';
|
|
60
|
+
import {
|
|
61
|
+
$exportCode,
|
|
62
|
+
$exportHeading,
|
|
63
|
+
$exportLineBreak,
|
|
64
|
+
$exportLink,
|
|
65
|
+
$exportList,
|
|
66
|
+
$importBlockquote,
|
|
67
|
+
$importBreak,
|
|
68
|
+
$importCode,
|
|
69
|
+
$importEmphasis,
|
|
70
|
+
$importHeading,
|
|
71
|
+
$importLink,
|
|
72
|
+
$importLinkReference,
|
|
73
|
+
$importList,
|
|
74
|
+
$importListItem,
|
|
75
|
+
$importParagraph,
|
|
76
|
+
$importShadowRootBlockquote,
|
|
77
|
+
$importStrong,
|
|
78
|
+
exportParagraph,
|
|
79
|
+
exportQuote,
|
|
80
|
+
exportTab,
|
|
81
|
+
exportText,
|
|
82
|
+
importDefinition,
|
|
83
|
+
importDelete,
|
|
84
|
+
importHtml,
|
|
85
|
+
importInlineCode,
|
|
86
|
+
importText,
|
|
87
|
+
} from './handlers';
|
|
88
|
+
import {createMdastImport} from './MdastImport';
|
|
89
|
+
import {registerMarkdownShortcuts} from './MdastShortcuts';
|
|
90
|
+
import {hrMarkerState} from './state';
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Configuration for the core {@link MdastImportExtension} registry. Feature
|
|
94
|
+
* extensions contribute to these arrays via `configExtension(MdastImportExtension,
|
|
95
|
+
* …)`; you rarely need to set them by hand. The shape mirrors
|
|
96
|
+
* `@lexical/html`'s `DOMImportExtension` config: raw contribution arrays that
|
|
97
|
+
* `mergeConfig` concatenates and `build` compiles.
|
|
98
|
+
* @experimental
|
|
99
|
+
*/
|
|
100
|
+
export interface MdastConfig {
|
|
101
|
+
/** mdast `type` -> Lexical mapping rules used while importing. */
|
|
102
|
+
readonly importRules: readonly MdastImportRule[];
|
|
103
|
+
/** Lexical `getType()` -> mdast mapping rules used while exporting. */
|
|
104
|
+
readonly exportRules: readonly MdastExportRule[];
|
|
105
|
+
/** micromark syntax extensions (the tokenizer layer). */
|
|
106
|
+
readonly micromarkExtensions: readonly MicromarkExtension[];
|
|
107
|
+
/** `mdast-util-from-markdown` extensions (tokens -> mdast). */
|
|
108
|
+
readonly mdastExtensions: readonly FromMarkdownExtension[];
|
|
109
|
+
/** `mdast-util-to-markdown` extensions (mdast -> Markdown string). */
|
|
110
|
+
readonly toMarkdownExtensions: readonly ToMarkdownExtension[];
|
|
111
|
+
/**
|
|
112
|
+
* mdast inline `type`s that the streaming shortcuts may materialize when
|
|
113
|
+
* their closing delimiter is typed. Extensions that contribute a new inline
|
|
114
|
+
* construct add its type here (with a matching import rule) so shortcuts
|
|
115
|
+
* stay in lock-step with the parser.
|
|
116
|
+
*/
|
|
117
|
+
readonly inlineShortcutTypes: readonly string[];
|
|
118
|
+
/**
|
|
119
|
+
* Characters that can close an inline construct; typing one triggers an
|
|
120
|
+
* inline re-scan. Extensions add their construct's closing character here
|
|
121
|
+
* (e.g. `'='` for `==highlight==`).
|
|
122
|
+
*/
|
|
123
|
+
readonly inlineShortcutTriggers: readonly string[];
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* The runtime API exposed by {@link MdastImportExtension}. Obtain it inside a
|
|
128
|
+
* read/update with `$getExtensionOutput(MdastImportExtension)`, or use the
|
|
129
|
+
* {@link $convertFromMarkdownString} shorthand. Serialization lives in
|
|
130
|
+
* `MdastExportExtension` so import-only editors don't bundle the
|
|
131
|
+
* serializer (`mdast-util-to-markdown`).
|
|
132
|
+
* @experimental
|
|
133
|
+
*/
|
|
134
|
+
export interface MdastImportExtensionOutput {
|
|
135
|
+
/**
|
|
136
|
+
* Parses `markdown` with micromark/mdast and replaces the contents of the
|
|
137
|
+
* editor root (or `node`). Must be called inside an `editor.update()`.
|
|
138
|
+
*/
|
|
139
|
+
$convertFromMarkdownString(markdown: string, node?: ElementNode): void;
|
|
140
|
+
/**
|
|
141
|
+
* Imports an already-parsed mdast `Root` tree (e.g. produced or
|
|
142
|
+
* transformed by unified/remark tooling) and replaces the contents of the
|
|
143
|
+
* editor root (or `node`). Must be called inside an `editor.update()`.
|
|
144
|
+
* Source-based syntax preservation does not apply (there is no source
|
|
145
|
+
* text to recover literal markers from).
|
|
146
|
+
*/
|
|
147
|
+
$convertFromMdast(tree: Root, node?: ElementNode): void;
|
|
148
|
+
/**
|
|
149
|
+
* Parses `markdown` and returns the resulting block-level nodes as a
|
|
150
|
+
* detached array, without modifying the document or the selection — e.g.
|
|
151
|
+
* for insertion at an arbitrary position via `selection.insertNodes()`.
|
|
152
|
+
* Must be called inside an `editor.update()`.
|
|
153
|
+
*/
|
|
154
|
+
$generateNodesFromMarkdownString(markdown: string): LexicalNode[];
|
|
155
|
+
/**
|
|
156
|
+
* Walks an already-parsed mdast `Root` tree and returns the resulting
|
|
157
|
+
* block-level nodes as a detached array, without modifying the document
|
|
158
|
+
* or the selection. Must be called inside an `editor.update()`. As with
|
|
159
|
+
* {@link MdastImportExtensionOutput.$convertFromMdast}, source-based
|
|
160
|
+
* syntax preservation does not apply.
|
|
161
|
+
*/
|
|
162
|
+
$generateNodesFromMdast(tree: Root): LexicalNode[];
|
|
163
|
+
/**
|
|
164
|
+
* The compiled registry assembled from every contributing extension.
|
|
165
|
+
*
|
|
166
|
+
* @internal consumed by {@link MdastShortcutsExtension}.
|
|
167
|
+
*/
|
|
168
|
+
readonly registry: CompiledMdast;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// The baseline rules that need no node packages: paragraphs and inline text
|
|
172
|
+
// formatting (CommonMark handles these without any micromark extension).
|
|
173
|
+
const CORE_IMPORT_RULES: readonly MdastImportRule[] = [
|
|
174
|
+
{$import: $importParagraph, type: 'paragraph'},
|
|
175
|
+
{$import: importText, type: 'text'},
|
|
176
|
+
{$import: importHtml, type: 'html'},
|
|
177
|
+
{$import: importInlineCode, type: 'inlineCode'},
|
|
178
|
+
{$import: $importEmphasis, type: 'emphasis'},
|
|
179
|
+
{$import: $importStrong, type: 'strong'},
|
|
180
|
+
{$import: $importBreak, type: 'break'},
|
|
181
|
+
];
|
|
182
|
+
const CORE_EXPORT_RULES: readonly MdastExportRule[] = [
|
|
183
|
+
{$export: exportParagraph, type: 'paragraph'},
|
|
184
|
+
{$export: exportText, type: 'text'},
|
|
185
|
+
{$export: $exportLineBreak, type: 'linebreak'},
|
|
186
|
+
{$export: exportTab, type: 'tab'},
|
|
187
|
+
];
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* The core Markdown registry for `@lexical/mdast`, modeled on
|
|
191
|
+
* `@lexical/html`'s `DOMImportExtension`. It assembles the import/export rules
|
|
192
|
+
* and micromark/mdast extensions contributed by feature extensions into a
|
|
193
|
+
* compiled registry, and exposes Markdown import through its
|
|
194
|
+
* {@link MdastImportExtensionOutput}. Markdown export is provided separately by
|
|
195
|
+
* `MdastExportExtension`, so editors that never serialize back to Markdown
|
|
196
|
+
* don't bundle the serializer.
|
|
197
|
+
*
|
|
198
|
+
* You normally do not depend on this directly — depend on a feature extension
|
|
199
|
+
* (e.g. {@link MdastCommonMarkExtension}) which contributes its rules here and
|
|
200
|
+
* ships the nodes those rules need.
|
|
201
|
+
*
|
|
202
|
+
* @example
|
|
203
|
+
* ```ts
|
|
204
|
+
* import {$convertFromMarkdownString, MdastCommonMarkExtension}
|
|
205
|
+
* from '@lexical/mdast';
|
|
206
|
+
* import {buildEditorFromExtensions} from '@lexical/extension';
|
|
207
|
+
* import {defineExtension} from 'lexical';
|
|
208
|
+
*
|
|
209
|
+
* const editor = buildEditorFromExtensions(
|
|
210
|
+
* defineExtension({dependencies: [MdastCommonMarkExtension], name: '[root]'}),
|
|
211
|
+
* );
|
|
212
|
+
* editor.update(() => $convertFromMarkdownString('# Hi'));
|
|
213
|
+
* ```
|
|
214
|
+
* @experimental
|
|
215
|
+
*/
|
|
216
|
+
export const MdastImportExtension = /* @__PURE__ */ defineExtension<
|
|
217
|
+
MdastConfig,
|
|
218
|
+
'@lexical/mdast/Import',
|
|
219
|
+
MdastImportExtensionOutput,
|
|
220
|
+
void
|
|
221
|
+
>({
|
|
222
|
+
build(editor, config): MdastImportExtensionOutput {
|
|
223
|
+
const registry = compileMdast(config);
|
|
224
|
+
const {
|
|
225
|
+
$generateNodesFromMarkdown,
|
|
226
|
+
$generateNodesFromMdast: $generateNodesFromTree,
|
|
227
|
+
$importMarkdown,
|
|
228
|
+
$importMdast,
|
|
229
|
+
} = createMdastImport(registry);
|
|
230
|
+
return {
|
|
231
|
+
$convertFromMarkdownString: $importMarkdown,
|
|
232
|
+
$convertFromMdast: $importMdast,
|
|
233
|
+
$generateNodesFromMarkdownString: $generateNodesFromMarkdown,
|
|
234
|
+
$generateNodesFromMdast: $generateNodesFromTree,
|
|
235
|
+
registry,
|
|
236
|
+
};
|
|
237
|
+
},
|
|
238
|
+
config: /* @__PURE__ */ safeCast<MdastConfig>({
|
|
239
|
+
exportRules: CORE_EXPORT_RULES,
|
|
240
|
+
importRules: CORE_IMPORT_RULES,
|
|
241
|
+
// Core CommonMark inline formatting; feature extensions contribute
|
|
242
|
+
// their own types/triggers (links add 'link'/')', strikethrough adds
|
|
243
|
+
// 'delete'/'~').
|
|
244
|
+
inlineShortcutTriggers: ['*', '_', '`'],
|
|
245
|
+
inlineShortcutTypes: ['emphasis', 'inlineCode', 'strong'],
|
|
246
|
+
mdastExtensions: [],
|
|
247
|
+
micromarkExtensions: [],
|
|
248
|
+
toMarkdownExtensions: [],
|
|
249
|
+
}),
|
|
250
|
+
mergeConfig(config, partial) {
|
|
251
|
+
// Prepend contributed rules so extensions merged later (closer to the
|
|
252
|
+
// editor root) take priority, matching DOMImportExtension's convention.
|
|
253
|
+
// Every key is set explicitly so an explicitly-undefined key in `partial`
|
|
254
|
+
// (allowed by Partial<MdastConfig>) can never clobber the merged arrays.
|
|
255
|
+
function mergeArray<T>(
|
|
256
|
+
contributed: readonly T[] | undefined,
|
|
257
|
+
existing: readonly T[],
|
|
258
|
+
): readonly T[] {
|
|
259
|
+
return contributed ? [...contributed, ...existing] : existing;
|
|
260
|
+
}
|
|
261
|
+
return shallowMergeConfig(config, {
|
|
262
|
+
exportRules: mergeArray(partial.exportRules, config.exportRules),
|
|
263
|
+
importRules: mergeArray(partial.importRules, config.importRules),
|
|
264
|
+
inlineShortcutTriggers: mergeArray(
|
|
265
|
+
partial.inlineShortcutTriggers,
|
|
266
|
+
config.inlineShortcutTriggers,
|
|
267
|
+
),
|
|
268
|
+
inlineShortcutTypes: mergeArray(
|
|
269
|
+
partial.inlineShortcutTypes,
|
|
270
|
+
config.inlineShortcutTypes,
|
|
271
|
+
),
|
|
272
|
+
mdastExtensions: mergeArray(
|
|
273
|
+
partial.mdastExtensions,
|
|
274
|
+
config.mdastExtensions,
|
|
275
|
+
),
|
|
276
|
+
micromarkExtensions: mergeArray(
|
|
277
|
+
partial.micromarkExtensions,
|
|
278
|
+
config.micromarkExtensions,
|
|
279
|
+
),
|
|
280
|
+
toMarkdownExtensions: mergeArray(
|
|
281
|
+
partial.toMarkdownExtensions,
|
|
282
|
+
config.toMarkdownExtensions,
|
|
283
|
+
),
|
|
284
|
+
});
|
|
285
|
+
},
|
|
286
|
+
name: '@lexical/mdast/Import',
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* ATX (`# …`) and setext headings, shipping {@link HeadingNode}.
|
|
291
|
+
* @experimental
|
|
292
|
+
*/
|
|
293
|
+
export const MdastHeadingExtension = /* @__PURE__ */ defineExtension({
|
|
294
|
+
dependencies: [
|
|
295
|
+
/* @__PURE__ */ configExtension(MdastImportExtension, {
|
|
296
|
+
exportRules: [{$export: $exportHeading, type: 'heading'}],
|
|
297
|
+
importRules: [{$import: $importHeading, type: 'heading'}],
|
|
298
|
+
}),
|
|
299
|
+
],
|
|
300
|
+
name: '@lexical/mdast/Heading',
|
|
301
|
+
nodes: [HeadingNode],
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Block quotes (`> …`), shipping {@link QuoteNode}. For blockquotes that hold
|
|
306
|
+
* block-level children (nested lists, code, quotes) with full fidelity, add
|
|
307
|
+
* {@link MdastShadowRootQuoteExtension}.
|
|
308
|
+
* @experimental
|
|
309
|
+
*/
|
|
310
|
+
export const MdastBlockquoteExtension = /* @__PURE__ */ defineExtension({
|
|
311
|
+
dependencies: [
|
|
312
|
+
/* @__PURE__ */ configExtension(MdastImportExtension, {
|
|
313
|
+
exportRules: [{$export: exportQuote, type: 'quote'}],
|
|
314
|
+
importRules: [{$import: $importBlockquote, type: 'blockquote'}],
|
|
315
|
+
}),
|
|
316
|
+
],
|
|
317
|
+
name: '@lexical/mdast/Blockquote',
|
|
318
|
+
nodes: [QuoteNode],
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Convenience bundle of {@link MdastHeadingExtension} and
|
|
323
|
+
* {@link MdastBlockquoteExtension} — the constructs backed by
|
|
324
|
+
* `@lexical/rich-text` nodes.
|
|
325
|
+
* @experimental
|
|
326
|
+
*/
|
|
327
|
+
export const MdastRichTextExtension = /* @__PURE__ */ defineExtension({
|
|
328
|
+
dependencies: [MdastHeadingExtension, MdastBlockquoteExtension],
|
|
329
|
+
name: '@lexical/mdast/RichText',
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Ordered and unordered lists, shipping {@link ListNode} and
|
|
334
|
+
* {@link ListItemNode}. For GFM task lists (`- [x] …`) add
|
|
335
|
+
* {@link MdastTaskListExtension}.
|
|
336
|
+
* @experimental
|
|
337
|
+
*/
|
|
338
|
+
export const MdastListExtension = /* @__PURE__ */ defineExtension({
|
|
339
|
+
dependencies: [
|
|
340
|
+
/* @__PURE__ */ configExtension(MdastImportExtension, {
|
|
341
|
+
exportRules: [{$export: $exportList, type: 'list'}],
|
|
342
|
+
importRules: [
|
|
343
|
+
{$import: $importList, type: 'list'},
|
|
344
|
+
{$import: $importListItem, type: 'listItem'},
|
|
345
|
+
],
|
|
346
|
+
}),
|
|
347
|
+
],
|
|
348
|
+
name: '@lexical/mdast/List',
|
|
349
|
+
nodes: [ListNode, ListItemNode],
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Opt-in: GFM task lists (`- [x] done`), layered on
|
|
354
|
+
* {@link MdastListExtension}. Contributes the `gfmTaskListItem` grammar; the
|
|
355
|
+
* list import/export handlers already understand `checked`, and the typing
|
|
356
|
+
* shortcut (`[ ] ` / `[x] ` in a list item) is enabled by the grammar's
|
|
357
|
+
* presence in the registry.
|
|
358
|
+
* @experimental
|
|
359
|
+
*/
|
|
360
|
+
export const MdastTaskListExtension = /* @__PURE__ */ defineExtension({
|
|
361
|
+
dependencies: [
|
|
362
|
+
MdastListExtension,
|
|
363
|
+
/* @__PURE__ */ configExtension(MdastImportExtension, {
|
|
364
|
+
mdastExtensions: [/* @__PURE__ */ gfmTaskListItemFromMarkdown()],
|
|
365
|
+
micromarkExtensions: [/* @__PURE__ */ gfmTaskListItem()],
|
|
366
|
+
toMarkdownExtensions: [/* @__PURE__ */ gfmTaskListItemToMarkdown()],
|
|
367
|
+
}),
|
|
368
|
+
],
|
|
369
|
+
name: '@lexical/mdast/TaskList',
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Fenced and indented code blocks, shipping {@link CodeNode}.
|
|
374
|
+
* @experimental
|
|
375
|
+
*/
|
|
376
|
+
export const MdastCodeExtension = /* @__PURE__ */ defineExtension({
|
|
377
|
+
dependencies: [
|
|
378
|
+
/* @__PURE__ */ configExtension(MdastImportExtension, {
|
|
379
|
+
exportRules: [{$export: $exportCode, type: 'code'}],
|
|
380
|
+
importRules: [{$import: $importCode, type: 'code'}],
|
|
381
|
+
}),
|
|
382
|
+
],
|
|
383
|
+
name: '@lexical/mdast/Code',
|
|
384
|
+
nodes: [CodeNode],
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Inline links, CommonMark autolinks (`<https://…>`), and CommonMark
|
|
389
|
+
* reference links (`[text][id]` resolved against `[id]: url` definitions),
|
|
390
|
+
* shipping {@link LinkNode}. Reference links are resolved to their target on
|
|
391
|
+
* import and serialize back as inline links. For GFM *literal* autolinks
|
|
392
|
+
* (bare `https://…` in prose) add {@link MdastAutolinkLiteralExtension}.
|
|
393
|
+
* @experimental
|
|
394
|
+
*/
|
|
395
|
+
export const MdastLinkExtension = /* @__PURE__ */ defineExtension({
|
|
396
|
+
dependencies: [
|
|
397
|
+
/* @__PURE__ */ configExtension(MdastImportExtension, {
|
|
398
|
+
exportRules: [{$export: $exportLink, type: 'link'}],
|
|
399
|
+
importRules: [
|
|
400
|
+
{$import: $importLink, type: 'link'},
|
|
401
|
+
{$import: $importLinkReference, type: 'linkReference'},
|
|
402
|
+
{$import: importDefinition, type: 'definition'},
|
|
403
|
+
],
|
|
404
|
+
inlineShortcutTriggers: [')'],
|
|
405
|
+
inlineShortcutTypes: ['link'],
|
|
406
|
+
}),
|
|
407
|
+
],
|
|
408
|
+
name: '@lexical/mdast/Link',
|
|
409
|
+
nodes: [LinkNode],
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Opt-in: GFM literal autolinks — bare `https://…` / `www.…` URLs and email
|
|
414
|
+
* addresses in prose become links, the way GitHub renders them. This is a GFM
|
|
415
|
+
* extension rather than CommonMark, so it is not part of
|
|
416
|
+
* {@link MdastCommonMarkExtension}; add it alongside to opt in:
|
|
417
|
+
* ```ts
|
|
418
|
+
* dependencies: [MdastCommonMarkExtension, MdastAutolinkLiteralExtension]
|
|
419
|
+
* ```
|
|
420
|
+
* @experimental
|
|
421
|
+
*/
|
|
422
|
+
export const MdastAutolinkLiteralExtension = /* @__PURE__ */ defineExtension({
|
|
423
|
+
dependencies: [
|
|
424
|
+
MdastLinkExtension,
|
|
425
|
+
/* @__PURE__ */ configExtension(MdastImportExtension, {
|
|
426
|
+
mdastExtensions: [/* @__PURE__ */ gfmAutolinkLiteralFromMarkdown()],
|
|
427
|
+
micromarkExtensions: [/* @__PURE__ */ gfmAutolinkLiteral()],
|
|
428
|
+
toMarkdownExtensions: [/* @__PURE__ */ gfmAutolinkLiteralToMarkdown()],
|
|
429
|
+
}),
|
|
430
|
+
],
|
|
431
|
+
name: '@lexical/mdast/AutolinkLiteral',
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Opt-in: import Markdown blockquotes as *shadow root* {@link QuoteNode}s
|
|
436
|
+
* (`$createQuoteNode({shadowRoot: true})`), which hold block-level children
|
|
437
|
+
* like a table cell. Structured blockquotes — multiple paragraphs, nested
|
|
438
|
+
* lists, code blocks, nested quotes — then round-trip with full fidelity
|
|
439
|
+
* instead of being reassembled from inline content.
|
|
440
|
+
*
|
|
441
|
+
* Not part of {@link MdastCommonMarkExtension}; add it alongside to opt in:
|
|
442
|
+
* ```ts
|
|
443
|
+
* dependencies: [MdastCommonMarkExtension, MdastShadowRootQuoteExtension]
|
|
444
|
+
* ```
|
|
445
|
+
* The quote *export* handler supports both forms per node, so legacy quotes
|
|
446
|
+
* (e.g. created by the `> ` shortcut) and shadow root quotes can coexist.
|
|
447
|
+
* @experimental
|
|
448
|
+
*/
|
|
449
|
+
export const MdastShadowRootQuoteExtension = /* @__PURE__ */ defineExtension({
|
|
450
|
+
dependencies: [
|
|
451
|
+
MdastBlockquoteExtension,
|
|
452
|
+
// Declared after (and depending on) MdastBlockquoteExtension so this
|
|
453
|
+
// blockquote rule merges later and takes priority over the default.
|
|
454
|
+
/* @__PURE__ */ configExtension(MdastImportExtension, {
|
|
455
|
+
importRules: [{$import: $importShadowRootBlockquote, type: 'blockquote'}],
|
|
456
|
+
}),
|
|
457
|
+
],
|
|
458
|
+
name: '@lexical/mdast/ShadowRootQuote',
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
const $importThematicBreak: MdastImportHandler<ThematicBreak> = (node, ctx) => {
|
|
462
|
+
const hr = $createHorizontalRuleNode();
|
|
463
|
+
// Preserve the marker character (`---` vs `***` vs `___`).
|
|
464
|
+
if (ctx.source && node.position && node.position.start.offset != null) {
|
|
465
|
+
const marker = ctx.source
|
|
466
|
+
.slice(node.position.start.offset, node.position.start.offset + 4)
|
|
467
|
+
.trimStart()[0];
|
|
468
|
+
if (marker === '-' || marker === '*' || marker === '_') {
|
|
469
|
+
$setState(hr, hrMarkerState, marker);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
return hr;
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
const $exportThematicBreak: MdastExportHandler = node => {
|
|
476
|
+
if (!$isHorizontalRuleNode(node)) {
|
|
477
|
+
return null;
|
|
478
|
+
}
|
|
479
|
+
const rule: ThematicBreak = {type: 'thematicBreak'};
|
|
480
|
+
const marker = $getState(node, hrMarkerState);
|
|
481
|
+
if (marker) {
|
|
482
|
+
rule.data = {mdastRule: marker};
|
|
483
|
+
}
|
|
484
|
+
return rule;
|
|
485
|
+
};
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* Thematic breaks (`---`, `***`, `___`), mapped to
|
|
489
|
+
* {@link HorizontalRuleExtension}'s `HorizontalRuleNode`. The original marker
|
|
490
|
+
* character is preserved on round-trip.
|
|
491
|
+
* @experimental
|
|
492
|
+
*/
|
|
493
|
+
export const MdastHorizontalRuleExtension = /* @__PURE__ */ defineExtension({
|
|
494
|
+
dependencies: [
|
|
495
|
+
HorizontalRuleExtension,
|
|
496
|
+
/* @__PURE__ */ configExtension(MdastImportExtension, {
|
|
497
|
+
exportRules: [{$export: $exportThematicBreak, type: 'horizontalrule'}],
|
|
498
|
+
importRules: [{$import: $importThematicBreak, type: 'thematicBreak'}],
|
|
499
|
+
}),
|
|
500
|
+
],
|
|
501
|
+
name: '@lexical/mdast/HorizontalRule',
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* GFM `~~strikethrough~~`, mapped to the Lexical `strikethrough` text format.
|
|
506
|
+
* Needs no extra nodes (the core text handlers carry the format bit).
|
|
507
|
+
* @experimental
|
|
508
|
+
*/
|
|
509
|
+
export const MdastStrikethroughExtension = /* @__PURE__ */ defineExtension({
|
|
510
|
+
dependencies: [
|
|
511
|
+
/* @__PURE__ */ configExtension(MdastImportExtension, {
|
|
512
|
+
importRules: [{$import: importDelete, type: 'delete'}],
|
|
513
|
+
inlineShortcutTriggers: ['~'],
|
|
514
|
+
inlineShortcutTypes: ['delete'],
|
|
515
|
+
mdastExtensions: [/* @__PURE__ */ gfmStrikethroughFromMarkdown()],
|
|
516
|
+
micromarkExtensions: [/* @__PURE__ */ gfmStrikethrough()],
|
|
517
|
+
toMarkdownExtensions: [/* @__PURE__ */ gfmStrikethroughToMarkdown()],
|
|
518
|
+
}),
|
|
519
|
+
],
|
|
520
|
+
name: '@lexical/mdast/Strikethrough',
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Convenience bundle of every CommonMark construct: headings, block quotes,
|
|
525
|
+
* lists, code blocks, links, and thematic breaks. GFM features
|
|
526
|
+
* (strikethrough, task lists, literal autolinks, tables) are bundled
|
|
527
|
+
* separately as `MdastGfmExtension`, and `MdastExportExtension` (or the
|
|
528
|
+
* `MdastExtension` bundle) adds serialization back to Markdown.
|
|
529
|
+
* @experimental
|
|
530
|
+
*/
|
|
531
|
+
export const MdastCommonMarkExtension = /* @__PURE__ */ defineExtension({
|
|
532
|
+
dependencies: [
|
|
533
|
+
MdastRichTextExtension,
|
|
534
|
+
MdastListExtension,
|
|
535
|
+
MdastCodeExtension,
|
|
536
|
+
MdastLinkExtension,
|
|
537
|
+
MdastHorizontalRuleExtension,
|
|
538
|
+
],
|
|
539
|
+
name: '@lexical/mdast/CommonMark',
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
export interface MdastShortcutsConfig {
|
|
543
|
+
/** Disable the streaming shortcuts without removing the extension. */
|
|
544
|
+
disabled: boolean;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* Streaming Markdown shortcuts (block markers convert on space, fenced code on
|
|
549
|
+
* Enter, inline constructs on their closing delimiter). Each keystroke is fed
|
|
550
|
+
* back through micromark, so shortcut recognition uses the same grammar and
|
|
551
|
+
* the same enabled extensions as import: shortcuts exist for exactly the
|
|
552
|
+
* feature extensions in the editor and no others. Combine with
|
|
553
|
+
* {@link MdastCommonMarkExtension} (and `MdastGfmExtension`) — this extension
|
|
554
|
+
* only wires up the behavior, it does not pull in any grammar of its own.
|
|
555
|
+
* @experimental
|
|
556
|
+
*/
|
|
557
|
+
export const MdastShortcutsExtension = /* @__PURE__ */ defineExtension({
|
|
558
|
+
build: (editor, config) => namedSignals(config),
|
|
559
|
+
config: /* @__PURE__ */ safeCast<MdastShortcutsConfig>({disabled: false}),
|
|
560
|
+
dependencies: [MdastImportExtension],
|
|
561
|
+
name: '@lexical/mdast/Shortcuts',
|
|
562
|
+
register: (editor, config, state) => {
|
|
563
|
+
const {disabled} = state.getOutput();
|
|
564
|
+
return effect(() => {
|
|
565
|
+
if (disabled.value) {
|
|
566
|
+
return undefined;
|
|
567
|
+
}
|
|
568
|
+
const {registry} = getExtensionDependencyFromEditor(
|
|
569
|
+
editor,
|
|
570
|
+
MdastImportExtension,
|
|
571
|
+
).output;
|
|
572
|
+
return registerMarkdownShortcuts(editor, registry);
|
|
573
|
+
});
|
|
574
|
+
},
|
|
575
|
+
});
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* Shorthand for `$getExtensionOutput(MdastImportExtension).$convertFromMarkdownString`.
|
|
579
|
+
* Must be called inside an `editor.update()`. Throws if the editor was not
|
|
580
|
+
* built with {@link MdastImportExtension} (or an extension that depends on it).
|
|
581
|
+
* @experimental
|
|
582
|
+
*/
|
|
583
|
+
export function $convertFromMarkdownString(
|
|
584
|
+
markdown: string,
|
|
585
|
+
node?: ElementNode,
|
|
586
|
+
): void {
|
|
587
|
+
$getExtensionOutput(MdastImportExtension).$convertFromMarkdownString(
|
|
588
|
+
markdown,
|
|
589
|
+
node,
|
|
590
|
+
);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Shorthand for `$getExtensionOutput(MdastImportExtension).$convertFromMdast`.
|
|
595
|
+
* Must be called inside an `editor.update()`. Throws if the editor was not
|
|
596
|
+
* built with {@link MdastImportExtension} (or an extension that depends on
|
|
597
|
+
* it).
|
|
598
|
+
* @experimental
|
|
599
|
+
*/
|
|
600
|
+
export function $convertFromMdast(tree: Root, node?: ElementNode): void {
|
|
601
|
+
$getExtensionOutput(MdastImportExtension).$convertFromMdast(tree, node);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* Shorthand for
|
|
606
|
+
* `$getExtensionOutput(MdastImportExtension).$generateNodesFromMarkdownString`.
|
|
607
|
+
* Parses `markdown` and returns the resulting block-level nodes as a
|
|
608
|
+
* detached array, without modifying the document or the selection. Must be
|
|
609
|
+
* called inside an `editor.update()`. Throws if the editor was not built
|
|
610
|
+
* with {@link MdastImportExtension} (or an extension that depends on it).
|
|
611
|
+
* @experimental
|
|
612
|
+
*/
|
|
613
|
+
export function $generateNodesFromMarkdownString(
|
|
614
|
+
markdown: string,
|
|
615
|
+
): LexicalNode[] {
|
|
616
|
+
return $getExtensionOutput(
|
|
617
|
+
MdastImportExtension,
|
|
618
|
+
).$generateNodesFromMarkdownString(markdown);
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
/**
|
|
622
|
+
* Shorthand for
|
|
623
|
+
* `$getExtensionOutput(MdastImportExtension).$generateNodesFromMdast`.
|
|
624
|
+
* Walks an already-parsed mdast `Root` tree and returns the resulting
|
|
625
|
+
* block-level nodes as a detached array, without modifying the document or
|
|
626
|
+
* the selection. Must be called inside an `editor.update()`. Throws if the
|
|
627
|
+
* editor was not built with {@link MdastImportExtension} (or an extension
|
|
628
|
+
* that depends on it).
|
|
629
|
+
* @experimental
|
|
630
|
+
*/
|
|
631
|
+
export function $generateNodesFromMdast(tree: Root): LexicalNode[] {
|
|
632
|
+
return $getExtensionOutput(MdastImportExtension).$generateNodesFromMdast(
|
|
633
|
+
tree,
|
|
634
|
+
);
|
|
635
|
+
}
|