@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/src/state.ts ADDED
@@ -0,0 +1,124 @@
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 {createState} from 'lexical';
10
+
11
+ /**
12
+ * Per-node state used to round-trip the *exact* Markdown syntax a construct was
13
+ * parsed from, so re-serializing produces minimally different output. This is
14
+ * the same technique `@lexical/markdown` uses; the state lives on the Lexical
15
+ * nodes (and therefore survives serialization), and the exporter reads it to
16
+ * reproduce the original marker/fence/break.
17
+ *
18
+ * All of these default to the empty sentinel (`''` / `false`) meaning
19
+ * "unknown" — i.e. the node was not created by a Markdown import. The exporter
20
+ * only pins a node's syntax when the marker is known, so nodes created in the
21
+ * editor defer to the document-level serialization options.
22
+ */
23
+
24
+ /** The bullet character (`-`, `*`, `+`) an unordered/check `ListNode` used. */
25
+ export const listMarkerState = /* @__PURE__ */ createState('mdastListMarker', {
26
+ parse: (v): '' | '*' | '+' | '-' =>
27
+ v === '-' || v === '*' || v === '+' ? v : '',
28
+ resetOnCopyNode: true,
29
+ });
30
+
31
+ /** The delimiter (`.` or `)`) an ordered `ListNode` used. */
32
+ export const orderedMarkerState = /* @__PURE__ */ createState(
33
+ 'mdastOrderedMarker',
34
+ {
35
+ parse: (v): '' | ')' | '.' => (v === '.' || v === ')' ? v : ''),
36
+ resetOnCopyNode: true,
37
+ },
38
+ );
39
+
40
+ /** The marker (`_`) an italic run used when it was not the default `*`. */
41
+ export const emphasisMarkerState = /* @__PURE__ */ createState(
42
+ 'mdastEmphasisMarker',
43
+ {
44
+ parse: (v): string => (v === '_' ? '_' : ''),
45
+ resetOnCopyNode: true,
46
+ },
47
+ );
48
+
49
+ /** The marker (`_`) a bold run used when it was not the default `*`. */
50
+ export const strongMarkerState = /* @__PURE__ */ createState(
51
+ 'mdastStrongMarker',
52
+ {
53
+ parse: (v): string => (v === '_' ? '_' : ''),
54
+ resetOnCopyNode: true,
55
+ },
56
+ );
57
+
58
+ /** Whether a (level 1/2) `HeadingNode` was written in setext style. */
59
+ export const setextState = /* @__PURE__ */ createState('mdastSetext', {
60
+ parse: (v): boolean => v === true,
61
+ resetOnCopyNode: true,
62
+ });
63
+
64
+ /** The fence a `CodeNode` used (e.g. ```` ``` ````, ````` ```` `````, `~~~`). */
65
+ export const codeFenceState = /* @__PURE__ */ createState('mdastCodeFence', {
66
+ parse: (v): string =>
67
+ typeof v === 'string' && /^(`{3,}|~{3,})$/.test(v) ? v : '',
68
+ resetOnCopyNode: true,
69
+ });
70
+
71
+ /**
72
+ * The info-string tail after a `CodeNode`'s language (e.g. `title=x` in
73
+ * ```` ```js title=x ````). `CodeNode` itself only models the language;
74
+ * this keeps the rest of the info string so it survives the round-trip.
75
+ */
76
+ export const codeMetaState = /* @__PURE__ */ createState('mdastCodeMeta', {
77
+ parse: (v): string => (typeof v === 'string' ? v : ''),
78
+ resetOnCopyNode: true,
79
+ });
80
+
81
+ /**
82
+ * The hard-line-break marker a `LineBreakNode` used (`\` or trailing spaces).
83
+ * The empty sentinel means the break is *soft* (a source newline or an
84
+ * editor-created line break) and serializes as a plain newline.
85
+ */
86
+ export const hardLineBreakState = /* @__PURE__ */ createState(
87
+ 'mdastHardLineBreak',
88
+ {
89
+ parse: (v): string =>
90
+ typeof v === 'string' && /^(\\| {2,})$/.test(v) ? v : '',
91
+ resetOnCopyNode: true,
92
+ },
93
+ );
94
+
95
+ /**
96
+ * Marks a `LineBreakNode` that stands for a *paragraph boundary* inside a
97
+ * container whose Lexical children are inline (blockquote, list item). Set by
98
+ * the import handlers when they join sibling mdast paragraphs; the exporter
99
+ * splits on it to reconstruct the paragraphs.
100
+ */
101
+ export const paragraphBreakState = /* @__PURE__ */ createState(
102
+ 'mdastParagraphBreak',
103
+ {
104
+ parse: (v): boolean => v === true,
105
+ resetOnCopyNode: true,
106
+ },
107
+ );
108
+
109
+ /** The marker (`-`, `*`, `_`) a thematic break / `HorizontalRuleNode` used. */
110
+ export const hrMarkerState = /* @__PURE__ */ createState('mdastHrMarker', {
111
+ parse: (v): string => (v === '-' || v === '*' || v === '_' ? v : ''),
112
+ resetOnCopyNode: true,
113
+ });
114
+
115
+ /**
116
+ * The syntax a `LinkNode` was written in: `'inline'` (`[text](url)`),
117
+ * `'autolink'` (`<url>`), or `'literal'` (a bare GFM autolink literal,
118
+ * `https://…` in prose).
119
+ */
120
+ export const linkStyleState = /* @__PURE__ */ createState('mdastLinkStyle', {
121
+ parse: (v): '' | 'autolink' | 'inline' | 'literal' =>
122
+ v === 'inline' || v === 'autolink' || v === 'literal' ? v : '',
123
+ resetOnCopyNode: true,
124
+ });
package/src/types.ts ADDED
@@ -0,0 +1,197 @@
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 {ElementNode, LexicalNode} from 'lexical';
10
+ import type {
11
+ BlockContent,
12
+ Nodes as MdastNode,
13
+ Parent as MdastParent,
14
+ PhrasingContent,
15
+ } from 'mdast';
16
+ import type {Extension as FromMarkdownExtension} from 'mdast-util-from-markdown';
17
+ import type {Options as ToMarkdownExtension} from 'mdast-util-to-markdown';
18
+ import type {Extension as MicromarkExtension} from 'micromark-util-types';
19
+
20
+ export type {
21
+ FromMarkdownExtension,
22
+ MdastNode,
23
+ MdastParent,
24
+ MicromarkExtension,
25
+ ToMarkdownExtension,
26
+ };
27
+
28
+ // The mdast `data` fields this package uses to round-trip the original
29
+ // Markdown syntax. Declared through interface merging — mdast's sanctioned
30
+ // extension point — so no casts are needed to read or write them, and
31
+ // consumers see them typed on exported trees ($convertToMdast).
32
+ declare module 'mdast' {
33
+ interface BreakData {
34
+ /** The hard-break marker: `'\\'`, trailing spaces, or `''` for soft. */
35
+ mdastBreak?: string;
36
+ }
37
+ interface CodeData {
38
+ /** The literal fence the code block used (e.g. ``` or ~~~). */
39
+ mdastFence?: string;
40
+ }
41
+ interface HeadingData {
42
+ /** Present (true) when the heading was written in setext style. */
43
+ mdastSetext?: boolean;
44
+ }
45
+ interface LinkData {
46
+ /** The syntax the link was written in. */
47
+ mdastLinkStyle?: 'autolink' | 'inline' | 'literal';
48
+ }
49
+ interface ListData {
50
+ /** The bullet character an unordered list used (`-`, `*`, `+`). */
51
+ mdastBullet?: '*' | '+' | '-';
52
+ /** The delimiter an ordered list used (`.` or `)`). */
53
+ mdastBulletOrdered?: ')' | '.';
54
+ }
55
+ interface ThematicBreakData {
56
+ /** The marker character the thematic break used (`-`, `*`, `_`). */
57
+ mdastRule?: string;
58
+ }
59
+ }
60
+
61
+ /**
62
+ * The context passed to {@link MdastImportHandler}s while an mdast tree is
63
+ * being walked and converted into Lexical nodes.
64
+ * @experimental
65
+ */
66
+ export interface MdastImportContext {
67
+ /**
68
+ * The accumulated text-format bitmask for the current inline position
69
+ * (e.g. inside `strong` > `emphasis` this carries the bold + italic bits).
70
+ * Block handlers can ignore this; inline handlers should pass it along to
71
+ * {@link createText} and the recursion helpers.
72
+ */
73
+ readonly format: number;
74
+ /**
75
+ * The original Markdown source being imported, or `''` when importing a
76
+ * pre-parsed mdast tree. Handlers slice this by `node.position` to recover
77
+ * the literal syntax (list marker, code fence, hard-break style) for
78
+ * round-trip preservation.
79
+ */
80
+ readonly source: string;
81
+ /**
82
+ * Convert every child of `parent` into Lexical nodes, optionally layering an
83
+ * additional text-format bitmask on top of the current {@link format}.
84
+ */
85
+ importChildren(parent: MdastParent, format?: number): LexicalNode[];
86
+ /**
87
+ * Convert a single mdast node (and its descendants) into Lexical nodes.
88
+ */
89
+ importNode(node: MdastNode, format?: number): LexicalNode[];
90
+ /**
91
+ * Create text nodes for `value` with the current (or supplied) format
92
+ * bitmask; `\n` becomes a `LineBreakNode` and `\t` a `TabNode`.
93
+ */
94
+ createText(value: string, format?: number): LexicalNode[];
95
+ /**
96
+ * Resolves a link/image reference `identifier` (already normalized by
97
+ * mdast) against the document's definitions (`[id]: url "title"`).
98
+ */
99
+ getDefinition(
100
+ identifier: string,
101
+ ): {url: string; title?: string | null} | undefined;
102
+ }
103
+
104
+ /**
105
+ * Converts an mdast node of a particular `type` into one or more Lexical
106
+ * nodes. Returning `null` defers to the next registered handler.
107
+ * @experimental
108
+ */
109
+ export type MdastImportHandler<T extends MdastNode = MdastNode> = (
110
+ node: T,
111
+ context: MdastImportContext,
112
+ ) => LexicalNode | LexicalNode[] | null;
113
+
114
+ /**
115
+ * The context passed to {@link MdastExportHandler}s while a Lexical tree is
116
+ * converted back into an mdast tree.
117
+ * @experimental
118
+ */
119
+ export interface MdastExportContext {
120
+ /**
121
+ * Convert the children of `node` into mdast nodes by dispatching each child
122
+ * through the registered export handlers.
123
+ */
124
+ exportChildren(node: ElementNode): MdastNode[];
125
+ /**
126
+ * Convert the inline children of `node` into mdast phrasing content,
127
+ * grouping bare line breaks as mdast `break` nodes.
128
+ */
129
+ exportInline(node: ElementNode): PhrasingContent[];
130
+ /**
131
+ * Convert the inline children of `node` into one or more mdast block nodes
132
+ * (paragraphs), splitting on hard line breaks. Used by containers such as
133
+ * block quotes and list items whose Lexical children are inline but whose
134
+ * mdast children must be block-level.
135
+ */
136
+ exportBlocks(node: ElementNode): BlockContent[];
137
+ /**
138
+ * Whether `node` belongs in the current export: always `true` for a
139
+ * whole-document export; during a selection export, `true` when the node
140
+ * or any descendant is selected. The `exportChildren`/`exportInline`/
141
+ * `exportBlocks` walks apply this automatically — handlers only need it
142
+ * when they iterate children manually (e.g. list items, table rows) to
143
+ * skip structural children the selection does not reach.
144
+ */
145
+ isIncluded(node: LexicalNode): boolean;
146
+ }
147
+
148
+ /**
149
+ * Converts a Lexical node into one or more mdast nodes. Returning `null`
150
+ * defers to the next registered handler.
151
+ * @experimental
152
+ */
153
+ export type MdastExportHandler<T extends LexicalNode = LexicalNode> = (
154
+ node: T,
155
+ context: MdastExportContext,
156
+ ) => MdastNode | MdastNode[] | null;
157
+
158
+ /**
159
+ * A single import mapping: which mdast node `type` it handles and how. The
160
+ * unit an extension contributes (alongside the micromark/mdast extensions that
161
+ * tokenize the construct) to {@link MdastImportExtension}'s `importRules` config.
162
+ * @experimental
163
+ */
164
+ export interface MdastImportRule {
165
+ /** The mdast node `type` this rule handles (e.g. `'heading'`). */
166
+ type: string;
167
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
168
+ $import: MdastImportHandler<any>;
169
+ }
170
+
171
+ /**
172
+ * A single export mapping: which Lexical node `getType()` it handles and how.
173
+ * @experimental
174
+ */
175
+ export interface MdastExportRule {
176
+ /** The Lexical node `getType()` this rule handles (e.g. `'heading'`). */
177
+ type: string;
178
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
179
+ $export: MdastExportHandler<any>;
180
+ }
181
+
182
+ /**
183
+ * The compiled registry produced from {@link MdastConfig} at editor build
184
+ * time and consumed by the importer, exporter, and shortcut scanner.
185
+ * @experimental
186
+ */
187
+ export interface CompiledMdast {
188
+ importHandlers: Map<string, MdastImportHandler>;
189
+ exportHandlers: Map<string, MdastExportHandler>;
190
+ micromarkExtensions: MicromarkExtension[];
191
+ mdastExtensions: FromMarkdownExtension[];
192
+ toMarkdownExtensions: ToMarkdownExtension[];
193
+ /** mdast inline `type`s eligible for streaming shortcut materialization. */
194
+ inlineShortcutTypes: Set<string>;
195
+ /** Characters that can close an inline construct and trigger a re-scan. */
196
+ inlineShortcutTriggers: Set<string>;
197
+ }