@effected/markdown 0.2.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.
Files changed (65) hide show
  1. package/Frontmatter.js +272 -0
  2. package/FrontmatterResolver.js +273 -0
  3. package/JsonFrontmatter.js +46 -0
  4. package/LICENSE +21 -0
  5. package/Markdown.js +251 -0
  6. package/MarkdownDiagnostic.js +85 -0
  7. package/MarkdownDocument.js +248 -0
  8. package/MarkdownEdit.js +52 -0
  9. package/MarkdownFormat.js +380 -0
  10. package/MarkdownNode.js +641 -0
  11. package/MarkdownVisitor.js +88 -0
  12. package/Mdast.js +384 -0
  13. package/README.md +255 -0
  14. package/TomlFrontmatter.js +44 -0
  15. package/YamlFrontmatter.js +45 -0
  16. package/index.d.ts +2190 -0
  17. package/index.js +15 -0
  18. package/internal/blockParser.js +419 -0
  19. package/internal/blockRegistry.js +90 -0
  20. package/internal/blockTypes.js +27 -0
  21. package/internal/blocks/atxHeading.js +54 -0
  22. package/internal/blocks/blockquote.js +40 -0
  23. package/internal/blocks/code.js +90 -0
  24. package/internal/blocks/document.js +17 -0
  25. package/internal/blocks/fencedCode.js +26 -0
  26. package/internal/blocks/footnoteDefinition.js +84 -0
  27. package/internal/blocks/frontmatter.js +56 -0
  28. package/internal/blocks/htmlBlock.js +72 -0
  29. package/internal/blocks/indentedCode.js +16 -0
  30. package/internal/blocks/linkReferenceDefinition.js +72 -0
  31. package/internal/blocks/list.js +159 -0
  32. package/internal/blocks/paragraph.js +27 -0
  33. package/internal/blocks/setextHeading.js +24 -0
  34. package/internal/blocks/table.js +378 -0
  35. package/internal/blocks/taskListItem.js +36 -0
  36. package/internal/blocks/thematicBreak.js +35 -0
  37. package/internal/carriers.js +42 -0
  38. package/internal/entities.js +26 -0
  39. package/internal/entityMap.js +7 -0
  40. package/internal/htmlTags.js +18 -0
  41. package/internal/inlineNode.js +54 -0
  42. package/internal/inlineParser.js +403 -0
  43. package/internal/inlineRegistry.js +68 -0
  44. package/internal/inlines/autolink.js +36 -0
  45. package/internal/inlines/autolinkLiteral.js +374 -0
  46. package/internal/inlines/codeSpan.js +32 -0
  47. package/internal/inlines/emphasis.js +79 -0
  48. package/internal/inlines/entity.js +21 -0
  49. package/internal/inlines/escape.js +34 -0
  50. package/internal/inlines/footnoteReference.js +63 -0
  51. package/internal/inlines/lineBreak.js +25 -0
  52. package/internal/inlines/link.js +212 -0
  53. package/internal/inlines/rawHtml.js +27 -0
  54. package/internal/inlines/strikethrough.js +81 -0
  55. package/internal/inlines/text.js +22 -0
  56. package/internal/lineIndex.js +76 -0
  57. package/internal/patterns.js +26 -0
  58. package/internal/preprocess.js +58 -0
  59. package/internal/rawInline.js +57 -0
  60. package/internal/references.js +160 -0
  61. package/internal/segments.js +36 -0
  62. package/internal/stringify.js +519 -0
  63. package/internal/unescape.js +18 -0
  64. package/package.json +61 -0
  65. package/tsdoc-metadata.json +11 -0
@@ -0,0 +1,160 @@
1
+ import { unescapeString } from "./unescape.js";
2
+ import { stickyOf } from "./patterns.js";
3
+
4
+ //#region src/internal/references.ts
5
+ const ESCAPABLE = "[!\"#$%&'()*+,./:;<=>?@[\\\\\\]^_`{|}~-]";
6
+ const ESCAPED_CHAR = `\\\\${ESCAPABLE}`;
7
+ const reLinkTitle = new RegExp(`^(?:"(${ESCAPED_CHAR}|\\\\[^\\\\]|[^\\\\"\\x00])*"|'(${ESCAPED_CHAR}|\\\\[^\\\\]|[^\\\\'\\x00])*'|\\((${ESCAPED_CHAR}|\\\\[^\\\\]|[^\\\\()\\x00])*\\))`);
8
+ const reLinkDestinationBraces = /^(?:<(?:[^<>\n\\\0]|\\.)*>)/;
9
+ const reEscapable = new RegExp(`^${ESCAPABLE}`);
10
+ const reSpnl = /^ *(?:\n *)?/;
11
+ const reSpaceAtEndOfLine = /^ *(?:\n|$)/;
12
+ const reLinkLabel = /^\[(?:[^\\[\]]|\\.){0,1000}\]/s;
13
+ /**
14
+ * The whitespace set a link destination may not contain, as codes.
15
+ *
16
+ * The destination walk below tests every character it passes. Upstream spells
17
+ * that test `reWhitespaceChar.exec(fromCodePoint(c))`, which allocates a
18
+ * string and runs a regex per character — in a scan that is already O(n) per
19
+ * attempt on an unterminated destination, that constant is the difference
20
+ * between seconds and minutes.
21
+ */
22
+ const isWhitespaceCode = (code) => code === 32 || code === 9 || code === 10 || code === 11 || code === 12 || code === 13;
23
+ const C_BACKSLASH = 92;
24
+ const C_COLON = 58;
25
+ const C_LESSTHAN = 60;
26
+ const C_OPEN_PAREN = 40;
27
+ const C_CLOSE_PAREN = 41;
28
+ /**
29
+ * A cursor over a subject string, with the handful of primitives upstream's
30
+ * inline parser is built from.
31
+ */
32
+ var ReferenceScanner = class {
33
+ subject;
34
+ pos = 0;
35
+ constructor(subject) {
36
+ this.subject = subject;
37
+ }
38
+ /** The char code at the cursor, or `-1` at the end of the subject. */
39
+ peek() {
40
+ return this.pos < this.subject.length ? this.subject.charCodeAt(this.pos) : -1;
41
+ }
42
+ /**
43
+ * Match `pattern` at the cursor, advancing past it on success.
44
+ *
45
+ * Sticky rather than upstream's slice-then-match: every pattern here is
46
+ * anchored, and slicing the subject per attempt is quadratic on a large
47
+ * one (`patterns.ts`).
48
+ */
49
+ match(pattern) {
50
+ const sticky = stickyOf(pattern);
51
+ sticky.lastIndex = this.pos;
52
+ const found = sticky.exec(this.subject);
53
+ if (found === null) return;
54
+ this.pos = sticky.lastIndex;
55
+ return found[0];
56
+ }
57
+ /** Consume optional spaces and up to one line ending. */
58
+ spnl() {
59
+ this.match(reSpnl);
60
+ }
61
+ /** The length of the link label at the cursor, or `0` if there is none. */
62
+ parseLinkLabel() {
63
+ const found = this.match(reLinkLabel);
64
+ return found === void 0 || found.length > 1001 ? 0 : found.length;
65
+ }
66
+ /**
67
+ * The link destination at the cursor, unescaped, or `undefined` if there
68
+ * is none. Never percent-encoded (see the port notes).
69
+ */
70
+ parseLinkDestination() {
71
+ const braced = this.match(reLinkDestinationBraces);
72
+ if (braced !== void 0) return unescapeString(braced.slice(1, -1));
73
+ if (this.peek() === C_LESSTHAN) return;
74
+ const start = this.pos;
75
+ let openParens = 0;
76
+ let code = this.peek();
77
+ while (code !== -1) {
78
+ if (code === C_BACKSLASH && reEscapable.test(this.subject.charAt(this.pos + 1))) {
79
+ this.pos += 1;
80
+ if (this.peek() !== -1) this.pos += 1;
81
+ } else if (code === C_OPEN_PAREN) {
82
+ this.pos += 1;
83
+ openParens += 1;
84
+ } else if (code === C_CLOSE_PAREN) {
85
+ if (openParens < 1) break;
86
+ this.pos += 1;
87
+ openParens -= 1;
88
+ } else if (isWhitespaceCode(code)) break;
89
+ else this.pos += 1;
90
+ code = this.peek();
91
+ }
92
+ if (this.pos === start && code !== C_CLOSE_PAREN) return;
93
+ if (openParens !== 0) return;
94
+ return unescapeString(this.subject.slice(start, this.pos));
95
+ }
96
+ /** The link title at the cursor, quotes stripped and unescaped. */
97
+ parseLinkTitle() {
98
+ const title = this.match(reLinkTitle);
99
+ return title === void 0 ? void 0 : unescapeString(title.slice(1, -1));
100
+ }
101
+ };
102
+ /**
103
+ * Case-fold a bare label: trim, collapse internal whitespace, fold case.
104
+ *
105
+ * The `toLowerCase().toUpperCase()` pair is upstream's Unicode case-folding
106
+ * trick, not redundancy — it is what makes `ẞ` and `ß` the same label.
107
+ *
108
+ * Split out of {@link normalizeReference} because GFM footnote labels are the
109
+ * same fold applied to a label that never carried brackets to strip: this is
110
+ * cmark-gfm's `normalize_map_label`, which its footnote map and its link
111
+ * refmap both call.
112
+ */
113
+ const normalizeLabelText = (label) => label.trim().replace(/[ \t\r\n]+/g, " ").toLowerCase().toUpperCase();
114
+ /**
115
+ * commonmark.js `normalizeReference`: strip the brackets, then case-fold what
116
+ * is left.
117
+ */
118
+ const normalizeReference = (rawLabel) => normalizeLabelText(rawLabel.slice(1, rawLabel.length - 1));
119
+ /**
120
+ * Parse a link reference definition at the start of `text`.
121
+ *
122
+ * Returns `undefined` when `text` does not begin with one — which is not an
123
+ * error: the text is simply paragraph content.
124
+ */
125
+ const parseReference = (text) => {
126
+ const scanner = new ReferenceScanner(text);
127
+ const labelLength = scanner.parseLinkLabel();
128
+ if (labelLength === 0) return;
129
+ const rawLabel = text.slice(0, labelLength);
130
+ if (scanner.peek() !== C_COLON) return;
131
+ scanner.pos += 1;
132
+ scanner.spnl();
133
+ const url = scanner.parseLinkDestination();
134
+ if (url === void 0) return;
135
+ const beforeTitle = scanner.pos;
136
+ scanner.spnl();
137
+ let title = scanner.pos === beforeTitle ? void 0 : scanner.parseLinkTitle();
138
+ if (title === void 0) scanner.pos = beforeTitle;
139
+ let atLineEnd = true;
140
+ if (scanner.match(reSpaceAtEndOfLine) === void 0) if (title === void 0) atLineEnd = false;
141
+ else {
142
+ title = void 0;
143
+ scanner.pos = beforeTitle;
144
+ atLineEnd = scanner.match(reSpaceAtEndOfLine) !== void 0;
145
+ }
146
+ if (!atLineEnd) return;
147
+ const key = normalizeReference(rawLabel);
148
+ if (key === "") return;
149
+ return {
150
+ key,
151
+ identifier: key.toLowerCase(),
152
+ label: rawLabel.slice(1, -1),
153
+ url,
154
+ ...title === void 0 ? {} : { title },
155
+ length: scanner.pos
156
+ };
157
+ };
158
+
159
+ //#endregion
160
+ export { ReferenceScanner, normalizeLabelText, normalizeReference, parseReference };
@@ -0,0 +1,36 @@
1
+ //#region src/internal/segments.ts
2
+ /**
3
+ * The absolute source offset of `textIndex` within a segmented content run.
4
+ *
5
+ * An index that falls between segments resolves to the end of the segment
6
+ * before it, which is the closest real source position there is.
7
+ */
8
+ const sourceOffsetAt = (segments, textIndex, fallback) => {
9
+ let offset = fallback;
10
+ for (const segment of segments) {
11
+ if (textIndex < segment.textOffset) return offset;
12
+ if (textIndex < segment.textOffset + segment.length) return segment.sourceOffset + (textIndex - segment.textOffset);
13
+ offset = segment.sourceOffset + segment.length;
14
+ }
15
+ return offset;
16
+ };
17
+ /**
18
+ * Cut `[from, to)` out of a segmented content run, keeping the provenance of
19
+ * every character that survives.
20
+ */
21
+ const sliceWithSegments = (segments, from, to) => {
22
+ const sliced = [];
23
+ for (const segment of segments) {
24
+ const start = Math.max(segment.textOffset, from);
25
+ const end = Math.min(segment.textOffset + segment.length, to);
26
+ if (end > start) sliced.push({
27
+ textOffset: start - from,
28
+ sourceOffset: segment.sourceOffset + (start - segment.textOffset),
29
+ length: end - start
30
+ });
31
+ }
32
+ return sliced;
33
+ };
34
+
35
+ //#endregion
36
+ export { sliceWithSegments, sourceOffsetAt };