@markmap/parse 1.1.0 → 1.1.1

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/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from './parse';
2
2
  export * from './to-tree';
3
+ export * from './mark';
3
4
  export * from './plugins';
4
5
  export * from './html-image';
package/dist/index.js CHANGED
@@ -3,6 +3,9 @@ import remarkGfm from "remark-gfm";
3
3
  import remarkParse from "remark-parse";
4
4
  import { unified } from "unified";
5
5
  import { parse as parse$1 } from "yaml";
6
+ import { splice } from "micromark-util-chunked";
7
+ import { classifyCharacter } from "micromark-util-classify-character";
8
+ import { resolveAll } from "micromark-util-resolve-all";
6
9
  import { assignIds } from "@markmap/core";
7
10
  // src/html-image.ts
8
11
  var TAG = /^<img((?:\s+[a-z][a-z0-9-]*(?:\s*=\s*(?:"[^"<>]*"|'[^'<>]*'|[^\s"'=<>`]+))?)*)\s*\/?>$/i;
@@ -91,95 +94,155 @@ function remarkHtmlImages() {
91
94
  };
92
95
  return (tree) => visit(tree);
93
96
  }
94
- // src/plugins.ts
95
- function visitParents(node, fn) {
96
- if (!("children" in node)) return;
97
- for (const child of node.children) visitParents(child, fn);
98
- fn(node);
99
- }
97
+ // src/mark.ts
98
+ var EQUALS = 61;
99
+ var PUNCTUATION = 2;
100
+ /** Whether each `==` sequence can open or close a mark, from its flanking characters. */
101
+ var sides = /* @__PURE__ */ new WeakMap();
100
102
  /**
101
- * Turn `==text==` into `mark` nodes. Like `**`, the opening `==` must be followed by
102
- * a non-space and the closing one preceded by a non-space. Delimiters can wrap other
103
- * inline nodes (`==a **b**==`) but not cross block boundaries.
103
+ * micromark syntax for `==text==`, modeled on GFM strikethrough: a sequence of exactly two
104
+ * `=` with the same flanking rules as `**`. Because it runs in the tokenizer, escapes
105
+ * (`\=`), character references (`&#61;`) and code spans are never delimiters.
104
106
  */
105
- function remarkMark() {
106
- return (tree) => visitParents(tree, (parent) => {
107
- if (parent.children.some((child) => child.type === "text" && child.value.includes("=="))) parent.children = markChildren(parent.children);
108
- });
107
+ function markSyntax() {
108
+ const construct = {
109
+ name: "mark",
110
+ tokenize: tokenizeMark,
111
+ resolveAll: resolveAllMark
112
+ };
113
+ return {
114
+ text: { [EQUALS]: construct },
115
+ insideSpan: { null: [construct] },
116
+ attentionMarkers: { null: [EQUALS] }
117
+ };
109
118
  }
110
- function markChildren(children) {
111
- const tokens = [];
112
- for (const child of children) {
113
- if (child.type !== "text") {
114
- tokens.push({
115
- delim: false,
116
- node: child
117
- });
118
- continue;
119
+ var resolveAllMark = (events, context) => {
120
+ for (let index = 0; index < events.length; index += 1) {
121
+ const closer = events[index][1];
122
+ if (events[index][0] !== "enter" || closer.type !== "markSequenceTemporary" || !sides.get(closer)?.close) continue;
123
+ for (let open = index - 1; open >= 0; open -= 1) {
124
+ const opener = events[open][1];
125
+ if (events[open][0] !== "exit" || opener.type !== "markSequenceTemporary" || !sides.get(opener)?.open) continue;
126
+ opener.type = "markSequence";
127
+ closer.type = "markSequence";
128
+ const mark = {
129
+ type: "mark",
130
+ start: { ...opener.start },
131
+ end: { ...closer.end }
132
+ };
133
+ const text = {
134
+ type: "markText",
135
+ start: { ...opener.end },
136
+ end: { ...closer.start }
137
+ };
138
+ const next = [
139
+ [
140
+ "enter",
141
+ mark,
142
+ context
143
+ ],
144
+ [
145
+ "enter",
146
+ opener,
147
+ context
148
+ ],
149
+ [
150
+ "exit",
151
+ opener,
152
+ context
153
+ ],
154
+ [
155
+ "enter",
156
+ text,
157
+ context
158
+ ]
159
+ ];
160
+ const insideSpan = context.parser.constructs.insideSpan.null;
161
+ if (insideSpan) splice(next, next.length, 0, resolveAll(insideSpan, events.slice(open + 1, index), context));
162
+ splice(next, next.length, 0, [
163
+ [
164
+ "exit",
165
+ text,
166
+ context
167
+ ],
168
+ [
169
+ "enter",
170
+ closer,
171
+ context
172
+ ],
173
+ [
174
+ "exit",
175
+ closer,
176
+ context
177
+ ],
178
+ [
179
+ "exit",
180
+ mark,
181
+ context
182
+ ]
183
+ ]);
184
+ splice(events, open - 1, index - open + 3, next);
185
+ index = open + next.length - 2;
186
+ break;
119
187
  }
120
- child.value.split(/(==)/).forEach((part) => {
121
- if (part === "==") tokens.push({
122
- delim: true,
123
- canOpen: false,
124
- canClose: false
125
- });
126
- else if (part) tokens.push({
127
- delim: false,
128
- node: {
129
- type: "text",
130
- value: part
131
- }
132
- });
133
- });
134
188
  }
135
- const edgeChar = (token, end) => {
136
- if (!token) return "";
137
- if (token.delim) return "=";
138
- if (token.node.type === "text") return end ? token.node.value.at(-1) : token.node.value[0];
139
- return "x";
140
- };
141
- tokens.forEach((token, i) => {
142
- if (!token.delim) return;
143
- const next = edgeChar(tokens[i + 1], false);
144
- const prev = edgeChar(tokens[i - 1], true);
145
- token.canOpen = !!next && !/\s/.test(next);
146
- token.canClose = !!prev && !/\s/.test(prev);
147
- });
148
- const pairs = /* @__PURE__ */ new Map();
149
- let open = -1;
150
- tokens.forEach((token, i) => {
151
- if (!token.delim) return;
152
- if (open >= 0 && token.canClose && i > open + 1) {
153
- pairs.set(open, i);
154
- open = -1;
155
- } else if (open < 0 && token.canOpen) open = i;
156
- });
157
- const build = (from, to) => {
158
- const result = [];
159
- const pushText = (value) => {
160
- const last = result.at(-1);
161
- if (last?.type === "text") last.value += value;
162
- else result.push({
163
- type: "text",
164
- value
165
- });
166
- };
167
- for (let i = from; i < to; i += 1) {
168
- const token = tokens[i];
169
- const close = pairs.get(i);
170
- if (close != null) {
171
- result.push({
172
- type: "mark",
173
- children: build(i + 1, close)
174
- });
175
- i = close;
176
- } else if (token.delim) pushText("==");
177
- else if (token.node.type === "text") pushText(token.node.value);
178
- else result.push(token.node);
189
+ for (const [, token] of events) if (token.type === "markSequenceTemporary") token.type = "data";
190
+ return events;
191
+ };
192
+ var tokenizeMark = function(effects, ok, nok) {
193
+ const { previous, events } = this;
194
+ let size = 0;
195
+ const more = (code) => {
196
+ if (code === EQUALS) {
197
+ if (size > 1) return nok(code);
198
+ effects.consume(code);
199
+ size += 1;
200
+ return more;
179
201
  }
180
- return result;
202
+ if (size < 2) return nok(code);
203
+ const token = effects.exit("markSequenceTemporary");
204
+ const before = classifyCharacter(previous);
205
+ const after = classifyCharacter(code);
206
+ sides.set(token, {
207
+ open: !after || after === PUNCTUATION && Boolean(before),
208
+ close: !before || before === PUNCTUATION && Boolean(after)
209
+ });
210
+ return ok(code);
211
+ };
212
+ return (code) => {
213
+ if (previous === EQUALS && events.at(-1)?.[1].type !== "characterEscape") return nok(code);
214
+ effects.enter("markSequenceTemporary");
215
+ return more(code);
216
+ };
217
+ };
218
+ function markFromMarkdown() {
219
+ return {
220
+ canContainEols: ["mark"],
221
+ enter: { mark(token) {
222
+ this.enter({
223
+ type: "mark",
224
+ children: []
225
+ }, token);
226
+ } },
227
+ exit: { mark(token) {
228
+ this.exit(token);
229
+ } }
181
230
  };
182
- return build(0, tokens.length);
231
+ }
232
+ /**
233
+ * Turn `==text==` into `mark` nodes. Like `**`, delimiters can wrap other inline nodes
234
+ * (`==a **b**==`) but not cross block boundaries.
235
+ */
236
+ var remarkMark = function() {
237
+ const data = this.data();
238
+ (data.micromarkExtensions ??= []).push(markSyntax());
239
+ (data.fromMarkdownExtensions ??= []).push(markFromMarkdown());
240
+ };
241
+ // src/plugins.ts
242
+ function visitParents(node, fn) {
243
+ if (!("children" in node)) return;
244
+ for (const child of node.children) visitParents(child, fn);
245
+ fn(node);
183
246
  }
184
247
  /** Replace `[text][ref]` and `![alt][ref]` with plain links and images, and drop definitions. */
185
248
  function remarkResolveReferences() {
@@ -382,4 +445,4 @@ function readFrontmatter(mdast) {
382
445
  return {};
383
446
  }
384
447
  }
385
- export { parse, parseHtmlImage, remarkHtmlImages, remarkMark, remarkResolveReferences, toTree };
448
+ export { markFromMarkdown, markSyntax, parse, parseHtmlImage, remarkHtmlImages, remarkMark, remarkResolveReferences, toTree };
package/dist/mark.d.ts ADDED
@@ -0,0 +1,24 @@
1
+ import type { Extension as FromMarkdownExtension } from 'mdast-util-from-markdown';
2
+ import type { Extension } from 'micromark-util-types';
3
+ import type { Root } from 'mdast';
4
+ import type { Plugin } from 'unified';
5
+ declare module 'micromark-util-types' {
6
+ interface TokenTypeMap {
7
+ mark: 'mark';
8
+ markSequence: 'markSequence';
9
+ markSequenceTemporary: 'markSequenceTemporary';
10
+ markText: 'markText';
11
+ }
12
+ }
13
+ /**
14
+ * micromark syntax for `==text==`, modeled on GFM strikethrough: a sequence of exactly two
15
+ * `=` with the same flanking rules as `**`. Because it runs in the tokenizer, escapes
16
+ * (`\=`), character references (`&#61;`) and code spans are never delimiters.
17
+ */
18
+ export declare function markSyntax(): Extension;
19
+ export declare function markFromMarkdown(): FromMarkdownExtension;
20
+ /**
21
+ * Turn `==text==` into `mark` nodes. Like `**`, delimiters can wrap other inline nodes
22
+ * (`==a **b**==`) but not cross block boundaries.
23
+ */
24
+ export declare const remarkMark: Plugin<[], Root>;
package/dist/plugins.d.ts CHANGED
@@ -1,9 +1,3 @@
1
1
  import type { Root } from 'mdast';
2
- /**
3
- * Turn `==text==` into `mark` nodes. Like `**`, the opening `==` must be followed by
4
- * a non-space and the closing one preceded by a non-space. Delimiters can wrap other
5
- * inline nodes (`==a **b**==`) but not cross block boundaries.
6
- */
7
- export declare function remarkMark(): (tree: Root) => void;
8
2
  /** Replace `[text][ref]` and `![alt][ref]` with plain links and images, and drop definitions. */
9
3
  export declare function remarkResolveReferences(): (tree: Root) => void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markmap/parse",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "Parse Markdown into a Markmap tree",
5
5
  "homepage": "https://github.com/markmap/markmap#readme",
6
6
  "license": "MIT",
@@ -24,7 +24,10 @@
24
24
  "access": "public"
25
25
  },
26
26
  "dependencies": {
27
- "@markmap/core": "1.1.0",
27
+ "@markmap/core": "1.1.1",
28
+ "micromark-util-chunked": "^2.0.1",
29
+ "micromark-util-classify-character": "^2.0.1",
30
+ "micromark-util-resolve-all": "^2.0.1",
28
31
  "remark-frontmatter": "^5.0.0",
29
32
  "remark-gfm": "^4.0.1",
30
33
  "remark-parse": "^11.0.0",
@@ -32,7 +35,9 @@
32
35
  "yaml": "^2.9.1"
33
36
  },
34
37
  "devDependencies": {
35
- "@types/mdast": "^4.0.4"
38
+ "@types/mdast": "^4.0.4",
39
+ "mdast-util-from-markdown": "^2.1.0",
40
+ "micromark-util-types": "^2.0.3"
36
41
  },
37
42
  "scripts": {
38
43
  "clean": "rm -rf dist tsconfig.tsbuildinfo",