@thomasfosterau/effect-richtext 0.0.0-stage → 0.1.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/LICENSE +48 -0
- package/README.md +118 -2
- package/dist/_virtual/_rolldown/runtime.js +12 -0
- package/dist/commands/commands.d.ts +323 -0
- package/dist/commands/commands.js +722 -0
- package/dist/commands/internal.js +43 -0
- package/dist/commands/keymaps.d.ts +46 -0
- package/dist/commands/keymaps.js +64 -0
- package/dist/commands.d.ts +3 -0
- package/dist/commands.js +3 -0
- package/dist/compat/commands.d.ts +250 -0
- package/dist/compat/commands.js +275 -0
- package/dist/compat/internal/core.d.ts +20 -0
- package/dist/compat/internal/core.js +148 -0
- package/dist/compat/internal/props.js +82 -0
- package/dist/compat/keymap.d.ts +21 -0
- package/dist/compat/keymap.js +38 -0
- package/dist/compat/model.d.ts +893 -0
- package/dist/compat/model.js +1212 -0
- package/dist/compat/orderedmap.d.ts +74 -0
- package/dist/compat/orderedmap.js +124 -0
- package/dist/compat/schema-basic.d.ts +38 -0
- package/dist/compat/schema-basic.js +238 -0
- package/dist/compat/schema-list.d.ts +81 -0
- package/dist/compat/schema-list.js +156 -0
- package/dist/compat/state.d.ts +415 -0
- package/dist/compat/state.js +689 -0
- package/dist/compat/test-builder.d.ts +98 -0
- package/dist/compat/test-builder.js +197 -0
- package/dist/compat/transform.d.ts +458 -0
- package/dist/compat/transform.js +803 -0
- package/dist/compat/view.d.ts +411 -0
- package/dist/compat/view.js +409 -0
- package/dist/dropcursor.d.ts +46 -0
- package/dist/dropcursor.js +176 -0
- package/dist/extensions/defaults.d.ts +716 -0
- package/dist/extensions/defaults.js +209 -0
- package/dist/extensions/editor.d.ts +246 -0
- package/dist/extensions/editor.js +271 -0
- package/dist/extensions/extension.d.ts +375 -0
- package/dist/extensions/extension.js +233 -0
- package/dist/extensions.d.ts +4 -0
- package/dist/extensions.js +4 -0
- package/dist/gapcursor.d.ts +66 -0
- package/dist/gapcursor.js +273 -0
- package/dist/history.d.ts +178 -0
- package/dist/history.js +412 -0
- package/dist/html.d.ts +158 -0
- package/dist/html.js +164 -0
- package/dist/inputrules.d.ts +271 -0
- package/dist/inputrules.js +321 -0
- package/dist/internal/attempt.js +15 -0
- package/dist/internal/json.js +29 -0
- package/dist/keymap/keymap.d.ts +130 -0
- package/dist/keymap/keymap.js +159 -0
- package/dist/keymap.d.ts +2 -0
- package/dist/keymap.js +1 -0
- package/dist/markdown/errors.d.ts +23 -0
- package/dist/markdown/errors.js +26 -0
- package/dist/markdown/from_markdown.d.ts +185 -0
- package/dist/markdown/from_markdown.js +266 -0
- package/dist/markdown/schema.d.ts +522 -0
- package/dist/markdown/schema.js +292 -0
- package/dist/markdown/to_markdown.d.ts +251 -0
- package/dist/markdown/to_markdown.js +413 -0
- package/dist/markdown.d.ts +5 -0
- package/dist/markdown.js +5 -0
- package/dist/model/attrsCodec.js +48 -0
- package/dist/model/comparedeep.js +17 -0
- package/dist/model/content.d.ts +232 -0
- package/dist/model/content.js +630 -0
- package/dist/model/diff.js +66 -0
- package/dist/model/dom.d.ts +3 -0
- package/dist/model/errors.d.ts +90 -0
- package/dist/model/errors.js +76 -0
- package/dist/model/fragment.d.ts +131 -0
- package/dist/model/fragment.js +262 -0
- package/dist/model/from_dom.d.ts +299 -0
- package/dist/model/from_dom.js +618 -0
- package/dist/model/json.d.ts +37 -0
- package/dist/model/json.js +126 -0
- package/dist/model/mark.d.ts +73 -0
- package/dist/model/mark.js +121 -0
- package/dist/model/node.d.ts +267 -0
- package/dist/model/node.js +434 -0
- package/dist/model/replace.d.ts +78 -0
- package/dist/model/replace.js +207 -0
- package/dist/model/resolvedpos.d.ts +196 -0
- package/dist/model/resolvedpos.js +305 -0
- package/dist/model/schema.d.ts +740 -0
- package/dist/model/schema.js +451 -0
- package/dist/model/to_dom.d.ts +169 -0
- package/dist/model/to_dom.js +238 -0
- package/dist/model.d.ts +12 -0
- package/dist/model.js +12 -0
- package/dist/placeholder.d.ts +53 -0
- package/dist/placeholder.js +79 -0
- package/dist/reactive/observer.d.ts +54 -0
- package/dist/reactive/observer.js +61 -0
- package/dist/reactive/selectors.d.ts +22 -0
- package/dist/reactive/selectors.js +61 -0
- package/dist/reactive/stateref.d.ts +72 -0
- package/dist/reactive/stateref.js +71 -0
- package/dist/reactive/streams.d.ts +83 -0
- package/dist/reactive/streams.js +85 -0
- package/dist/reactive.d.ts +16 -0
- package/dist/reactive.js +26 -0
- package/dist/schema-basic.d.ts +448 -0
- package/dist/schema-basic.js +269 -0
- package/dist/schema-list/commands.d.ts +66 -0
- package/dist/schema-list/commands.js +203 -0
- package/dist/schema-list.d.ts +92 -0
- package/dist/schema-list.js +100 -0
- package/dist/state/errors.d.ts +39 -0
- package/dist/state/errors.js +32 -0
- package/dist/state/plugin.d.ts +232 -0
- package/dist/state/plugin.js +82 -0
- package/dist/state/selection.d.ts +297 -0
- package/dist/state/selection.js +486 -0
- package/dist/state/state.d.ts +212 -0
- package/dist/state/state.js +489 -0
- package/dist/state/transaction.d.ts +145 -0
- package/dist/state/transaction.js +195 -0
- package/dist/state.d.ts +6 -0
- package/dist/state.js +6 -0
- package/dist/test-builder.d.ts +3582 -0
- package/dist/test-builder.js +239 -0
- package/dist/transform/attr_step.d.ts +60 -0
- package/dist/transform/attr_step.js +105 -0
- package/dist/transform/errors.d.ts +50 -0
- package/dist/transform/errors.js +35 -0
- package/dist/transform/map.d.ts +182 -0
- package/dist/transform/map.js +280 -0
- package/dist/transform/mark.js +98 -0
- package/dist/transform/mark_set_step.js +69 -0
- package/dist/transform/mark_step.d.ts +113 -0
- package/dist/transform/mark_step.js +225 -0
- package/dist/transform/replace.d.ts +18 -0
- package/dist/transform/replace.js +381 -0
- package/dist/transform/replace_step.d.ts +109 -0
- package/dist/transform/replace_step.js +175 -0
- package/dist/transform/step.d.ts +157 -0
- package/dist/transform/step.js +168 -0
- package/dist/transform/structure.d.ts +89 -0
- package/dist/transform/structure.js +350 -0
- package/dist/transform/transform.d.ts +248 -0
- package/dist/transform/transform.js +373 -0
- package/dist/transform.d.ts +10 -0
- package/dist/transform.js +10 -0
- package/dist/view/browser.js +39 -0
- package/dist/view/capturekeys.js +282 -0
- package/dist/view/clipboard.d.ts +8 -0
- package/dist/view/clipboard.js +245 -0
- package/dist/view/decoration.d.ts +339 -0
- package/dist/view/decoration.js +566 -0
- package/dist/view/dom.d.ts +18 -0
- package/dist/view/dom.js +140 -0
- package/dist/view/domchange.js +295 -0
- package/dist/view/domcoords.d.ts +22 -0
- package/dist/view/domcoords.js +448 -0
- package/dist/view/domobserver.d.ts +47 -0
- package/dist/view/domobserver.js +328 -0
- package/dist/view/errors.d.ts +18 -0
- package/dist/view/errors.js +21 -0
- package/dist/view/input.d.ts +58 -0
- package/dist/view/input.js +704 -0
- package/dist/view/selection.js +198 -0
- package/dist/view/view.d.ts +699 -0
- package/dist/view/view.js +623 -0
- package/dist/view/viewdesc.d.ts +236 -0
- package/dist/view/viewdesc.js +1205 -0
- package/dist/view-css.d.ts +43 -0
- package/dist/view-css.js +124 -0
- package/dist/view.d.ts +6 -0
- package/dist/view.js +4 -0
- package/package.json +206 -3
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import * as Schema from "effect/Schema";
|
|
2
|
+
// src/markdown/errors.ts
|
|
3
|
+
/**
|
|
4
|
+
* Tagged errors raised by the Markdown parser and serializer.
|
|
5
|
+
*
|
|
6
|
+
* @since 0.1.0
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* A Markdown error: the tokenizer produced a token type the parser has
|
|
10
|
+
* no handler for, a parser was configured with an invalid parsing
|
|
11
|
+
* spec, or (in strict mode) the serializer met a node or mark type it
|
|
12
|
+
* has no serializer for.
|
|
13
|
+
*
|
|
14
|
+
* Parsing reports unsupported tokens in the Effect error channel; the
|
|
15
|
+
* other cases are programming errors and are thrown.
|
|
16
|
+
*
|
|
17
|
+
* @since 0.1.0
|
|
18
|
+
* @category errors
|
|
19
|
+
*/
|
|
20
|
+
var MarkdownError = class extends Schema.TaggedError()("MarkdownError", { message: Schema.String }) {};
|
|
21
|
+
/**
|
|
22
|
+
* @since 0.1.0
|
|
23
|
+
* @category guards
|
|
24
|
+
*/
|
|
25
|
+
const isMarkdownError = (u) => u instanceof MarkdownError;
|
|
26
|
+
export { MarkdownError, isMarkdownError };
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { ModelError } from "../model/errors.js";
|
|
2
|
+
import { AnySchema, Attrs, MarkNames, MarkType, NodeNames, NodeType } from "../model/schema.js";
|
|
3
|
+
import { Mark } from "../model/mark.js";
|
|
4
|
+
import { Node } from "../model/node.js";
|
|
5
|
+
import "../model.js";
|
|
6
|
+
import { MarkdownError } from "./errors.js";
|
|
7
|
+
import { schema } from "./schema.js";
|
|
8
|
+
import * as Effect from "effect/Effect";
|
|
9
|
+
import { Env, MarkdownIt, Token } from "markdown-it";
|
|
10
|
+
/**
|
|
11
|
+
* A function handling a markdown-it token while parsing.
|
|
12
|
+
*
|
|
13
|
+
* @since 0.1.0
|
|
14
|
+
* @category models
|
|
15
|
+
*/
|
|
16
|
+
export type TokenHandler<Sc extends AnySchema = AnySchema> = (state: MarkdownParseState<Sc>, token: Token, tokens: Array<Token>, i: number) => void;
|
|
17
|
+
interface StackEntry {
|
|
18
|
+
readonly type: NodeType<any, any>;
|
|
19
|
+
readonly attrs: Attrs | null;
|
|
20
|
+
readonly content: Array<Node<any, any>>;
|
|
21
|
+
marks: ReadonlyArray<Mark<any, any>>;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Object used to track the context of a running parse.
|
|
25
|
+
*
|
|
26
|
+
* @since 0.1.0
|
|
27
|
+
* @category models
|
|
28
|
+
*/
|
|
29
|
+
export declare class MarkdownParseState<Sc extends AnySchema = AnySchema> {
|
|
30
|
+
/** The schema being parsed into. */
|
|
31
|
+
readonly schema: Sc;
|
|
32
|
+
/** @internal */
|
|
33
|
+
readonly tokenHandlers: {
|
|
34
|
+
readonly [token: string]: TokenHandler<Sc>;
|
|
35
|
+
};
|
|
36
|
+
/** @internal */
|
|
37
|
+
readonly stack: Array<StackEntry>;
|
|
38
|
+
/** @internal */
|
|
39
|
+
constructor(
|
|
40
|
+
/** The schema being parsed into. */
|
|
41
|
+
schema: Sc,
|
|
42
|
+
/** @internal */
|
|
43
|
+
tokenHandlers: {
|
|
44
|
+
readonly [token: string]: TokenHandler<Sc>;
|
|
45
|
+
});
|
|
46
|
+
/** @internal */
|
|
47
|
+
top(): StackEntry;
|
|
48
|
+
/** @internal */
|
|
49
|
+
push(elt: Node<any, any>): void;
|
|
50
|
+
/**
|
|
51
|
+
* Adds the given text to the current position in the document, using
|
|
52
|
+
* the current marks as styling.
|
|
53
|
+
*/
|
|
54
|
+
addText(text: string): void;
|
|
55
|
+
/** Adds the given mark to the set of active marks. */
|
|
56
|
+
openMark(mark: Mark<Sc, any>): void;
|
|
57
|
+
/** Removes the given mark from the set of active marks. */
|
|
58
|
+
closeMark(mark: MarkType<Sc, any>): void;
|
|
59
|
+
/** Run the token handlers for the given tokens. Throws a `MarkdownError` for unsupported tokens. */
|
|
60
|
+
parseTokens(toks: Array<Token>): void;
|
|
61
|
+
/**
|
|
62
|
+
* Add a node at the current position. Returns `null` (adding nothing)
|
|
63
|
+
* when the content doesn't fit the node type.
|
|
64
|
+
*/
|
|
65
|
+
addNode(type: NodeType<Sc, any>, attrs: Attrs | null, content?: ReadonlyArray<Node<Sc, any>>): Node<Sc, any> | null;
|
|
66
|
+
/** Wrap subsequent content in a node of the given type. */
|
|
67
|
+
openNode(type: NodeType<Sc, any>, attrs: Attrs | null): void;
|
|
68
|
+
/** Close and return the node that is currently on top of the stack. */
|
|
69
|
+
closeNode(): Node<Sc, any> | null;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Object type used to specify how Markdown tokens should be parsed.
|
|
73
|
+
*
|
|
74
|
+
* @since 0.1.0
|
|
75
|
+
* @category models
|
|
76
|
+
*/
|
|
77
|
+
export interface ParseSpec<Sc extends AnySchema = AnySchema> {
|
|
78
|
+
/**
|
|
79
|
+
* This token maps to a single node, whose type can be looked up in the
|
|
80
|
+
* schema under the given name. Exactly one of `node`, `block`, or
|
|
81
|
+
* `mark` must be set.
|
|
82
|
+
*/
|
|
83
|
+
readonly node?: NodeNames<Sc> | undefined;
|
|
84
|
+
/**
|
|
85
|
+
* This token (unless `noCloseToken` is true) comes in `_open` and
|
|
86
|
+
* `_close` variants (which are appended to the base token name
|
|
87
|
+
* provides a the object property), and wraps a block of content. The
|
|
88
|
+
* block should be wrapped in a node of the type named to by the
|
|
89
|
+
* property's value. If the token does not have `_open` or `_close`,
|
|
90
|
+
* use the `noCloseToken` option.
|
|
91
|
+
*/
|
|
92
|
+
readonly block?: NodeNames<Sc> | undefined;
|
|
93
|
+
/**
|
|
94
|
+
* This token (again, unless `noCloseToken` is true) also comes in
|
|
95
|
+
* `_open` and `_close` variants, but should add a mark (named by the
|
|
96
|
+
* value) to its content, rather than wrapping it in a node.
|
|
97
|
+
*/
|
|
98
|
+
readonly mark?: MarkNames<Sc> | undefined;
|
|
99
|
+
/** Attributes for the node or mark. When `getAttrs` is provided, it takes precedence. */
|
|
100
|
+
readonly attrs?: Attrs | null | undefined;
|
|
101
|
+
/**
|
|
102
|
+
* A function used to compute the attributes for the node or mark that
|
|
103
|
+
* takes a markdown-it token and returns an attribute object.
|
|
104
|
+
*/
|
|
105
|
+
readonly getAttrs?: ((token: Token, tokenStream: Array<Token>, index: number) => Attrs | null) | undefined;
|
|
106
|
+
/**
|
|
107
|
+
* Indicates that the markdown-it token has no `_open` or `_close` for
|
|
108
|
+
* the nodes. This defaults to `true` for `code_inline`, `code_block`
|
|
109
|
+
* and `fence`.
|
|
110
|
+
*/
|
|
111
|
+
readonly noCloseToken?: boolean | undefined;
|
|
112
|
+
/** When true, ignore content for the matched token. */
|
|
113
|
+
readonly ignore?: boolean | undefined;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* A configuration of a Markdown parser. Such a parser uses
|
|
117
|
+
* [markdown-it](https://github.com/markdown-it/markdown-it) to tokenize
|
|
118
|
+
* a file, and then runs the custom rules it is given over the tokens to
|
|
119
|
+
* create a document tree.
|
|
120
|
+
*
|
|
121
|
+
* @since 0.1.0
|
|
122
|
+
* @category models
|
|
123
|
+
*/
|
|
124
|
+
export declare class MarkdownParser<Sc extends AnySchema = AnySchema> {
|
|
125
|
+
/** The parser's document schema. */
|
|
126
|
+
readonly schema: Sc;
|
|
127
|
+
/** This parser's markdown-it tokenizer. */
|
|
128
|
+
readonly tokenizer: MarkdownIt;
|
|
129
|
+
/**
|
|
130
|
+
* The value of the `tokens` object used to construct this parser.
|
|
131
|
+
* Can be useful to copy and modify to base other parsers on.
|
|
132
|
+
*/
|
|
133
|
+
readonly tokens: {
|
|
134
|
+
readonly [name: string]: ParseSpec<Sc>;
|
|
135
|
+
};
|
|
136
|
+
/** @internal */
|
|
137
|
+
readonly tokenHandlers: {
|
|
138
|
+
readonly [token: string]: TokenHandler<Sc>;
|
|
139
|
+
};
|
|
140
|
+
/**
|
|
141
|
+
* Create a parser with the given configuration. You can configure the
|
|
142
|
+
* markdown-it parser to parse the dialect you want, and provide a
|
|
143
|
+
* description of the entities those tokens map to in the `tokens`
|
|
144
|
+
* object, which maps token names to descriptions of what to do with
|
|
145
|
+
* them.
|
|
146
|
+
*
|
|
147
|
+
* Throws a `MarkdownError` for invalid parsing specs, and a
|
|
148
|
+
* `ContentError` for unknown node types.
|
|
149
|
+
*/
|
|
150
|
+
constructor(
|
|
151
|
+
/** The parser's document schema. */
|
|
152
|
+
schema: Sc,
|
|
153
|
+
/** This parser's markdown-it tokenizer. */
|
|
154
|
+
tokenizer: MarkdownIt,
|
|
155
|
+
/**
|
|
156
|
+
* The value of the `tokens` object used to construct this parser.
|
|
157
|
+
* Can be useful to copy and modify to base other parsers on.
|
|
158
|
+
*/
|
|
159
|
+
tokens: {
|
|
160
|
+
readonly [name: string]: ParseSpec<Sc>;
|
|
161
|
+
});
|
|
162
|
+
/**
|
|
163
|
+
* Parse a string as [CommonMark](http://commonmark.org/) markup, and
|
|
164
|
+
* create a document as prescribed by this parser's rules.
|
|
165
|
+
*
|
|
166
|
+
* The second argument, when given, is passed through to the
|
|
167
|
+
* [Markdown parser](https://markdown-it.github.io/markdown-it/#MarkdownIt.parse).
|
|
168
|
+
*
|
|
169
|
+
* Fails with a `MarkdownError` when the tokenizer produces a token
|
|
170
|
+
* the parser has no rule for, and with a `ModelError` when a rule
|
|
171
|
+
* produces invalid attributes.
|
|
172
|
+
*/
|
|
173
|
+
parse(text: string, markdownEnv?: Env): Effect.Effect<Node<Sc>, MarkdownError | ModelError>;
|
|
174
|
+
/** Like `parse`, but throws a `MarkdownError` or `ModelError` on failure. */
|
|
175
|
+
parseUnsafe(text: string, markdownEnv?: Env): Node<Sc>;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* A parser parsing unextended [CommonMark](http://commonmark.org/),
|
|
179
|
+
* without inline HTML, and producing a document in the Markdown schema.
|
|
180
|
+
*
|
|
181
|
+
* @since 0.1.0
|
|
182
|
+
* @category parsers
|
|
183
|
+
*/
|
|
184
|
+
export declare const defaultMarkdownParser: MarkdownParser<typeof schema>;
|
|
185
|
+
export {};
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { attempt } from "../internal/attempt.js";
|
|
2
|
+
import { showJson } from "../internal/json.js";
|
|
3
|
+
import { isModelError } from "../model/errors.js";
|
|
4
|
+
import { Mark } from "../model/mark.js";
|
|
5
|
+
import { MarkdownError, isMarkdownError } from "./errors.js";
|
|
6
|
+
import { schema } from "./schema.js";
|
|
7
|
+
import * as Predicate from "effect/Predicate";
|
|
8
|
+
import MarkdownIt$1 from "markdown-it";
|
|
9
|
+
// src/markdown/from_markdown.ts
|
|
10
|
+
function maybeMerge(a, b) {
|
|
11
|
+
if (a.isText && b.isText && Mark.sameSet(a.marks, b.marks)) return a.withText(a.text + b.text);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Object used to track the context of a running parse.
|
|
15
|
+
*
|
|
16
|
+
* @since 0.1.0
|
|
17
|
+
* @category models
|
|
18
|
+
*/
|
|
19
|
+
var MarkdownParseState = class {
|
|
20
|
+
/** @internal */
|
|
21
|
+
constructor(schema, tokenHandlers) {
|
|
22
|
+
this.schema = schema;
|
|
23
|
+
this.tokenHandlers = tokenHandlers;
|
|
24
|
+
this.stack = [{
|
|
25
|
+
type: schema.topNodeType,
|
|
26
|
+
attrs: null,
|
|
27
|
+
content: [],
|
|
28
|
+
marks: Mark.none
|
|
29
|
+
}];
|
|
30
|
+
}
|
|
31
|
+
/** @internal */
|
|
32
|
+
top() {
|
|
33
|
+
return this.stack[this.stack.length - 1];
|
|
34
|
+
}
|
|
35
|
+
/** @internal */
|
|
36
|
+
push(elt) {
|
|
37
|
+
if (this.stack.length) this.top().content.push(elt);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Adds the given text to the current position in the document, using
|
|
41
|
+
* the current marks as styling.
|
|
42
|
+
*/
|
|
43
|
+
addText(text) {
|
|
44
|
+
if (!text) return;
|
|
45
|
+
const top = this.top(), nodes = top.content, last = nodes[nodes.length - 1];
|
|
46
|
+
const node = this.schema.textUnsafe(text, top.marks);
|
|
47
|
+
const merged = last && maybeMerge(last, node);
|
|
48
|
+
if (merged) nodes[nodes.length - 1] = merged;
|
|
49
|
+
else nodes.push(node);
|
|
50
|
+
}
|
|
51
|
+
/** Adds the given mark to the set of active marks. */
|
|
52
|
+
openMark(mark) {
|
|
53
|
+
const top = this.top();
|
|
54
|
+
top.marks = mark.addToSet(top.marks);
|
|
55
|
+
}
|
|
56
|
+
/** Removes the given mark from the set of active marks. */
|
|
57
|
+
closeMark(mark) {
|
|
58
|
+
const top = this.top();
|
|
59
|
+
top.marks = mark.removeFromSet(top.marks);
|
|
60
|
+
}
|
|
61
|
+
/** Run the token handlers for the given tokens. Throws a `MarkdownError` for unsupported tokens. */
|
|
62
|
+
parseTokens(toks) {
|
|
63
|
+
for (let i = 0; i < toks.length; i++) {
|
|
64
|
+
const tok = toks[i];
|
|
65
|
+
const handler = this.tokenHandlers[tok.type];
|
|
66
|
+
if (!handler) throw new MarkdownError({ message: "Token type `" + tok.type + "` not supported by Markdown parser" });
|
|
67
|
+
handler(this, tok, toks, i);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Add a node at the current position. Returns `null` (adding nothing)
|
|
72
|
+
* when the content doesn't fit the node type.
|
|
73
|
+
*/
|
|
74
|
+
addNode(type, attrs, content) {
|
|
75
|
+
const top = this.top();
|
|
76
|
+
const node = type.createAndFillOrNull(attrs, content, top ? top.marks : []);
|
|
77
|
+
if (!node) return null;
|
|
78
|
+
this.push(node);
|
|
79
|
+
return node;
|
|
80
|
+
}
|
|
81
|
+
/** Wrap subsequent content in a node of the given type. */
|
|
82
|
+
openNode(type, attrs) {
|
|
83
|
+
this.stack.push({
|
|
84
|
+
type,
|
|
85
|
+
attrs,
|
|
86
|
+
content: [],
|
|
87
|
+
marks: Mark.none
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
/** Close and return the node that is currently on top of the stack. */
|
|
91
|
+
closeNode() {
|
|
92
|
+
const info = this.stack.pop();
|
|
93
|
+
return this.addNode(info.type, info.attrs, info.content);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
function attrs(spec, token, tokens, i) {
|
|
97
|
+
if (spec.getAttrs) return spec.getAttrs(token, tokens, i);
|
|
98
|
+
else if (Predicate.isFunction(spec.attrs)) return spec.attrs(token);
|
|
99
|
+
else return spec.attrs ?? null;
|
|
100
|
+
}
|
|
101
|
+
function noCloseToken(spec, type) {
|
|
102
|
+
return spec.noCloseToken || type == "code_inline" || type == "code_block" || type == "fence";
|
|
103
|
+
}
|
|
104
|
+
function withoutTrailingNewline(str) {
|
|
105
|
+
return str[str.length - 1] == "\n" ? str.slice(0, str.length - 1) : str;
|
|
106
|
+
}
|
|
107
|
+
function noOp() {}
|
|
108
|
+
function tokenHandlers(schema, tokens) {
|
|
109
|
+
const handlers = Object.create(null);
|
|
110
|
+
for (const type in tokens) {
|
|
111
|
+
const spec = tokens[type];
|
|
112
|
+
if (spec.block) {
|
|
113
|
+
const nodeType = schema.nodeType(spec.block);
|
|
114
|
+
if (noCloseToken(spec, type)) handlers[type] = (state, tok, tokens, i) => {
|
|
115
|
+
state.openNode(nodeType, attrs(spec, tok, tokens, i));
|
|
116
|
+
state.addText(withoutTrailingNewline(tok.content));
|
|
117
|
+
state.closeNode();
|
|
118
|
+
};
|
|
119
|
+
else {
|
|
120
|
+
handlers[type + "_open"] = (state, tok, tokens, i) => state.openNode(nodeType, attrs(spec, tok, tokens, i));
|
|
121
|
+
handlers[type + "_close"] = (state) => state.closeNode();
|
|
122
|
+
}
|
|
123
|
+
} else if (spec.node) {
|
|
124
|
+
const nodeType = schema.nodeType(spec.node);
|
|
125
|
+
handlers[type] = (state, tok, tokens, i) => state.addNode(nodeType, attrs(spec, tok, tokens, i));
|
|
126
|
+
} else if (spec.mark) {
|
|
127
|
+
const markType = schema.marks[spec.mark];
|
|
128
|
+
if (!markType) throw new MarkdownError({ message: "Unknown mark type: " + spec.mark });
|
|
129
|
+
if (noCloseToken(spec, type)) handlers[type] = (state, tok, tokens, i) => {
|
|
130
|
+
state.openMark(markType.createUnsafe(attrs(spec, tok, tokens, i) ?? void 0));
|
|
131
|
+
state.addText(withoutTrailingNewline(tok.content));
|
|
132
|
+
state.closeMark(markType);
|
|
133
|
+
};
|
|
134
|
+
else {
|
|
135
|
+
handlers[type + "_open"] = (state, tok, tokens, i) => state.openMark(markType.createUnsafe(attrs(spec, tok, tokens, i) ?? void 0));
|
|
136
|
+
handlers[type + "_close"] = (state) => state.closeMark(markType);
|
|
137
|
+
}
|
|
138
|
+
} else if (spec.ignore) {
|
|
139
|
+
if (noCloseToken(spec, type)) handlers[type] = noOp;
|
|
140
|
+
else {
|
|
141
|
+
handlers[type + "_open"] = noOp;
|
|
142
|
+
handlers[type + "_close"] = noOp;
|
|
143
|
+
}
|
|
144
|
+
} else throw new MarkdownError({ message: "Unrecognized parsing spec " + showJson(spec) });
|
|
145
|
+
}
|
|
146
|
+
handlers.text = (state, tok) => state.addText(tok.content);
|
|
147
|
+
handlers.inline = (state, tok) => state.parseTokens(tok.children);
|
|
148
|
+
handlers.softbreak = handlers.softbreak || ((state) => state.addText(" "));
|
|
149
|
+
return handlers;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* A configuration of a Markdown parser. Such a parser uses
|
|
153
|
+
* [markdown-it](https://github.com/markdown-it/markdown-it) to tokenize
|
|
154
|
+
* a file, and then runs the custom rules it is given over the tokens to
|
|
155
|
+
* create a document tree.
|
|
156
|
+
*
|
|
157
|
+
* @since 0.1.0
|
|
158
|
+
* @category models
|
|
159
|
+
*/
|
|
160
|
+
var MarkdownParser = class {
|
|
161
|
+
/**
|
|
162
|
+
* Create a parser with the given configuration. You can configure the
|
|
163
|
+
* markdown-it parser to parse the dialect you want, and provide a
|
|
164
|
+
* description of the entities those tokens map to in the `tokens`
|
|
165
|
+
* object, which maps token names to descriptions of what to do with
|
|
166
|
+
* them.
|
|
167
|
+
*
|
|
168
|
+
* Throws a `MarkdownError` for invalid parsing specs, and a
|
|
169
|
+
* `ContentError` for unknown node types.
|
|
170
|
+
*/
|
|
171
|
+
constructor(schema, tokenizer, tokens) {
|
|
172
|
+
this.schema = schema;
|
|
173
|
+
this.tokenizer = tokenizer;
|
|
174
|
+
this.tokens = tokens;
|
|
175
|
+
this.tokenHandlers = tokenHandlers(schema, tokens);
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Parse a string as [CommonMark](http://commonmark.org/) markup, and
|
|
179
|
+
* create a document as prescribed by this parser's rules.
|
|
180
|
+
*
|
|
181
|
+
* The second argument, when given, is passed through to the
|
|
182
|
+
* [Markdown parser](https://markdown-it.github.io/markdown-it/#MarkdownIt.parse).
|
|
183
|
+
*
|
|
184
|
+
* Fails with a `MarkdownError` when the tokenizer produces a token
|
|
185
|
+
* the parser has no rule for, and with a `ModelError` when a rule
|
|
186
|
+
* produces invalid attributes.
|
|
187
|
+
*/
|
|
188
|
+
parse(text, markdownEnv = {}) {
|
|
189
|
+
return attempt(() => this.parseUnsafe(text, markdownEnv), (u) => isMarkdownError(u) || isModelError(u));
|
|
190
|
+
}
|
|
191
|
+
/** Like `parse`, but throws a `MarkdownError` or `ModelError` on failure. */
|
|
192
|
+
parseUnsafe(text, markdownEnv = {}) {
|
|
193
|
+
const state = new MarkdownParseState(this.schema, this.tokenHandlers);
|
|
194
|
+
let doc;
|
|
195
|
+
state.parseTokens(this.tokenizer.parse(text, markdownEnv));
|
|
196
|
+
do
|
|
197
|
+
doc = state.closeNode();
|
|
198
|
+
while (state.stack.length);
|
|
199
|
+
return doc || this.schema.topNodeType.createAndFillOrNull();
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
function listIsTight(tokens, i) {
|
|
203
|
+
while (++i < tokens.length) if (tokens[i].type != "list_item_open") return tokens[i].hidden;
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
const attrString = (value) => value == null ? null : String(value);
|
|
207
|
+
/**
|
|
208
|
+
* A parser parsing unextended [CommonMark](http://commonmark.org/),
|
|
209
|
+
* without inline HTML, and producing a document in the Markdown schema.
|
|
210
|
+
*
|
|
211
|
+
* @since 0.1.0
|
|
212
|
+
* @category parsers
|
|
213
|
+
*/
|
|
214
|
+
const defaultMarkdownParser = new MarkdownParser(schema, new MarkdownIt$1("commonmark", { html: false }), {
|
|
215
|
+
blockquote: { block: "blockquote" },
|
|
216
|
+
paragraph: { block: "paragraph" },
|
|
217
|
+
list_item: { block: "list_item" },
|
|
218
|
+
bullet_list: {
|
|
219
|
+
block: "bullet_list",
|
|
220
|
+
getAttrs: (_, tokens, i) => ({ tight: listIsTight(tokens, i) })
|
|
221
|
+
},
|
|
222
|
+
ordered_list: {
|
|
223
|
+
block: "ordered_list",
|
|
224
|
+
getAttrs: (tok, tokens, i) => ({
|
|
225
|
+
order: +tok.attrGet("start") || 1,
|
|
226
|
+
tight: listIsTight(tokens, i)
|
|
227
|
+
})
|
|
228
|
+
},
|
|
229
|
+
heading: {
|
|
230
|
+
block: "heading",
|
|
231
|
+
getAttrs: (tok) => ({ level: +tok.tag.slice(1) })
|
|
232
|
+
},
|
|
233
|
+
code_block: {
|
|
234
|
+
block: "code_block",
|
|
235
|
+
noCloseToken: true
|
|
236
|
+
},
|
|
237
|
+
fence: {
|
|
238
|
+
block: "code_block",
|
|
239
|
+
getAttrs: (tok) => ({ params: tok.info || "" }),
|
|
240
|
+
noCloseToken: true
|
|
241
|
+
},
|
|
242
|
+
hr: { node: "horizontal_rule" },
|
|
243
|
+
image: {
|
|
244
|
+
node: "image",
|
|
245
|
+
getAttrs: (tok) => ({
|
|
246
|
+
src: attrString(tok.attrGet("src")),
|
|
247
|
+
title: attrString(tok.attrGet("title")) || null,
|
|
248
|
+
alt: tok.children[0] && tok.children[0].content || null
|
|
249
|
+
})
|
|
250
|
+
},
|
|
251
|
+
hardbreak: { node: "hard_break" },
|
|
252
|
+
em: { mark: "em" },
|
|
253
|
+
strong: { mark: "strong" },
|
|
254
|
+
link: {
|
|
255
|
+
mark: "link",
|
|
256
|
+
getAttrs: (tok) => ({
|
|
257
|
+
href: attrString(tok.attrGet("href")),
|
|
258
|
+
title: attrString(tok.attrGet("title")) || null
|
|
259
|
+
})
|
|
260
|
+
},
|
|
261
|
+
code_inline: {
|
|
262
|
+
mark: "code",
|
|
263
|
+
noCloseToken: true
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
export { MarkdownParseState, MarkdownParser, defaultMarkdownParser };
|