@effected/markdown 0.7.0 → 0.8.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/internal/stringify.js +53 -9
- package/package.json +2 -2
package/internal/stringify.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ENTITY } from "./unescape.js";
|
|
1
2
|
import { GuardExceeded } from "./carriers.js";
|
|
2
3
|
|
|
3
4
|
//#region src/internal/stringify.ts
|
|
@@ -21,15 +22,15 @@ const ALWAYS_ESCAPE = /* @__PURE__ */ new Set([
|
|
|
21
22
|
"\\",
|
|
22
23
|
"`",
|
|
23
24
|
"*",
|
|
24
|
-
"_",
|
|
25
25
|
"[",
|
|
26
26
|
"]",
|
|
27
27
|
"<",
|
|
28
|
-
">",
|
|
29
|
-
"&",
|
|
30
28
|
"~",
|
|
31
29
|
"|"
|
|
32
30
|
]);
|
|
31
|
+
/** A character reference at the head of the string: the engine's own grammar. */
|
|
32
|
+
const reEntityAhead = new RegExp(`^${ENTITY}`, "i");
|
|
33
|
+
const reAlphanumeric = /[\p{L}\p{N}]/u;
|
|
33
34
|
/** Line-start characters that could open a block construct. */
|
|
34
35
|
const LINE_START_ESCAPE = /* @__PURE__ */ new Set([
|
|
35
36
|
"#",
|
|
@@ -68,11 +69,37 @@ const isSchemeColon = (value, index) => {
|
|
|
68
69
|
while (start > 0 && /[a-zA-Z]/.test(value[start - 1])) start -= 1;
|
|
69
70
|
return SCHEME_WORDS.has(value.slice(start, index).toLowerCase());
|
|
70
71
|
};
|
|
72
|
+
/** Whether `value[index]` is a Unicode letter or number (`undefined` is not). */
|
|
73
|
+
const isAlphanumericAt = (value, index) => {
|
|
74
|
+
const char = value[index];
|
|
75
|
+
return char !== void 0 && reAlphanumeric.test(char);
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Whether the `#` at `value[index]` heads a run that would read as an ATX
|
|
79
|
+
* closing sequence: preceded by whitespace or the value start, and followed
|
|
80
|
+
* by nothing but whitespace to the end of the contiguous text (this value
|
|
81
|
+
* plus `followingText`). A non-text sibling after that may contribute only
|
|
82
|
+
* blank content, so it keeps the escape (conservative).
|
|
83
|
+
*/
|
|
84
|
+
const isClosingHashRun = (value, index, followingText, nonTextFollows) => {
|
|
85
|
+
const before = value[index - 1];
|
|
86
|
+
if (before !== void 0 && before !== " " && before !== " ") return false;
|
|
87
|
+
const rest = value.slice(index) + followingText;
|
|
88
|
+
let end = 0;
|
|
89
|
+
while (rest[end] === "#") end += 1;
|
|
90
|
+
return nonTextFollows || /^[ \t\n]*$/.test(rest.slice(end));
|
|
91
|
+
};
|
|
92
|
+
/** A run that a later sibling could still complete into a character reference. */
|
|
93
|
+
const reEntityPrefix = /^&[#a-z0-9]*$/i;
|
|
71
94
|
/**
|
|
72
95
|
* Escape one text value into `out`, tracking line starts. Returns whether the
|
|
73
|
-
* emission ended at a line start.
|
|
96
|
+
* emission ended at a line start. `followingText` is the concatenated value
|
|
97
|
+
* of the contiguous text siblings after this one (the parser never splits a
|
|
98
|
+
* text run, but a synthesized tree may), and `nonTextFollows` says whether a
|
|
99
|
+
* non-text inline comes after those; together they decide the value-end
|
|
100
|
+
* cases (`&`, and `#` in a heading) against what will actually be emitted.
|
|
74
101
|
*/
|
|
75
|
-
const escapeText = (value, context, atLineStart, mdx) => {
|
|
102
|
+
const escapeText = (value, context, atLineStart, mdx, followingText, nonTextFollows) => {
|
|
76
103
|
let out = "";
|
|
77
104
|
let lineStart = atLineStart;
|
|
78
105
|
for (let index = 0; index < value.length; index += 1) {
|
|
@@ -129,7 +156,17 @@ const escapeText = (value, context, atLineStart, mdx) => {
|
|
|
129
156
|
out += "\\{";
|
|
130
157
|
continue;
|
|
131
158
|
}
|
|
132
|
-
if (
|
|
159
|
+
if (char === "_" && !(isAlphanumericAt(value, index - 1) && isAlphanumericAt(value, index + 1))) {
|
|
160
|
+
out += "\\_";
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
if (char === "&") {
|
|
164
|
+
const ahead = value.slice(index) + followingText;
|
|
165
|
+
const entityShaped = reEntityAhead.test(ahead) || nonTextFollows && reEntityPrefix.test(ahead);
|
|
166
|
+
out += entityShaped ? "\\&" : "&";
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
if (context.inHeading && char === "#" && isClosingHashRun(value, index, followingText, nonTextFollows)) {
|
|
133
170
|
out += "\\#";
|
|
134
171
|
continue;
|
|
135
172
|
}
|
|
@@ -218,7 +255,13 @@ const serializeInlines = (children, context, state, startsLine, junctionGuard) =
|
|
|
218
255
|
};
|
|
219
256
|
switch (child.type) {
|
|
220
257
|
case "text": {
|
|
221
|
-
|
|
258
|
+
let followingText = "";
|
|
259
|
+
let next = index + 1;
|
|
260
|
+
while (next < children.length && children[next]?.type === "text") {
|
|
261
|
+
followingText += children[next].value;
|
|
262
|
+
next += 1;
|
|
263
|
+
}
|
|
264
|
+
const escaped = escapeText(child.value, context, atLineStart, state.mdx, followingText, next < children.length);
|
|
222
265
|
out += escaped.text;
|
|
223
266
|
atLineStart = escaped.atLineStart;
|
|
224
267
|
break;
|
|
@@ -443,8 +486,9 @@ const setextUnderline = (heading, content) => {
|
|
|
443
486
|
return (heading.depth === 1 ? "=" : "-").repeat(Math.max(1, lastLine.length));
|
|
444
487
|
};
|
|
445
488
|
const serializeHeading = (heading, state) => {
|
|
446
|
-
const
|
|
447
|
-
|
|
489
|
+
const setext = heading.headingStyle === "setext" && heading.depth <= 2;
|
|
490
|
+
const content = serializeInlines(heading.children, HEADING_CONTEXT, state, setext);
|
|
491
|
+
if (setext && content !== "") return `${content}\n${setextUnderline(heading, content)}`;
|
|
448
492
|
const hashes = "#".repeat(heading.depth);
|
|
449
493
|
return content === "" ? hashes : `${hashes} ${content}`;
|
|
450
494
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effected/markdown",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "CommonMark and GFM markdown parse, edit and transform schemas for Effect",
|
|
6
6
|
"keywords": [
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"./package.json": "./package.json"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
|
-
"@effected/jsonc": "^0.8.
|
|
42
|
+
"@effected/jsonc": "^0.8.1",
|
|
43
43
|
"@effected/toml": "^0.5.0",
|
|
44
44
|
"@effected/yaml": "^0.12.0",
|
|
45
45
|
"effect": "4.0.0-rc.109"
|