@effected/markdown 0.14.0 → 0.15.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/Frontmatter.js +22 -9
- package/FrontmatterResolver.js +2 -4
- package/FrontmatterSource.js +0 -15
- package/Markdown.js +2 -2
- package/MarkdownDiagnostic.js +10 -7
- package/MarkdownDocument.js +19 -10
- package/MarkdownEdit.js +4 -0
- package/MarkdownFormat.js +55 -5
- package/MarkdownNode.js +5 -5
- package/MarkdownVisitor.js +22 -1
- package/Mdast.js +37 -30
- package/README.md +4 -4
- package/TomlFrontmatter.js +2 -2
- package/index.d.ts +180 -50
- package/internal/carriers.js +2 -2
- package/package.json +5 -5
package/Frontmatter.js
CHANGED
|
@@ -115,9 +115,8 @@ reason: FrontmatterMissingReason }) {
|
|
|
115
115
|
* @remarks
|
|
116
116
|
* `issue` carries the **structured** schema failure — at runtime a
|
|
117
117
|
* `SchemaIssue.Issue` tree, reachable through `_tag` and nested `issues` —
|
|
118
|
-
* never a stringified rendering
|
|
119
|
-
*
|
|
120
|
-
* `Schema` for `Issue`; narrow it with the `SchemaIssue` module.
|
|
118
|
+
* never a stringified rendering. It is typed `unknown` because core exposes
|
|
119
|
+
* no `Schema` for `Issue`; narrow it with the `SchemaIssue` module.
|
|
121
120
|
*
|
|
122
121
|
* @public
|
|
123
122
|
*/
|
|
@@ -159,13 +158,27 @@ const renderBlock = (format, body) => {
|
|
|
159
158
|
return `${open}\n${body === "" || body.endsWith("\n") ? body : `${body}\n`}${close}`;
|
|
160
159
|
};
|
|
161
160
|
/**
|
|
162
|
-
* The frontmatter schema composition facade
|
|
161
|
+
* The frontmatter schema composition facade: decodes a parsed document's frontmatter into typed data, and writes typed data back as a frontmatter block.
|
|
163
162
|
*
|
|
164
163
|
* @remarks
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
164
|
+
* Named `MarkdownFrontmatter` rather than `Frontmatter` because `Frontmatter`
|
|
165
|
+
* names the capture node class (the node classes are named after their mdast
|
|
166
|
+
* types).
|
|
167
|
+
*
|
|
168
|
+
* @example
|
|
169
|
+
* ```ts
|
|
170
|
+
* import { MarkdownDocument, MarkdownFrontmatter, MarkdownParseOptions, YamlFrontmatter } from "@effected/markdown";
|
|
171
|
+
* import { Effect, Schema } from "effect";
|
|
172
|
+
*
|
|
173
|
+
* const Meta = Schema.Struct({ title: Schema.String });
|
|
174
|
+
*
|
|
175
|
+
* const program = Effect.gen(function* () {
|
|
176
|
+
* // Frontmatter capture is opt-in at parse time.
|
|
177
|
+
* const source = "---\ntitle: Hello\n---\n\n# Body\n";
|
|
178
|
+
* const doc = yield* MarkdownDocument.parse(source, MarkdownParseOptions.make({ frontmatter: true }));
|
|
179
|
+
* return yield* MarkdownFrontmatter.schema(Meta, YamlFrontmatter)(doc); // { title: "Hello" }
|
|
180
|
+
* });
|
|
181
|
+
* ```
|
|
169
182
|
*
|
|
170
183
|
* @public
|
|
171
184
|
*/
|
|
@@ -233,7 +246,7 @@ var MarkdownFrontmatter = class MarkdownFrontmatter {
|
|
|
233
246
|
* parity, not surgical editing — so anything the format's data model does
|
|
234
247
|
* not carry is not preserved: comments inside a yaml frontmatter block do
|
|
235
248
|
* **not** survive `set`. A per-key surgical mode over the format packages'
|
|
236
|
-
* edit layers is
|
|
249
|
+
* edit layers is not offered.
|
|
237
250
|
*
|
|
238
251
|
* Schema-producing in spirit: bind the returned writer to a `const` when
|
|
239
252
|
* writing many documents.
|
package/FrontmatterResolver.js
CHANGED
|
@@ -197,9 +197,7 @@ var SchemaResolver = class SchemaResolver {
|
|
|
197
197
|
* @remarks
|
|
198
198
|
* Non-mapping data and a mapping without a `$schema` key both carry no
|
|
199
199
|
* declaration: the result succeeds with `undefined` by default, or fails
|
|
200
|
-
* with {@link SchemaDeclarationMissingError} under `requireDeclaration
|
|
201
|
-
* the design's strictness knob, applied at extraction where the
|
|
202
|
-
* present-or-absent branch actually lives.
|
|
200
|
+
* with {@link SchemaDeclarationMissingError} under `requireDeclaration`.
|
|
203
201
|
*
|
|
204
202
|
* @param data - The decoded frontmatter data.
|
|
205
203
|
* @param options - `requireDeclaration` makes a missing `$schema` a typed
|
|
@@ -212,7 +210,7 @@ var SchemaResolver = class SchemaResolver {
|
|
|
212
210
|
return SchemaResolver.classify(data.$schema);
|
|
213
211
|
}
|
|
214
212
|
/**
|
|
215
|
-
* The package's one built-in resolver: a name-keyed registry with
|
|
213
|
+
* The package's one built-in resolver: a name-keyed registry with
|
|
216
214
|
* exact version-segment resolution.
|
|
217
215
|
*
|
|
218
216
|
* @remarks
|
package/FrontmatterSource.js
CHANGED
|
@@ -4,21 +4,6 @@ import { Effect, Schema } from "effect";
|
|
|
4
4
|
|
|
5
5
|
//#region src/FrontmatterSource.ts
|
|
6
6
|
/**
|
|
7
|
-
* String-level frontmatter split and join — the raw-source surface, usable
|
|
8
|
-
* without parsing the body at all.
|
|
9
|
-
*
|
|
10
|
-
* @remarks
|
|
11
|
-
* The `MarkdownFrontmatter` seam requires a parsed `MarkdownDocument`; this
|
|
12
|
-
* module serves the consumer whose body the CommonMark engine cannot or
|
|
13
|
-
* should not parse (an MDX page, a template) and whose contract is
|
|
14
|
-
* byte-exact boundaries (a snapshot hash over the body). It runs the SAME
|
|
15
|
-
* closed fence grammar as the parser's offset-0 pre-scan — `---` yaml,
|
|
16
|
-
* `+++` toml, `---json` json, fence lines exactly the fence, an unclosed
|
|
17
|
-
* fence is not frontmatter — over raw bytes, and never parses the
|
|
18
|
-
* frontmatter value or the body: string level only, decoding is the codec
|
|
19
|
-
* modules' business.
|
|
20
|
-
*/
|
|
21
|
-
/**
|
|
22
7
|
* The three line-terminator spellings a fence line may end with. Recorded by
|
|
23
8
|
* {@link FrontmatterSource.split} as fidelity — what the source's fence
|
|
24
9
|
* lines actually used — and consumed by {@link FrontmatterSource.join}.
|
package/Markdown.js
CHANGED
|
@@ -45,7 +45,7 @@ var MarkdownParseOptions = class extends Schema.Class("MarkdownParseOptions")({
|
|
|
45
45
|
*
|
|
46
46
|
* @remarks
|
|
47
47
|
* CommonMark has no syntax errors — every string is a valid document — so
|
|
48
|
-
* this error carries only hardening-guard trips (
|
|
48
|
+
* this error carries only hardening-guard trips (`NestingDepthExceeded`).
|
|
49
49
|
* Recoverable oddities are diagnostics on {@link MarkdownDocument}, not
|
|
50
50
|
* failures. A malformed-looking document parses; a nesting bomb fails here
|
|
51
51
|
* rather than crashing with a `RangeError`.
|
|
@@ -89,7 +89,7 @@ var MarkdownStringifyError = class extends Schema.TaggedError()("MarkdownStringi
|
|
|
89
89
|
const dialectOf = (options) => options?.dialect ?? "gfm";
|
|
90
90
|
/**
|
|
91
91
|
* Whether an options object opts into frontmatter capture. The default —
|
|
92
|
-
* `false
|
|
92
|
+
* `false` — is spelled exactly once, here: capture changes how
|
|
93
93
|
* a `---` at offset 0 parses, so the spec-conformant reading is the default
|
|
94
94
|
* and frontmatter is the consumer's explicit choice.
|
|
95
95
|
*/
|
package/MarkdownDiagnostic.js
CHANGED
|
@@ -3,9 +3,9 @@ import { Schema } from "effect";
|
|
|
3
3
|
|
|
4
4
|
//#region src/MarkdownDiagnostic.ts
|
|
5
5
|
/**
|
|
6
|
-
* Error codes `Markdown.parse`/`MarkdownDocument.parse` can fail with.
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* Error codes `Markdown.parse`/`MarkdownDocument.parse` can fail with.
|
|
7
|
+
* Currently exactly one, `"NestingDepthExceeded"`, the hardening-guard trip;
|
|
8
|
+
* the union may widen as new fatal conditions are identified.
|
|
9
9
|
*
|
|
10
10
|
* @public
|
|
11
11
|
*/
|
|
@@ -22,9 +22,8 @@ const MarkdownParseErrorCode = Schema.Literals(MARKDOWN_PARSE_ERROR_CODES);
|
|
|
22
22
|
* shape and `@effected/yaml`'s `YamlDiagnostic`); `message` is this
|
|
23
23
|
* package's additive extra. `line`/`character` here are zero-based to match
|
|
24
24
|
* that contract — a different numbering from the one-based `line`/`column`
|
|
25
|
-
* unist `Point`s carried on `MarkdownNode` positions
|
|
26
|
-
*
|
|
27
|
-
* mismatch to reconcile.
|
|
25
|
+
* unist `Point`s carried on `MarkdownNode` positions, which is a deliberate,
|
|
26
|
+
* unrelated convention for the AST rather than a mismatch to reconcile.
|
|
28
27
|
*
|
|
29
28
|
* @public
|
|
30
29
|
*/
|
|
@@ -38,8 +37,12 @@ var MarkdownDiagnostic = class MarkdownDiagnostic extends Schema.Class("Markdown
|
|
|
38
37
|
}) {
|
|
39
38
|
/**
|
|
40
39
|
* Materialize an engine record, deriving zero-based `line`/`character`
|
|
41
|
-
* from `offset` against the source
|
|
40
|
+
* from `offset` against the source text. Advanced — the parse entry
|
|
42
41
|
* points call this for you.
|
|
42
|
+
*
|
|
43
|
+
* @param source - The source text the record's `offset` indexes into.
|
|
44
|
+
* @param raw - The engine record: `code`, `message`, `offset` and `length`.
|
|
45
|
+
* @returns The diagnostic with `line` and `character` filled in.
|
|
43
46
|
*/
|
|
44
47
|
static fromRaw(source, raw) {
|
|
45
48
|
const { line, character } = lineChar(source, raw.offset);
|
package/MarkdownDocument.js
CHANGED
|
@@ -127,27 +127,36 @@ const phrasingText = (nodes) => {
|
|
|
127
127
|
*
|
|
128
128
|
* @remarks
|
|
129
129
|
* The document is the lossless unit — `source` is retained so offsets on the
|
|
130
|
-
* tree stay meaningful and so
|
|
131
|
-
* exact bytes that were parsed.
|
|
130
|
+
* tree stay meaningful and so the edit and format layer can splice against
|
|
131
|
+
* the exact bytes that were parsed.
|
|
132
132
|
*
|
|
133
133
|
* `definitions` is an index over the {@link Definition} nodes that remain in
|
|
134
134
|
* the tree, keyed by case-folded label with the first definition winning; it
|
|
135
135
|
* is not a place they were moved to. References are emitted unresolved, so
|
|
136
136
|
* resolution happens against this map.
|
|
137
137
|
*
|
|
138
|
-
* `diagnostics` is empty for every input the
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
* present-but-unparseable frontmatter in P3. Read an empty array as "nothing
|
|
144
|
-
* to report", not as "not implemented", and do not code against it staying
|
|
145
|
-
* empty.
|
|
138
|
+
* `diagnostics` is empty for every input the parser currently accepts: the
|
|
139
|
+
* plumbing from the engine through to this field exists, but no construct
|
|
140
|
+
* emits a non-fatal diagnostic yet. Read an empty array as "nothing to
|
|
141
|
+
* report", and do not code against it staying empty — fatal conditions fail
|
|
142
|
+
* through `MarkdownParseError` instead.
|
|
146
143
|
*
|
|
147
144
|
* The navigation accessors (`headings`, `sections`, `links`) are derived
|
|
148
145
|
* getters over the tree — no stored state, no parse-time cost, and they can
|
|
149
146
|
* never disagree with the tree they read.
|
|
150
147
|
*
|
|
148
|
+
* @example
|
|
149
|
+
* ```ts
|
|
150
|
+
* import { MarkdownDocument } from "@effected/markdown";
|
|
151
|
+
* import { Effect } from "effect";
|
|
152
|
+
*
|
|
153
|
+
* const program = Effect.gen(function* () {
|
|
154
|
+
* const doc = yield* MarkdownDocument.parse("# Title\n\nSee [docs](./docs.md).\n\n## Usage\n");
|
|
155
|
+
* doc.headings.map((heading) => heading.text); // ["Title", "Usage"]
|
|
156
|
+
* doc.links.map((link) => link.url); // ["./docs.md"]
|
|
157
|
+
* });
|
|
158
|
+
* ```
|
|
159
|
+
*
|
|
151
160
|
* @public
|
|
152
161
|
*/
|
|
153
162
|
var MarkdownDocument = class MarkdownDocument extends Schema.Class("MarkdownDocument")({
|
package/MarkdownEdit.js
CHANGED
|
@@ -34,6 +34,10 @@ var MarkdownEdit = class extends Schema.Class("MarkdownEdit")({
|
|
|
34
34
|
* reverse-offset order so earlier offsets stay valid; the input `edits`
|
|
35
35
|
* array is not mutated. Overlapping edits are a programmer error and throw
|
|
36
36
|
* as a defect — `MarkdownFormat` never produces them.
|
|
37
|
+
*
|
|
38
|
+
* @param text - The source text to edit.
|
|
39
|
+
* @param edits - The edits to apply, in any order.
|
|
40
|
+
* @returns The edited text.
|
|
37
41
|
*/
|
|
38
42
|
static applyAll(text, edits) {
|
|
39
43
|
const sorted = [...edits].sort((a, b) => b.offset - a.offset);
|
package/MarkdownFormat.js
CHANGED
|
@@ -23,7 +23,7 @@ const CodeBlockStyle = Schema.Literals(["fenced", "indented"]);
|
|
|
23
23
|
* source with (same defaults as `Markdown.parse`). Every marker option is
|
|
24
24
|
* optional and independent; an absent option normalizes nothing.
|
|
25
25
|
*
|
|
26
|
-
*
|
|
26
|
+
* Scope is marker normalization only — heading style, bullet
|
|
27
27
|
* character, emphasis/strong marker, fence character, thematic-break
|
|
28
28
|
* character, code-block style. Content is never rewritten, rewrapped or
|
|
29
29
|
* reflowed.
|
|
@@ -71,7 +71,7 @@ const MarkdownModificationErrorCode = Schema.Literals([
|
|
|
71
71
|
/**
|
|
72
72
|
* Raised when `MarkdownFormat.modify` cannot perform the requested
|
|
73
73
|
* replacement: the target node is not in the document (`NodeNotInDocument`),
|
|
74
|
-
* the target kind or splice context is outside the
|
|
74
|
+
* the target kind or splice context is outside the supported scope
|
|
75
75
|
* (`UnsupportedTarget`), the fragment's content category does not fit the
|
|
76
76
|
* target's slot (`FragmentCategoryMismatch`), or the fragment trips the
|
|
77
77
|
* stringifier's hardening guard (`FragmentUnrenderable`). Carries the typed
|
|
@@ -337,7 +337,26 @@ const findAncestry = (root, target) => {
|
|
|
337
337
|
return search(root, []);
|
|
338
338
|
};
|
|
339
339
|
/**
|
|
340
|
-
*
|
|
340
|
+
* Normalizes markdown markers and replaces a single node, as byte-minimal
|
|
341
|
+
* edits that leave the rest of the source untouched. Not instantiable.
|
|
342
|
+
*
|
|
343
|
+
* @example
|
|
344
|
+
* ```ts
|
|
345
|
+
* import { MarkdownDocument, MarkdownFormat, MarkdownFormattingOptions } from "@effected/markdown";
|
|
346
|
+
* import { Effect } from "effect";
|
|
347
|
+
*
|
|
348
|
+
* const options = MarkdownFormattingOptions.make({ bulletChar: "-", headingStyle: "atx" });
|
|
349
|
+
* const normalized = MarkdownFormat.formatToString("* a\n* b\n\nSetext\n======\n", undefined, options);
|
|
350
|
+
* // => "- a\n- b\n\n# Setext\n"
|
|
351
|
+
*
|
|
352
|
+
* const program = Effect.gen(function* () {
|
|
353
|
+
* const doc = yield* MarkdownDocument.parse("# Title\n\nHello world\n");
|
|
354
|
+
* const paragraph = doc.root.children[1];
|
|
355
|
+
* if (paragraph === undefined) return doc.source;
|
|
356
|
+
* return yield* MarkdownFormat.modifyToString(doc, paragraph, "Goodbye");
|
|
357
|
+
* // => "# Title\n\nGoodbye\n"
|
|
358
|
+
* });
|
|
359
|
+
* ```
|
|
341
360
|
*
|
|
342
361
|
* @remarks
|
|
343
362
|
* `format`/`formatToString` are pure and total: input that trips a parse
|
|
@@ -368,6 +387,15 @@ var MarkdownFormat = class MarkdownFormat {
|
|
|
368
387
|
* code block keeps whichever spelling it has — `fenceChar` alone
|
|
369
388
|
* normalizes existing fences and deliberately leaves indented blocks
|
|
370
389
|
* indented.
|
|
390
|
+
*
|
|
391
|
+
* @param text - The markdown source to format.
|
|
392
|
+
* @param range - Optional sub-range; only edits whose node intersects it are
|
|
393
|
+
* returned.
|
|
394
|
+
* @param options - Optional {@link MarkdownFormattingOptions}; an absent
|
|
395
|
+
* marker option normalizes nothing.
|
|
396
|
+
* @returns The edits that normalize the requested markers; apply them with
|
|
397
|
+
* `MarkdownEdit.applyAll`. Empty when the input trips a parse hardening
|
|
398
|
+
* guard.
|
|
371
399
|
*/
|
|
372
400
|
static format(text, range, options) {
|
|
373
401
|
const parsed = Markdown.parseResult(text, MarkdownParseOptions.make({
|
|
@@ -394,6 +422,12 @@ var MarkdownFormat = class MarkdownFormat {
|
|
|
394
422
|
/**
|
|
395
423
|
* Format `text` and apply the resulting edits in one step
|
|
396
424
|
* (`MarkdownEdit.applyAll ∘ format`). Pure and total.
|
|
425
|
+
*
|
|
426
|
+
* @param text - The markdown source to format.
|
|
427
|
+
* @param range - Optional sub-range; only edits whose node intersects it are
|
|
428
|
+
* applied.
|
|
429
|
+
* @param options - Optional {@link MarkdownFormattingOptions}.
|
|
430
|
+
* @returns The formatted text.
|
|
397
431
|
*/
|
|
398
432
|
static formatToString(text, range, options) {
|
|
399
433
|
return MarkdownEdit.applyAll(text, MarkdownFormat.format(text, range, options));
|
|
@@ -405,11 +439,19 @@ var MarkdownFormat = class MarkdownFormat {
|
|
|
405
439
|
* node) or a node fragment whose content category must fit the target's
|
|
406
440
|
* slot (flow for flow targets, phrasing for phrasing targets and table
|
|
407
441
|
* cells). Every replacement renders through the canonical stringifier, so
|
|
408
|
-
* the modified document re-parses cleanly by construction.
|
|
409
|
-
*
|
|
442
|
+
* the modified document re-parses cleanly by construction. List
|
|
443
|
+
* items, table rows, frontmatter and the root refuse with
|
|
410
444
|
* `UnsupportedTarget`, as does a multi-line replacement whose target sits
|
|
411
445
|
* inside a container (a blockquote, list, table or heading) whose
|
|
412
446
|
* continuation lines the splice cannot prefix.
|
|
447
|
+
*
|
|
448
|
+
* @param document - The parsed {@link MarkdownDocument} the target belongs to.
|
|
449
|
+
* @param target - The node to replace; it must be a node of
|
|
450
|
+
* `document.root`, matched by identity.
|
|
451
|
+
* @param replacement - A plain string (literal text) or a node fragment of
|
|
452
|
+
* the target's content category.
|
|
453
|
+
* @returns An `Effect` that succeeds with the edit to apply (via
|
|
454
|
+
* `MarkdownEdit.applyAll`), or fails with {@link MarkdownModificationError}.
|
|
413
455
|
*/
|
|
414
456
|
static modify = Effect.fn("MarkdownFormat.modify")(function* (document, target, replacement) {
|
|
415
457
|
const { start, end } = spanOf(target);
|
|
@@ -454,6 +496,14 @@ var MarkdownFormat = class MarkdownFormat {
|
|
|
454
496
|
/**
|
|
455
497
|
* Modify `document` and apply the resulting edit in one step
|
|
456
498
|
* (`MarkdownEdit.applyAll ∘ modify`).
|
|
499
|
+
*
|
|
500
|
+
* @param document - The parsed {@link MarkdownDocument} the target belongs to.
|
|
501
|
+
* @param target - The node to replace; it must be a node of
|
|
502
|
+
* `document.root`, matched by identity.
|
|
503
|
+
* @param replacement - A plain string (literal text) or a node fragment of
|
|
504
|
+
* the target's content category.
|
|
505
|
+
* @returns An `Effect` that succeeds with the modified source, or fails with
|
|
506
|
+
* {@link MarkdownModificationError}.
|
|
457
507
|
*/
|
|
458
508
|
static modifyToString = Effect.fn("MarkdownFormat.modifyToString")(function* (document, target, replacement) {
|
|
459
509
|
const edits = yield* MarkdownFormat.modify(document, target, replacement);
|
package/MarkdownNode.js
CHANGED
|
@@ -792,11 +792,11 @@ const FrontmatterFormat = Schema.Literals([
|
|
|
792
792
|
*
|
|
793
793
|
* mdast has no single frontmatter node: it names `yaml` in the readme and
|
|
794
794
|
* `toml` through the frontmatter extension, and json has no mdast name at
|
|
795
|
-
* all. This package captures all three through ONE node —
|
|
796
|
-
*
|
|
797
|
-
*
|
|
798
|
-
*
|
|
799
|
-
*
|
|
795
|
+
* all. This package captures all three through ONE node — text plus a format
|
|
796
|
+
* marker — and the `Mdast` projection maps `format` onto the mdast type
|
|
797
|
+
* names where they exist. Decoding the value is the codec modules' job
|
|
798
|
+
* (`YamlFrontmatter`/`TomlFrontmatter`/`JsonFrontmatter`); the engine never
|
|
799
|
+
* looks inside it.
|
|
800
800
|
*
|
|
801
801
|
* Only ever the first child of {@link Root}, and only when parsing opted in
|
|
802
802
|
* via `MarkdownParseOptions.frontmatter` — mdast's "limited to one node, only
|
package/MarkdownVisitor.js
CHANGED
|
@@ -11,7 +11,24 @@ import { Data, Stream } from "effect";
|
|
|
11
11
|
*/
|
|
12
12
|
const MarkdownVisitorEvent = Data.taggedEnum();
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
14
|
+
* Walks a parsed markdown tree as a lazy `Stream` of `Enter`/`Exit` events in
|
|
15
|
+
* document pre-order. Not instantiable.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* import { Markdown, MarkdownVisitor, MarkdownVisitorEvent } from "@effected/markdown";
|
|
20
|
+
* import { Effect, Stream } from "effect";
|
|
21
|
+
*
|
|
22
|
+
* const program = Effect.gen(function* () {
|
|
23
|
+
* const root = yield* Markdown.parse("# Hi\n\ntext\n");
|
|
24
|
+
* return yield* MarkdownVisitor.visit(root).pipe(
|
|
25
|
+
* Stream.filter(MarkdownVisitorEvent.$is("Enter")),
|
|
26
|
+
* Stream.map((event) => event.node.type),
|
|
27
|
+
* Stream.runCollect,
|
|
28
|
+
* );
|
|
29
|
+
* // => ["root", "heading", "text", "paragraph", "text"]
|
|
30
|
+
* });
|
|
31
|
+
* ```
|
|
15
32
|
*
|
|
16
33
|
* @public
|
|
17
34
|
*/
|
|
@@ -31,6 +48,10 @@ var MarkdownVisitor = class {
|
|
|
31
48
|
* as a terminal `MarkdownVisitorEvent` `Error` event carrying a
|
|
32
49
|
* `NestingDepthExceeded` {@link MarkdownDiagnostic}, mirroring the typed
|
|
33
50
|
* failure `Markdown.stringify` produces for the same tree.
|
|
51
|
+
*
|
|
52
|
+
* @param root - The tree to walk.
|
|
53
|
+
* @returns A lazy `Stream` of `MarkdownVisitorEvent`, infallible at the type
|
|
54
|
+
* level.
|
|
34
55
|
*/
|
|
35
56
|
static visit(root) {
|
|
36
57
|
return Stream.fromIterable(walkIterable(root));
|
package/Mdast.js
CHANGED
|
@@ -4,41 +4,14 @@ import { Effect, Result, Schema } from "effect";
|
|
|
4
4
|
|
|
5
5
|
//#region src/Mdast.ts
|
|
6
6
|
/**
|
|
7
|
-
* The remark-ecosystem interop boundary: projection between this package's
|
|
8
|
-
* node classes and plain mdast JSON.
|
|
9
|
-
*
|
|
10
|
-
* @remarks
|
|
11
|
-
* `Mdast.toMdast` projects a parsed {@link Root} to plain spec-valid mdast
|
|
12
|
-
* objects — fidelity extras stripped, optional fields spelled the way
|
|
13
|
-
* `mdast-util-from-markdown` spells them (explicit `null`/`false` where the
|
|
14
|
-
* reference utility emits them), so the output deep-equals what the remark
|
|
15
|
-
* ecosystem produces and consumes. `Mdast.fromMdast` admits foreign plain
|
|
16
|
-
* mdast back into the package's Schema node classes, synthesizing zero-width
|
|
17
|
-
* sentinel positions where unist leaves them optional.
|
|
18
|
-
*
|
|
19
|
-
* The emission target is `mdast-util-from-markdown@2.0.3` (the vendored
|
|
20
|
-
* interop corpus pin): `list.ordered`/`start`/`spread` and
|
|
21
|
-
* `listItem.spread`/`checked` always explicit (`null` for unknown `start`/
|
|
22
|
-
* `checked`), `code.lang`/`meta` and every `title` explicit `null` when
|
|
23
|
-
* absent, `image.alt`/`imageReference.alt` always a string. Under unist's
|
|
24
|
-
* null-equals-absent convention for optional fields this stays spec-valid
|
|
25
|
-
* mdast; it is also byte-identical to the reference utility's output, which
|
|
26
|
-
* is what interop means operationally. GFM shapes follow the same convention
|
|
27
|
-
* from `mdast-util-gfm`; the frontmatter capture projects to mdast's
|
|
28
|
-
* `yaml`/`toml` literal nodes, and a `json` capture projects to a
|
|
29
|
-
* `json`-typed literal node per the `mdast-util-frontmatter` custom-preset
|
|
30
|
-
* convention (presets name the node type after the language).
|
|
31
|
-
*/
|
|
32
|
-
/**
|
|
33
7
|
* Indicates that foreign mdast input failed to decode into the package's
|
|
34
8
|
* node classes.
|
|
35
9
|
*
|
|
36
10
|
* @remarks
|
|
37
11
|
* `issue` carries the **structured** schema failure — at runtime a
|
|
38
12
|
* `SchemaIssue.Issue` tree, reachable through `_tag` and nested `issues` —
|
|
39
|
-
* never a stringified rendering
|
|
40
|
-
*
|
|
41
|
-
* `Issue`; narrow it with the `SchemaIssue` module.
|
|
13
|
+
* never a stringified rendering. It is typed `unknown` because core exposes
|
|
14
|
+
* no `Schema` for `Issue`; narrow it with the `SchemaIssue` module.
|
|
42
15
|
*
|
|
43
16
|
* @public
|
|
44
17
|
*/
|
|
@@ -391,7 +364,41 @@ const normalizeMdxAttribute = (value) => {
|
|
|
391
364
|
};
|
|
392
365
|
const decodeRoot = Schema.decodeUnknownResult(Root);
|
|
393
366
|
/**
|
|
394
|
-
*
|
|
367
|
+
* Projects parsed markdown trees to plain mdast JSON and admits foreign mdast
|
|
368
|
+
* back into this package's node classes, for interop with the remark
|
|
369
|
+
* ecosystem.
|
|
370
|
+
*
|
|
371
|
+
* @remarks
|
|
372
|
+
* `Mdast.toMdast` projects a parsed {@link Root} to plain spec-valid mdast
|
|
373
|
+
* objects — fidelity extras stripped, optional fields spelled the way
|
|
374
|
+
* `mdast-util-from-markdown@2.0.3` spells them (explicit `null`/`false` where
|
|
375
|
+
* the reference utility emits them), so the output deep-equals what the
|
|
376
|
+
* remark ecosystem produces and consumes: `list.ordered`/`start`/`spread` and
|
|
377
|
+
* `listItem.spread`/`checked` are always explicit (`null` for unknown
|
|
378
|
+
* `start`/`checked`), `code.lang`/`meta` and every `title` are explicit
|
|
379
|
+
* `null` when absent, and `image.alt`/`imageReference.alt` are always
|
|
380
|
+
* strings. Under unist's null-equals-absent convention for optional fields
|
|
381
|
+
* this stays spec-valid mdast. GFM shapes follow the same convention from
|
|
382
|
+
* `mdast-util-gfm`; the frontmatter capture projects to mdast's `yaml`/`toml`
|
|
383
|
+
* literal nodes, and a `json` capture projects to a `json`-typed literal node
|
|
384
|
+
* per the `mdast-util-frontmatter` custom-preset convention (presets name the
|
|
385
|
+
* node type after the language).
|
|
386
|
+
*
|
|
387
|
+
* `Mdast.fromMdast` admits foreign plain mdast back into the package's Schema
|
|
388
|
+
* node classes, synthesizing zero-width sentinel positions where unist leaves
|
|
389
|
+
* them optional.
|
|
390
|
+
*
|
|
391
|
+
* @example
|
|
392
|
+
* ```ts
|
|
393
|
+
* import { Markdown, Mdast } from "@effected/markdown";
|
|
394
|
+
* import { Effect } from "effect";
|
|
395
|
+
*
|
|
396
|
+
* const program = Effect.gen(function* () {
|
|
397
|
+
* const root = yield* Markdown.parse("# Hello\n");
|
|
398
|
+
* const plain = Mdast.toMdast(root); // plain mdast JSON
|
|
399
|
+
* return yield* Mdast.fromMdast(plain); // back to node classes
|
|
400
|
+
* });
|
|
401
|
+
* ```
|
|
395
402
|
*
|
|
396
403
|
* @public
|
|
397
404
|
*/
|
package/README.md
CHANGED
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
|
|
8
8
|
Zero-dependency CommonMark 0.31.2 and GFM parsing, editing and transformation expressed as Effect schemas and pure functions. Parse markdown into mdast-shaped nodes carrying byte offsets, navigate headings, sections and links, compute surgical offset-splice edits, normalize markers, decode frontmatter into a validated domain schema, project to and from plain mdast for the remark ecosystem, and walk a document as a `Stream`.
|
|
9
9
|
|
|
10
|
-
> **Pre
|
|
11
|
-
>
|
|
12
|
-
> `1.0.0`
|
|
13
|
-
>
|
|
10
|
+
> **Pre-`1.0.0`.** This package is part of the `@effected/*` kit, built on stable
|
|
11
|
+
> Effect v4 (`effect` `^4.0.0`) and still in `0.x` development. Stable Effect
|
|
12
|
+
> makes a kit `1.0.0` possible, not automatic. To keep your `effect` and
|
|
13
|
+
> `@effect/*` versions on the line the kit is built and tested against, install
|
|
14
14
|
> [`@effected/pnpm-plugin-effect`](https://www.npmjs.com/package/@effected/pnpm-plugin-effect).
|
|
15
15
|
>
|
|
16
16
|
> **Stability: unstable.** This package's API surface is not yet considered
|
package/TomlFrontmatter.js
CHANGED
|
@@ -15,8 +15,8 @@ import { Toml } from "@effected/toml";
|
|
|
15
15
|
*
|
|
16
16
|
* Encodes with `Toml.stringify`; a value toml cannot represent fails as a
|
|
17
17
|
* {@link FrontmatterEncodeError} carrying the `TomlStringifyError`
|
|
18
|
-
* structurally. An empty object encodes to the **empty body** — the
|
|
19
|
-
*
|
|
18
|
+
* structurally. An empty object encodes to the **empty body** — the mirror
|
|
19
|
+
* of an empty capture decoding to `{}` — rendering as adjacent `+++` fences,
|
|
20
20
|
* so `set`-then-decode recovers `{}` exactly.
|
|
21
21
|
*
|
|
22
22
|
* `@effected/toml` is an optional peer — importing this module is what
|
package/index.d.ts
CHANGED
|
@@ -868,11 +868,11 @@ declare const Frontmatter_base: Schema.Class<Frontmatter, Schema.Struct<{
|
|
|
868
868
|
*
|
|
869
869
|
* mdast has no single frontmatter node: it names `yaml` in the readme and
|
|
870
870
|
* `toml` through the frontmatter extension, and json has no mdast name at
|
|
871
|
-
* all. This package captures all three through ONE node —
|
|
872
|
-
*
|
|
873
|
-
*
|
|
874
|
-
*
|
|
875
|
-
*
|
|
871
|
+
* all. This package captures all three through ONE node — text plus a format
|
|
872
|
+
* marker — and the `Mdast` projection maps `format` onto the mdast type
|
|
873
|
+
* names where they exist. Decoding the value is the codec modules' job
|
|
874
|
+
* (`YamlFrontmatter`/`TomlFrontmatter`/`JsonFrontmatter`); the engine never
|
|
875
|
+
* looks inside it.
|
|
876
876
|
*
|
|
877
877
|
* Only ever the first child of {@link Root}, and only when parsing opted in
|
|
878
878
|
* via `MarkdownParseOptions.frontmatter` — mdast's "limited to one node, only
|
|
@@ -944,9 +944,9 @@ type MarkdownNodeOfType<T extends MarkdownNodeType> = Extract<MarkdownNode, {
|
|
|
944
944
|
//#endregion
|
|
945
945
|
//#region src/MarkdownDiagnostic.d.ts
|
|
946
946
|
/**
|
|
947
|
-
* Error codes `Markdown.parse`/`MarkdownDocument.parse` can fail with.
|
|
948
|
-
*
|
|
949
|
-
*
|
|
947
|
+
* Error codes `Markdown.parse`/`MarkdownDocument.parse` can fail with.
|
|
948
|
+
* Currently exactly one, `"NestingDepthExceeded"`, the hardening-guard trip;
|
|
949
|
+
* the union may widen as new fatal conditions are identified.
|
|
950
950
|
*
|
|
951
951
|
* @public
|
|
952
952
|
*/
|
|
@@ -977,17 +977,20 @@ declare const MarkdownDiagnostic_base: Schema.Class<MarkdownDiagnostic, Schema.S
|
|
|
977
977
|
* shape and `@effected/yaml`'s `YamlDiagnostic`); `message` is this
|
|
978
978
|
* package's additive extra. `line`/`character` here are zero-based to match
|
|
979
979
|
* that contract — a different numbering from the one-based `line`/`column`
|
|
980
|
-
* unist `Point`s carried on `MarkdownNode` positions
|
|
981
|
-
*
|
|
982
|
-
* mismatch to reconcile.
|
|
980
|
+
* unist `Point`s carried on `MarkdownNode` positions, which is a deliberate,
|
|
981
|
+
* unrelated convention for the AST rather than a mismatch to reconcile.
|
|
983
982
|
*
|
|
984
983
|
* @public
|
|
985
984
|
*/
|
|
986
985
|
export declare class MarkdownDiagnostic extends MarkdownDiagnostic_base {
|
|
987
986
|
/**
|
|
988
987
|
* Materialize an engine record, deriving zero-based `line`/`character`
|
|
989
|
-
* from `offset` against the source
|
|
988
|
+
* from `offset` against the source text. Advanced — the parse entry
|
|
990
989
|
* points call this for you.
|
|
990
|
+
*
|
|
991
|
+
* @param source - The source text the record's `offset` indexes into.
|
|
992
|
+
* @param raw - The engine record: `code`, `message`, `offset` and `length`.
|
|
993
|
+
* @returns The diagnostic with `line` and `character` filled in.
|
|
991
994
|
*/
|
|
992
995
|
static fromRaw(source: string, raw: {
|
|
993
996
|
readonly code: MarkdownParseErrorCode;
|
|
@@ -1046,7 +1049,7 @@ declare const MarkdownParseError_base: Schema.Class<MarkdownParseError, Schema.T
|
|
|
1046
1049
|
*
|
|
1047
1050
|
* @remarks
|
|
1048
1051
|
* CommonMark has no syntax errors — every string is a valid document — so
|
|
1049
|
-
* this error carries only hardening-guard trips (
|
|
1052
|
+
* this error carries only hardening-guard trips (`NestingDepthExceeded`).
|
|
1050
1053
|
* Recoverable oddities are diagnostics on {@link MarkdownDocument}, not
|
|
1051
1054
|
* failures. A malformed-looking document parses; a nesting bomb fails here
|
|
1052
1055
|
* rather than crashing with a `RangeError`.
|
|
@@ -1337,8 +1340,7 @@ export declare class Markdown {
|
|
|
1337
1340
|
//#region src/MarkdownEdit.d.ts
|
|
1338
1341
|
/**
|
|
1339
1342
|
* A single path segment: a `number` for child indices in the node tree, or a
|
|
1340
|
-
* `string` for named addressing (reserved for
|
|
1341
|
-
* definition identifiers — which arrives with the visitor phase).
|
|
1343
|
+
* `string` for named addressing (reserved, e.g. for definition identifiers).
|
|
1342
1344
|
*
|
|
1343
1345
|
* @public
|
|
1344
1346
|
*/
|
|
@@ -1385,6 +1387,10 @@ export declare class MarkdownEdit extends MarkdownEdit_base {
|
|
|
1385
1387
|
* reverse-offset order so earlier offsets stay valid; the input `edits`
|
|
1386
1388
|
* array is not mutated. Overlapping edits are a programmer error and throw
|
|
1387
1389
|
* as a defect — `MarkdownFormat` never produces them.
|
|
1390
|
+
*
|
|
1391
|
+
* @param text - The source text to edit.
|
|
1392
|
+
* @param edits - The edits to apply, in any order.
|
|
1393
|
+
* @returns The edited text.
|
|
1388
1394
|
*/
|
|
1389
1395
|
static applyAll(text: string, edits: ReadonlyArray<MarkdownEdit>): string;
|
|
1390
1396
|
}
|
|
@@ -1540,27 +1546,36 @@ declare const MarkdownDocument_base: Schema.Class<MarkdownDocument, Schema.Struc
|
|
|
1540
1546
|
*
|
|
1541
1547
|
* @remarks
|
|
1542
1548
|
* The document is the lossless unit — `source` is retained so offsets on the
|
|
1543
|
-
* tree stay meaningful and so
|
|
1544
|
-
* exact bytes that were parsed.
|
|
1549
|
+
* tree stay meaningful and so the edit and format layer can splice against
|
|
1550
|
+
* the exact bytes that were parsed.
|
|
1545
1551
|
*
|
|
1546
1552
|
* `definitions` is an index over the {@link Definition} nodes that remain in
|
|
1547
1553
|
* the tree, keyed by case-folded label with the first definition winning; it
|
|
1548
1554
|
* is not a place they were moved to. References are emitted unresolved, so
|
|
1549
1555
|
* resolution happens against this map.
|
|
1550
1556
|
*
|
|
1551
|
-
* `diagnostics` is empty for every input the
|
|
1552
|
-
*
|
|
1553
|
-
*
|
|
1554
|
-
*
|
|
1555
|
-
*
|
|
1556
|
-
* present-but-unparseable frontmatter in P3. Read an empty array as "nothing
|
|
1557
|
-
* to report", not as "not implemented", and do not code against it staying
|
|
1558
|
-
* empty.
|
|
1557
|
+
* `diagnostics` is empty for every input the parser currently accepts: the
|
|
1558
|
+
* plumbing from the engine through to this field exists, but no construct
|
|
1559
|
+
* emits a non-fatal diagnostic yet. Read an empty array as "nothing to
|
|
1560
|
+
* report", and do not code against it staying empty — fatal conditions fail
|
|
1561
|
+
* through `MarkdownParseError` instead.
|
|
1559
1562
|
*
|
|
1560
1563
|
* The navigation accessors (`headings`, `sections`, `links`) are derived
|
|
1561
1564
|
* getters over the tree — no stored state, no parse-time cost, and they can
|
|
1562
1565
|
* never disagree with the tree they read.
|
|
1563
1566
|
*
|
|
1567
|
+
* @example
|
|
1568
|
+
* ```ts
|
|
1569
|
+
* import { MarkdownDocument } from "@effected/markdown";
|
|
1570
|
+
* import { Effect } from "effect";
|
|
1571
|
+
*
|
|
1572
|
+
* const program = Effect.gen(function* () {
|
|
1573
|
+
* const doc = yield* MarkdownDocument.parse("# Title\n\nSee [docs](./docs.md).\n\n## Usage\n");
|
|
1574
|
+
* doc.headings.map((heading) => heading.text); // ["Title", "Usage"]
|
|
1575
|
+
* doc.links.map((link) => link.url); // ["./docs.md"]
|
|
1576
|
+
* });
|
|
1577
|
+
* ```
|
|
1578
|
+
*
|
|
1564
1579
|
* @public
|
|
1565
1580
|
*/
|
|
1566
1581
|
export declare class MarkdownDocument extends MarkdownDocument_base {
|
|
@@ -1891,9 +1906,8 @@ declare const FrontmatterValidationError_base: Schema.Class<FrontmatterValidatio
|
|
|
1891
1906
|
* @remarks
|
|
1892
1907
|
* `issue` carries the **structured** schema failure — at runtime a
|
|
1893
1908
|
* `SchemaIssue.Issue` tree, reachable through `_tag` and nested `issues` —
|
|
1894
|
-
* never a stringified rendering
|
|
1895
|
-
*
|
|
1896
|
-
* `Schema` for `Issue`; narrow it with the `SchemaIssue` module.
|
|
1909
|
+
* never a stringified rendering. It is typed `unknown` because core exposes
|
|
1910
|
+
* no `Schema` for `Issue`; narrow it with the `SchemaIssue` module.
|
|
1897
1911
|
*
|
|
1898
1912
|
* @public
|
|
1899
1913
|
*/
|
|
@@ -1918,13 +1932,27 @@ type FrontmatterSchemaError = FrontmatterMissingError | FrontmatterFormatMismatc
|
|
|
1918
1932
|
*/
|
|
1919
1933
|
type FrontmatterWriteError = FrontmatterFormatMismatchError | FrontmatterEncodeError | FrontmatterValidationError;
|
|
1920
1934
|
/**
|
|
1921
|
-
* The frontmatter schema composition facade
|
|
1935
|
+
* The frontmatter schema composition facade: decodes a parsed document's frontmatter into typed data, and writes typed data back as a frontmatter block.
|
|
1922
1936
|
*
|
|
1923
1937
|
* @remarks
|
|
1924
|
-
*
|
|
1925
|
-
*
|
|
1926
|
-
*
|
|
1927
|
-
*
|
|
1938
|
+
* Named `MarkdownFrontmatter` rather than `Frontmatter` because `Frontmatter`
|
|
1939
|
+
* names the capture node class (the node classes are named after their mdast
|
|
1940
|
+
* types).
|
|
1941
|
+
*
|
|
1942
|
+
* @example
|
|
1943
|
+
* ```ts
|
|
1944
|
+
* import { MarkdownDocument, MarkdownFrontmatter, MarkdownParseOptions, YamlFrontmatter } from "@effected/markdown";
|
|
1945
|
+
* import { Effect, Schema } from "effect";
|
|
1946
|
+
*
|
|
1947
|
+
* const Meta = Schema.Struct({ title: Schema.String });
|
|
1948
|
+
*
|
|
1949
|
+
* const program = Effect.gen(function* () {
|
|
1950
|
+
* // Frontmatter capture is opt-in at parse time.
|
|
1951
|
+
* const source = "---\ntitle: Hello\n---\n\n# Body\n";
|
|
1952
|
+
* const doc = yield* MarkdownDocument.parse(source, MarkdownParseOptions.make({ frontmatter: true }));
|
|
1953
|
+
* return yield* MarkdownFrontmatter.schema(Meta, YamlFrontmatter)(doc); // { title: "Hello" }
|
|
1954
|
+
* });
|
|
1955
|
+
* ```
|
|
1928
1956
|
*
|
|
1929
1957
|
* @public
|
|
1930
1958
|
*/
|
|
@@ -1987,7 +2015,7 @@ export declare class MarkdownFrontmatter {
|
|
|
1987
2015
|
* parity, not surgical editing — so anything the format's data model does
|
|
1988
2016
|
* not carry is not preserved: comments inside a yaml frontmatter block do
|
|
1989
2017
|
* **not** survive `set`. A per-key surgical mode over the format packages'
|
|
1990
|
-
* edit layers is
|
|
2018
|
+
* edit layers is not offered.
|
|
1991
2019
|
*
|
|
1992
2020
|
* Schema-producing in spirit: bind the returned writer to a `const` when
|
|
1993
2021
|
* writing many documents.
|
|
@@ -2210,9 +2238,7 @@ export declare class SchemaResolver {
|
|
|
2210
2238
|
* @remarks
|
|
2211
2239
|
* Non-mapping data and a mapping without a `$schema` key both carry no
|
|
2212
2240
|
* declaration: the result succeeds with `undefined` by default, or fails
|
|
2213
|
-
* with {@link SchemaDeclarationMissingError} under `requireDeclaration
|
|
2214
|
-
* the design's strictness knob, applied at extraction where the
|
|
2215
|
-
* present-or-absent branch actually lives.
|
|
2241
|
+
* with {@link SchemaDeclarationMissingError} under `requireDeclaration`.
|
|
2216
2242
|
*
|
|
2217
2243
|
* @param data - The decoded frontmatter data.
|
|
2218
2244
|
* @param options - `requireDeclaration` makes a missing `$schema` a typed
|
|
@@ -2224,7 +2250,7 @@ export declare class SchemaResolver {
|
|
|
2224
2250
|
readonly requireDeclaration?: boolean;
|
|
2225
2251
|
}): Result.Result<SchemaDeclaration | undefined, SchemaDeclarationInvalidError | SchemaDeclarationMissingError>;
|
|
2226
2252
|
/**
|
|
2227
|
-
* The package's one built-in resolver: a name-keyed registry with
|
|
2253
|
+
* The package's one built-in resolver: a name-keyed registry with
|
|
2228
2254
|
* exact version-segment resolution.
|
|
2229
2255
|
*
|
|
2230
2256
|
* @remarks
|
|
@@ -2461,7 +2487,7 @@ declare const MarkdownFormattingOptions_base: Schema.Class<MarkdownFormattingOpt
|
|
|
2461
2487
|
* source with (same defaults as `Markdown.parse`). Every marker option is
|
|
2462
2488
|
* optional and independent; an absent option normalizes nothing.
|
|
2463
2489
|
*
|
|
2464
|
-
*
|
|
2490
|
+
* Scope is marker normalization only — heading style, bullet
|
|
2465
2491
|
* character, emphasis/strong marker, fence character, thematic-break
|
|
2466
2492
|
* character, code-block style. Content is never rewritten, rewrapped or
|
|
2467
2493
|
* reflowed.
|
|
@@ -2507,7 +2533,7 @@ declare const MarkdownModificationError_base: Schema.Class<MarkdownModificationE
|
|
|
2507
2533
|
/**
|
|
2508
2534
|
* Raised when `MarkdownFormat.modify` cannot perform the requested
|
|
2509
2535
|
* replacement: the target node is not in the document (`NodeNotInDocument`),
|
|
2510
|
-
* the target kind or splice context is outside the
|
|
2536
|
+
* the target kind or splice context is outside the supported scope
|
|
2511
2537
|
* (`UnsupportedTarget`), the fragment's content category does not fit the
|
|
2512
2538
|
* target's slot (`FragmentCategoryMismatch`), or the fragment trips the
|
|
2513
2539
|
* stringifier's hardening guard (`FragmentUnrenderable`). Carries the typed
|
|
@@ -2520,7 +2546,26 @@ export declare class MarkdownModificationError extends MarkdownModificationError
|
|
|
2520
2546
|
get message(): string;
|
|
2521
2547
|
}
|
|
2522
2548
|
/**
|
|
2523
|
-
*
|
|
2549
|
+
* Normalizes markdown markers and replaces a single node, as byte-minimal
|
|
2550
|
+
* edits that leave the rest of the source untouched. Not instantiable.
|
|
2551
|
+
*
|
|
2552
|
+
* @example
|
|
2553
|
+
* ```ts
|
|
2554
|
+
* import { MarkdownDocument, MarkdownFormat, MarkdownFormattingOptions } from "@effected/markdown";
|
|
2555
|
+
* import { Effect } from "effect";
|
|
2556
|
+
*
|
|
2557
|
+
* const options = MarkdownFormattingOptions.make({ bulletChar: "-", headingStyle: "atx" });
|
|
2558
|
+
* const normalized = MarkdownFormat.formatToString("* a\n* b\n\nSetext\n======\n", undefined, options);
|
|
2559
|
+
* // => "- a\n- b\n\n# Setext\n"
|
|
2560
|
+
*
|
|
2561
|
+
* const program = Effect.gen(function* () {
|
|
2562
|
+
* const doc = yield* MarkdownDocument.parse("# Title\n\nHello world\n");
|
|
2563
|
+
* const paragraph = doc.root.children[1];
|
|
2564
|
+
* if (paragraph === undefined) return doc.source;
|
|
2565
|
+
* return yield* MarkdownFormat.modifyToString(doc, paragraph, "Goodbye");
|
|
2566
|
+
* // => "# Title\n\nGoodbye\n"
|
|
2567
|
+
* });
|
|
2568
|
+
* ```
|
|
2524
2569
|
*
|
|
2525
2570
|
* @remarks
|
|
2526
2571
|
* `format`/`formatToString` are pure and total: input that trips a parse
|
|
@@ -2551,11 +2596,26 @@ export declare class MarkdownFormat {
|
|
|
2551
2596
|
* code block keeps whichever spelling it has — `fenceChar` alone
|
|
2552
2597
|
* normalizes existing fences and deliberately leaves indented blocks
|
|
2553
2598
|
* indented.
|
|
2599
|
+
*
|
|
2600
|
+
* @param text - The markdown source to format.
|
|
2601
|
+
* @param range - Optional sub-range; only edits whose node intersects it are
|
|
2602
|
+
* returned.
|
|
2603
|
+
* @param options - Optional {@link MarkdownFormattingOptions}; an absent
|
|
2604
|
+
* marker option normalizes nothing.
|
|
2605
|
+
* @returns The edits that normalize the requested markers; apply them with
|
|
2606
|
+
* `MarkdownEdit.applyAll`. Empty when the input trips a parse hardening
|
|
2607
|
+
* guard.
|
|
2554
2608
|
*/
|
|
2555
2609
|
static format(text: string, range?: MarkdownRangeLike, options?: MarkdownFormattingOptions): ReadonlyArray<MarkdownEdit>;
|
|
2556
2610
|
/**
|
|
2557
2611
|
* Format `text` and apply the resulting edits in one step
|
|
2558
2612
|
* (`MarkdownEdit.applyAll ∘ format`). Pure and total.
|
|
2613
|
+
*
|
|
2614
|
+
* @param text - The markdown source to format.
|
|
2615
|
+
* @param range - Optional sub-range; only edits whose node intersects it are
|
|
2616
|
+
* applied.
|
|
2617
|
+
* @param options - Optional {@link MarkdownFormattingOptions}.
|
|
2618
|
+
* @returns The formatted text.
|
|
2559
2619
|
*/
|
|
2560
2620
|
static formatToString(text: string, range?: MarkdownRangeLike, options?: MarkdownFormattingOptions): string;
|
|
2561
2621
|
/**
|
|
@@ -2565,16 +2625,32 @@ export declare class MarkdownFormat {
|
|
|
2565
2625
|
* node) or a node fragment whose content category must fit the target's
|
|
2566
2626
|
* slot (flow for flow targets, phrasing for phrasing targets and table
|
|
2567
2627
|
* cells). Every replacement renders through the canonical stringifier, so
|
|
2568
|
-
* the modified document re-parses cleanly by construction.
|
|
2569
|
-
*
|
|
2628
|
+
* the modified document re-parses cleanly by construction. List
|
|
2629
|
+
* items, table rows, frontmatter and the root refuse with
|
|
2570
2630
|
* `UnsupportedTarget`, as does a multi-line replacement whose target sits
|
|
2571
2631
|
* inside a container (a blockquote, list, table or heading) whose
|
|
2572
2632
|
* continuation lines the splice cannot prefix.
|
|
2633
|
+
*
|
|
2634
|
+
* @param document - The parsed {@link MarkdownDocument} the target belongs to.
|
|
2635
|
+
* @param target - The node to replace; it must be a node of
|
|
2636
|
+
* `document.root`, matched by identity.
|
|
2637
|
+
* @param replacement - A plain string (literal text) or a node fragment of
|
|
2638
|
+
* the target's content category.
|
|
2639
|
+
* @returns An `Effect` that succeeds with the edit to apply (via
|
|
2640
|
+
* `MarkdownEdit.applyAll`), or fails with {@link MarkdownModificationError}.
|
|
2573
2641
|
*/
|
|
2574
2642
|
static readonly modify: (document: MarkdownDocument, target: MarkdownNode, replacement: string | MarkdownNode) => Effect.Effect<readonly MarkdownEdit[], MarkdownModificationError, never>;
|
|
2575
2643
|
/**
|
|
2576
2644
|
* Modify `document` and apply the resulting edit in one step
|
|
2577
2645
|
* (`MarkdownEdit.applyAll ∘ modify`).
|
|
2646
|
+
*
|
|
2647
|
+
* @param document - The parsed {@link MarkdownDocument} the target belongs to.
|
|
2648
|
+
* @param target - The node to replace; it must be a node of
|
|
2649
|
+
* `document.root`, matched by identity.
|
|
2650
|
+
* @param replacement - A plain string (literal text) or a node fragment of
|
|
2651
|
+
* the target's content category.
|
|
2652
|
+
* @returns An `Effect` that succeeds with the modified source, or fails with
|
|
2653
|
+
* {@link MarkdownModificationError}.
|
|
2578
2654
|
*/
|
|
2579
2655
|
static readonly modifyToString: (document: MarkdownDocument, target: MarkdownNode, replacement: string | MarkdownNode) => Effect.Effect<string, MarkdownModificationError, never>;
|
|
2580
2656
|
}
|
|
@@ -2730,7 +2806,24 @@ export declare const MarkdownVisitorEvent: {
|
|
|
2730
2806
|
}, "_tag">;
|
|
2731
2807
|
};
|
|
2732
2808
|
/**
|
|
2733
|
-
*
|
|
2809
|
+
* Walks a parsed markdown tree as a lazy `Stream` of `Enter`/`Exit` events in
|
|
2810
|
+
* document pre-order. Not instantiable.
|
|
2811
|
+
*
|
|
2812
|
+
* @example
|
|
2813
|
+
* ```ts
|
|
2814
|
+
* import { Markdown, MarkdownVisitor, MarkdownVisitorEvent } from "@effected/markdown";
|
|
2815
|
+
* import { Effect, Stream } from "effect";
|
|
2816
|
+
*
|
|
2817
|
+
* const program = Effect.gen(function* () {
|
|
2818
|
+
* const root = yield* Markdown.parse("# Hi\n\ntext\n");
|
|
2819
|
+
* return yield* MarkdownVisitor.visit(root).pipe(
|
|
2820
|
+
* Stream.filter(MarkdownVisitorEvent.$is("Enter")),
|
|
2821
|
+
* Stream.map((event) => event.node.type),
|
|
2822
|
+
* Stream.runCollect,
|
|
2823
|
+
* );
|
|
2824
|
+
* // => ["root", "heading", "text", "paragraph", "text"]
|
|
2825
|
+
* });
|
|
2826
|
+
* ```
|
|
2734
2827
|
*
|
|
2735
2828
|
* @public
|
|
2736
2829
|
*/
|
|
@@ -2750,6 +2843,10 @@ export declare class MarkdownVisitor {
|
|
|
2750
2843
|
* as a terminal `MarkdownVisitorEvent` `Error` event carrying a
|
|
2751
2844
|
* `NestingDepthExceeded` {@link MarkdownDiagnostic}, mirroring the typed
|
|
2752
2845
|
* failure `Markdown.stringify` produces for the same tree.
|
|
2846
|
+
*
|
|
2847
|
+
* @param root - The tree to walk.
|
|
2848
|
+
* @returns A lazy `Stream` of `MarkdownVisitorEvent`, infallible at the type
|
|
2849
|
+
* level.
|
|
2753
2850
|
*/
|
|
2754
2851
|
static visit(root: Root): Stream.Stream<MarkdownVisitorEvent>;
|
|
2755
2852
|
}
|
|
@@ -2780,9 +2877,8 @@ declare const MdastDecodeError_base: Schema.Class<MdastDecodeError, Schema.Tagge
|
|
|
2780
2877
|
* @remarks
|
|
2781
2878
|
* `issue` carries the **structured** schema failure — at runtime a
|
|
2782
2879
|
* `SchemaIssue.Issue` tree, reachable through `_tag` and nested `issues` —
|
|
2783
|
-
* never a stringified rendering
|
|
2784
|
-
*
|
|
2785
|
-
* `Issue`; narrow it with the `SchemaIssue` module.
|
|
2880
|
+
* never a stringified rendering. It is typed `unknown` because core exposes
|
|
2881
|
+
* no `Schema` for `Issue`; narrow it with the `SchemaIssue` module.
|
|
2786
2882
|
*
|
|
2787
2883
|
* @public
|
|
2788
2884
|
*/
|
|
@@ -2790,7 +2886,41 @@ export declare class MdastDecodeError extends MdastDecodeError_base {
|
|
|
2790
2886
|
get message(): string;
|
|
2791
2887
|
}
|
|
2792
2888
|
/**
|
|
2793
|
-
*
|
|
2889
|
+
* Projects parsed markdown trees to plain mdast JSON and admits foreign mdast
|
|
2890
|
+
* back into this package's node classes, for interop with the remark
|
|
2891
|
+
* ecosystem.
|
|
2892
|
+
*
|
|
2893
|
+
* @remarks
|
|
2894
|
+
* `Mdast.toMdast` projects a parsed {@link Root} to plain spec-valid mdast
|
|
2895
|
+
* objects — fidelity extras stripped, optional fields spelled the way
|
|
2896
|
+
* `mdast-util-from-markdown@2.0.3` spells them (explicit `null`/`false` where
|
|
2897
|
+
* the reference utility emits them), so the output deep-equals what the
|
|
2898
|
+
* remark ecosystem produces and consumes: `list.ordered`/`start`/`spread` and
|
|
2899
|
+
* `listItem.spread`/`checked` are always explicit (`null` for unknown
|
|
2900
|
+
* `start`/`checked`), `code.lang`/`meta` and every `title` are explicit
|
|
2901
|
+
* `null` when absent, and `image.alt`/`imageReference.alt` are always
|
|
2902
|
+
* strings. Under unist's null-equals-absent convention for optional fields
|
|
2903
|
+
* this stays spec-valid mdast. GFM shapes follow the same convention from
|
|
2904
|
+
* `mdast-util-gfm`; the frontmatter capture projects to mdast's `yaml`/`toml`
|
|
2905
|
+
* literal nodes, and a `json` capture projects to a `json`-typed literal node
|
|
2906
|
+
* per the `mdast-util-frontmatter` custom-preset convention (presets name the
|
|
2907
|
+
* node type after the language).
|
|
2908
|
+
*
|
|
2909
|
+
* `Mdast.fromMdast` admits foreign plain mdast back into the package's Schema
|
|
2910
|
+
* node classes, synthesizing zero-width sentinel positions where unist leaves
|
|
2911
|
+
* them optional.
|
|
2912
|
+
*
|
|
2913
|
+
* @example
|
|
2914
|
+
* ```ts
|
|
2915
|
+
* import { Markdown, Mdast } from "@effected/markdown";
|
|
2916
|
+
* import { Effect } from "effect";
|
|
2917
|
+
*
|
|
2918
|
+
* const program = Effect.gen(function* () {
|
|
2919
|
+
* const root = yield* Markdown.parse("# Hello\n");
|
|
2920
|
+
* const plain = Mdast.toMdast(root); // plain mdast JSON
|
|
2921
|
+
* return yield* Mdast.fromMdast(plain); // back to node classes
|
|
2922
|
+
* });
|
|
2923
|
+
* ```
|
|
2794
2924
|
*
|
|
2795
2925
|
* @public
|
|
2796
2926
|
*/
|
|
@@ -2872,8 +3002,8 @@ export declare class Mdast {
|
|
|
2872
3002
|
*
|
|
2873
3003
|
* Encodes with `Toml.stringify`; a value toml cannot represent fails as a
|
|
2874
3004
|
* {@link FrontmatterEncodeError} carrying the `TomlStringifyError`
|
|
2875
|
-
* structurally. An empty object encodes to the **empty body** — the
|
|
2876
|
-
*
|
|
3005
|
+
* structurally. An empty object encodes to the **empty body** — the mirror
|
|
3006
|
+
* of an empty capture decoding to `{}` — rendering as adjacent `+++` fences,
|
|
2877
3007
|
* so `set`-then-decode recovers `{}` exactly.
|
|
2878
3008
|
*
|
|
2879
3009
|
* `@effected/toml` is an optional peer — importing this module is what
|
package/internal/carriers.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
//#region src/internal/carriers.ts
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* The engine's error-code vocabulary: currently exactly one, the
|
|
4
|
+
* hardening-guard trip. Widens as new parse-error kinds are added.
|
|
5
5
|
*/
|
|
6
6
|
const MARKDOWN_PARSE_ERROR_CODES = ["NestingDepthExceeded"];
|
|
7
7
|
/** The engine's carrier for a recoverable-turned-fatal parse condition. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effected/markdown",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "CommonMark and GFM markdown parse, edit and transform schemas for Effect",
|
|
6
6
|
"keywords": [
|
|
@@ -39,10 +39,10 @@
|
|
|
39
39
|
"./package.json": "./package.json"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
|
-
"@effected/jsonc": "^0.
|
|
43
|
-
"@effected/toml": "^0.
|
|
44
|
-
"@effected/yaml": "^0.
|
|
45
|
-
"effect": "4.0.0
|
|
42
|
+
"@effected/jsonc": "^0.15.0",
|
|
43
|
+
"@effected/toml": "^0.11.0",
|
|
44
|
+
"@effected/yaml": "^0.19.0",
|
|
45
|
+
"effect": "^4.0.0"
|
|
46
46
|
},
|
|
47
47
|
"peerDependenciesMeta": {
|
|
48
48
|
"@effected/jsonc": {
|