@effected/markdown 0.18.0 → 0.20.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 (66) hide show
  1. package/Frontmatter.js +1 -2
  2. package/FrontmatterResolver.js +1 -2
  3. package/FrontmatterSource.js +1 -2
  4. package/JsonFrontmatter.js +1 -2
  5. package/Markdown.js +25 -14
  6. package/MarkdownDiagnostic.js +1 -2
  7. package/MarkdownDocument.js +5 -39
  8. package/MarkdownEdit.js +1 -2
  9. package/MarkdownFormat.js +1 -2
  10. package/MarkdownNode.js +1 -2
  11. package/MarkdownTransform.js +166 -0
  12. package/MarkdownVisitor.js +1 -2
  13. package/Mdast.js +50 -2
  14. package/TomlFrontmatter.js +1 -2
  15. package/YamlFrontmatter.js +1 -2
  16. package/index.d.ts +310 -39
  17. package/index.js +2 -1
  18. package/internal/blockParser.js +1 -2
  19. package/internal/blockRegistry.js +1 -2
  20. package/internal/blockTypes.js +1 -2
  21. package/internal/blocks/atxHeading.js +1 -2
  22. package/internal/blocks/blockquote.js +1 -2
  23. package/internal/blocks/code.js +1 -2
  24. package/internal/blocks/document.js +1 -2
  25. package/internal/blocks/fencedCode.js +1 -2
  26. package/internal/blocks/footnoteDefinition.js +1 -2
  27. package/internal/blocks/frontmatter.js +1 -2
  28. package/internal/blocks/htmlBlock.js +1 -2
  29. package/internal/blocks/indentedCode.js +1 -2
  30. package/internal/blocks/linkReferenceDefinition.js +1 -2
  31. package/internal/blocks/list.js +1 -2
  32. package/internal/blocks/paragraph.js +1 -2
  33. package/internal/blocks/setextHeading.js +1 -2
  34. package/internal/blocks/table.js +1 -2
  35. package/internal/blocks/taskListItem.js +1 -2
  36. package/internal/blocks/thematicBreak.js +1 -2
  37. package/internal/carriers.js +1 -2
  38. package/internal/entities.js +1 -2
  39. package/internal/entityMap.js +1 -2
  40. package/internal/htmlTags.js +1 -2
  41. package/internal/inlineNode.js +1 -2
  42. package/internal/inlineParser.js +1 -2
  43. package/internal/inlineRegistry.js +1 -2
  44. package/internal/inlines/autolink.js +1 -2
  45. package/internal/inlines/autolinkLiteral.js +1 -2
  46. package/internal/inlines/codeSpan.js +1 -2
  47. package/internal/inlines/emphasis.js +1 -2
  48. package/internal/inlines/entity.js +1 -2
  49. package/internal/inlines/escape.js +1 -2
  50. package/internal/inlines/footnoteReference.js +1 -2
  51. package/internal/inlines/lineBreak.js +1 -2
  52. package/internal/inlines/link.js +1 -2
  53. package/internal/inlines/rawHtml.js +1 -2
  54. package/internal/inlines/strikethrough.js +1 -2
  55. package/internal/inlines/text.js +1 -2
  56. package/internal/lineIndex.js +1 -2
  57. package/internal/patterns.js +1 -2
  58. package/internal/phrasing.js +1 -2
  59. package/internal/preprocess.js +1 -2
  60. package/internal/rawInline.js +1 -2
  61. package/internal/references.js +1 -2
  62. package/internal/sectionBounds.js +43 -0
  63. package/internal/segments.js +1 -2
  64. package/internal/stringify.js +8 -11
  65. package/internal/unescape.js +1 -2
  66. package/package.json +4 -4
package/index.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { Data, Effect, Result, Schema, Stream } from "effect";
2
- //#region src/MarkdownNode.d.ts
3
2
  declare const Point_base: Schema.Class<Point, Schema.Struct<{
4
3
  readonly line: Schema.Number;
5
4
  readonly column: Schema.Number;
@@ -941,8 +940,6 @@ type MarkdownNodeType = MarkdownNode["type"];
941
940
  type MarkdownNodeOfType<T extends MarkdownNodeType> = Extract<MarkdownNode, {
942
941
  readonly type: T;
943
942
  }>;
944
- //#endregion
945
- //#region src/MarkdownDiagnostic.d.ts
946
943
  /**
947
944
  * Error codes `Markdown.parse`/`MarkdownDocument.parse` can fail with.
948
945
  * Currently exactly one, `"NestingDepthExceeded"`, the hardening-guard trip;
@@ -999,8 +996,6 @@ export declare class MarkdownDiagnostic extends MarkdownDiagnostic_base {
999
996
  readonly length: number;
1000
997
  }): MarkdownDiagnostic;
1001
998
  }
1002
- //#endregion
1003
- //#region src/Markdown.d.ts
1004
999
  /**
1005
1000
  * The markdown dialects the parser can be pointed at. `"gfm"` — CommonMark
1006
1001
  * 0.31.2 plus the GitHub extensions (tables, strikethrough, autolink
@@ -1204,11 +1199,20 @@ export declare class Markdown {
1204
1199
  */
1205
1200
  static readonly parsePhrasing: (text: string, options?: MarkdownParseOptions | undefined) => Effect.Effect<readonly PhrasingContent[], MarkdownParseError, never>;
1206
1201
  /**
1207
- * Serialize a {@link Root} tree to canonical markdown, synchronously, as a
1208
- * `Result`. The pure primitive twin of {@link Markdown.stringify}, on the
1209
- * same terms as {@link Markdown.parseResult}.
1202
+ * Serialize a {@link Root} tree, or a single {@link (FlowContent:type)} node, to
1203
+ * canonical markdown, synchronously, as a `Result`. The pure primitive twin
1204
+ * of {@link Markdown.stringify}, on the same terms as
1205
+ * {@link Markdown.parseResult}.
1210
1206
  *
1211
1207
  * @remarks
1208
+ * **A single flow node is defined as its one-child document**: passing
1209
+ * `node` is exactly `stringifyResult(Root.make({ children: [node] }))` —
1210
+ * the same code path, the same trailing `\n`, the same canonical choices.
1211
+ * This lets a caller serialize one block (a table, a section's paragraph,
1212
+ * an MDX flow element) without building the wrapper by hand.
1213
+ * `Frontmatter` and `MdxjsEsm` are deliberately not admitted alone — each
1214
+ * is meaningful only at a document's root — so wrap them in a `Root`.
1215
+ *
1212
1216
  * Canonical serialization: fidelity fields (marker characters, fence
1213
1217
  * style, heading spelling) win when present; documented canonical
1214
1218
  * defaults apply when absent. One default surprises: a language-less
@@ -1291,15 +1295,15 @@ export declare class Markdown {
1291
1295
  * trip on a tree nesting past the depth cap fails, and only synthesized
1292
1296
  * or decoded trees can nest that far.
1293
1297
  *
1294
- * @param root - The document tree to serialize.
1298
+ * @param node - The document tree, or a single flow node, to serialize.
1295
1299
  * @returns A `Result` succeeding with markdown source, or failing with
1296
1300
  * {@link MarkdownStringifyError}.
1297
1301
  */
1298
- static stringifyResult(root: Root): Result.Result<string, MarkdownStringifyError>;
1302
+ static stringifyResult(node: Root | FlowContent): Result.Result<string, MarkdownStringifyError>;
1299
1303
  /**
1300
- * Serialize a {@link Root} tree to canonical markdown. Defined in terms of
1301
- * {@link Markdown.stringifyResult} — synchronous callers can use that
1302
- * variant directly.
1304
+ * Serialize a {@link Root} tree, or a single {@link (FlowContent:type)} node, to
1305
+ * canonical markdown. Defined in terms of {@link Markdown.stringifyResult}
1306
+ * — synchronous callers can use that variant directly.
1303
1307
  *
1304
1308
  * @remarks
1305
1309
  * The canonical form is a **stability commitment** and safe to assert
@@ -1315,11 +1319,14 @@ export declare class Markdown {
1315
1319
  * `escapeStyle`, an emitter instruction rather than a source spelling,
1316
1320
  * which the boundary admits.
1317
1321
  *
1318
- * @param root - The document tree to serialize.
1322
+ * A single flow node serializes exactly as its one-child document does;
1323
+ * see {@link Markdown.stringifyResult}.
1324
+ *
1325
+ * @param node - The document tree, or a single flow node, to serialize.
1319
1326
  * @returns An `Effect` that succeeds with markdown source, or fails with
1320
1327
  * {@link MarkdownStringifyError}.
1321
1328
  */
1322
- static readonly stringify: (root: Root) => Effect.Effect<string, MarkdownStringifyError, never>;
1329
+ static readonly stringify: (node: Root | FlowContent) => Effect.Effect<string, MarkdownStringifyError, never>;
1323
1330
  /**
1324
1331
  * A `Schema<Root, string>` decoding markdown source into a {@link Root}
1325
1332
  * tree and encoding a tree back to canonical markdown via
@@ -1342,8 +1349,6 @@ export declare class Markdown {
1342
1349
  */
1343
1350
  static readonly MarkdownFromString: Schema.Codec<Root, string>;
1344
1351
  }
1345
- //#endregion
1346
- //#region src/MarkdownEdit.d.ts
1347
1352
  /**
1348
1353
  * A single path segment: a `number` for child indices in the node tree, or a
1349
1354
  * `string` for named addressing (reserved, e.g. for definition identifiers).
@@ -1400,8 +1405,6 @@ export declare class MarkdownEdit extends MarkdownEdit_base {
1400
1405
  */
1401
1406
  static applyAll(text: string, edits: ReadonlyArray<MarkdownEdit>): string;
1402
1407
  }
1403
- //#endregion
1404
- //#region src/MarkdownDocument.d.ts
1405
1408
  /**
1406
1409
  * A heading entry from {@link MarkdownDocument.headings}: the {@link Heading}
1407
1410
  * node plus the derivations navigation wants — its `depth` and its plain-text
@@ -1755,8 +1758,6 @@ export declare class MarkdownDocument extends MarkdownDocument_base {
1755
1758
  */
1756
1759
  static readonly parse: (text: string, options?: MarkdownParseOptions | undefined) => Effect.Effect<MarkdownDocument, MarkdownParseError, never>;
1757
1760
  }
1758
- //#endregion
1759
- //#region src/Frontmatter.d.ts
1760
1761
  declare const FrontmatterFormatMismatchError_base: Schema.Class<FrontmatterFormatMismatchError, Schema.TaggedStruct<"FrontmatterFormatMismatchError", {
1761
1762
  /** The format the codec decodes. */
1762
1763
  readonly expected: Schema.Literals<readonly ["yaml", "toml", "json"]>;
@@ -2049,8 +2050,6 @@ export declare class MarkdownFrontmatter {
2049
2050
  */
2050
2051
  static setToString<T, E, RD = never, RE = never>(schema: Schema.Codec<T, E, RD, RE>, codec: FrontmatterCodec): (document: MarkdownDocument, data: T) => Effect.Effect<string, FrontmatterWriteError, RE>;
2051
2052
  }
2052
- //#endregion
2053
- //#region src/FrontmatterResolver.d.ts
2054
2053
  declare const SchemaDeclarationByUrl_base: Schema.Class<SchemaDeclarationByUrl, Schema.TaggedStruct<"ByUrl", {
2055
2054
  /** The URL as written in the declaration. */
2056
2055
  readonly url: Schema.String;
@@ -2280,8 +2279,6 @@ export declare class SchemaResolver {
2280
2279
  */
2281
2280
  static fromRegistry(registrations: Readonly<Record<string, Schema.Top>>): FrontmatterSchemaResolver;
2282
2281
  }
2283
- //#endregion
2284
- //#region src/FrontmatterSource.d.ts
2285
2282
  /**
2286
2283
  * The three line-terminator spellings a fence line may end with. Recorded by
2287
2284
  * {@link FrontmatterSource.split} as fidelity — what the source's fence
@@ -2418,8 +2415,6 @@ export declare class FrontmatterSource {
2418
2415
  */
2419
2416
  static join(split: FrontmatterSourceSplit): string;
2420
2417
  }
2421
- //#endregion
2422
- //#region src/JsonFrontmatter.d.ts
2423
2418
  /**
2424
2419
  * The json frontmatter codec, over `@effected/jsonc`.
2425
2420
  *
@@ -2445,8 +2440,6 @@ export declare class FrontmatterSource {
2445
2440
  * @public
2446
2441
  */
2447
2442
  export declare const JsonFrontmatter: FrontmatterCodec;
2448
- //#endregion
2449
- //#region src/MarkdownFormat.d.ts
2450
2443
  /**
2451
2444
  * A range accepted at the `format`/`formatToString` call sites: either a
2452
2445
  * {@link MarkdownRange} instance or a plain `{ offset, length }` literal (the
@@ -2660,8 +2653,249 @@ export declare class MarkdownFormat {
2660
2653
  */
2661
2654
  static readonly modifyToString: (document: MarkdownDocument, target: MarkdownNode, replacement: string | MarkdownNode) => Effect.Effect<string, MarkdownModificationError, never>;
2662
2655
  }
2663
- //#endregion
2664
- //#region src/MarkdownVisitor.d.ts
2656
+ /**
2657
+ * The node `type` tags whose handler may change the node's type — the
2658
+ * phrasing-only types, i.e. every member of `PhrasingContent` that is
2659
+ * not also flow content.
2660
+ *
2661
+ * @remarks
2662
+ * `html` is excluded: an `html` node is both phrasing and flow content, so it
2663
+ * can sit in a `Root`, `Blockquote` or `ListItem` child list, where only flow
2664
+ * content is valid. Its handler must therefore return an `Html`.
2665
+ *
2666
+ * @public
2667
+ */
2668
+ type MarkdownPhrasingOnlyType = Exclude<PhrasingContent, FlowContent>["type"];
2669
+ /**
2670
+ * The handler table for `MarkdownTransform.map`: at most one function
2671
+ * per node `type` tag, each receiving the node narrowed to its class.
2672
+ *
2673
+ * @remarks
2674
+ * A handler for a phrasing-only type (see {@link MarkdownPhrasingOnlyType})
2675
+ * may return any {@link (PhrasingContent:type)} — turning a `link` into a
2676
+ * `linkReference`, say. That is sound because every parent of a phrasing
2677
+ * node holds `PhrasingContent` children, so any phrasing node is valid in its
2678
+ * place. Every other handler must return a node of its own type, which keeps
2679
+ * `map` total: no handler can put a node where its parent forbids it.
2680
+ *
2681
+ * @public
2682
+ */
2683
+ type MarkdownTransformHandlers = { readonly [T in MarkdownNodeType]?: (node: MarkdownNodeOfType<T>) => T extends MarkdownPhrasingOnlyType ? PhrasingContent : MarkdownNodeOfType<T>; };
2684
+ /**
2685
+ * What `MarkdownTransform.map` returns for an input of type `N`: any
2686
+ * `PhrasingContent` when `N` is phrasing-only (its handler may change
2687
+ * its type), otherwise `N` itself.
2688
+ *
2689
+ * @public
2690
+ */
2691
+ type MarkdownTransformResult<N extends MarkdownNode> = N extends Exclude<PhrasingContent, FlowContent> ? PhrasingContent : N;
2692
+ /**
2693
+ * Options for {@link MarkdownTransform.shiftHeadings}: the inclusive depth
2694
+ * range shifted headings are clamped into.
2695
+ *
2696
+ * @public
2697
+ */
2698
+ interface ShiftHeadingsOptions {
2699
+ /** The shallowest depth a shifted heading may take. Defaults to `1`. */
2700
+ readonly min?: HeadingDepth;
2701
+ /** The deepest depth a shifted heading may take. Defaults to `6`. */
2702
+ readonly max?: HeadingDepth;
2703
+ }
2704
+ /**
2705
+ * Options for {@link MarkdownTransform.sections}: the heading depth to match
2706
+ * and the window of `root.children` indexes to search.
2707
+ *
2708
+ * @public
2709
+ */
2710
+ interface SectionSpanOptions {
2711
+ /** The exact heading depth to match. */
2712
+ readonly depth: HeadingDepth;
2713
+ /** The first child index searched, inclusive. Defaults to `0`. */
2714
+ readonly start?: number;
2715
+ /** The child index the search stops at, exclusive. Defaults to `root.children.length`. */
2716
+ readonly end?: number;
2717
+ }
2718
+ /**
2719
+ * A root-level section located by index: the heading, its index in
2720
+ * `root.children`, and the exclusive index its section ends at. The body is
2721
+ * the children at `[headingIndex + 1, end)`.
2722
+ *
2723
+ * @public
2724
+ */
2725
+ interface SectionSpan {
2726
+ readonly heading: Heading;
2727
+ readonly headingIndex: number;
2728
+ readonly end: number;
2729
+ }
2730
+ /**
2731
+ * Pure, total tree-to-tree transforms over the markdown node classes. Not
2732
+ * instantiable.
2733
+ *
2734
+ * @remarks
2735
+ * Every method returns a plain value — none has an error channel. They work
2736
+ * on a tree, never on source text, so they compose with
2737
+ * {@link Markdown.stringify} for output and with each other; offset-splice
2738
+ * editing stays the job of `MarkdownFormat` and `MarkdownEdit`.
2739
+ *
2740
+ * @example
2741
+ * ```ts
2742
+ * import { LinkReference, Markdown, MarkdownTransform } from "@effected/markdown";
2743
+ * import { Result } from "effect";
2744
+ *
2745
+ * const parsed = Markdown.parseResult("# Title\n\nSee [docs](https://example.com).\n");
2746
+ * if (Result.isSuccess(parsed)) {
2747
+ * const shifted = MarkdownTransform.shiftHeadings(parsed.success, 1);
2748
+ * const rewritten = MarkdownTransform.map(shifted, {
2749
+ * link: (link) =>
2750
+ * LinkReference.make({ identifier: "docs", referenceType: "full", children: link.children }),
2751
+ * });
2752
+ * Markdown.stringifyResult(rewritten); // "## Title\n\nSee [docs][docs].\n"
2753
+ * }
2754
+ * ```
2755
+ *
2756
+ * @public
2757
+ */
2758
+ export declare class MarkdownTransform {
2759
+ private constructor();
2760
+ /**
2761
+ * Rebuild a tree by applying `handlers` to its nodes, bottom-up.
2762
+ *
2763
+ * @remarks
2764
+ * **Walk order is a contract:** post-order, children left to right. A
2765
+ * handler sees its node with its children ALREADY transformed, and
2766
+ * handlers are called in document order of the nodes' closing positions,
2767
+ * so a stateful handler can rely on it — deduplicating while keeping the
2768
+ * first occurrence, or remembering the last paragraph seen. The input
2769
+ * node itself is visited last.
2770
+ *
2771
+ * **Structural sharing:** a subtree no handler changed is returned as the
2772
+ * same instance (`===`), so `map(node, {})` returns `node`. A parent whose
2773
+ * children changed is rebuilt with only `children` swapped — every other
2774
+ * field, including `position`, kept as it was. A rebuilt parent therefore
2775
+ * carries its ORIGINAL `position`: offsets into the pre-transform source,
2776
+ * which no longer describe the transformed tree.
2777
+ *
2778
+ * **Handler typing keeps the map total.** A handler for a phrasing-only
2779
+ * type ({@link MarkdownPhrasingOnlyType}) may return any phrasing node —
2780
+ * a `link` may become a `linkReference` — because every parent of a
2781
+ * phrasing node holds phrasing children. Every other handler must return a
2782
+ * node of its own type. `html` is the one phrasing type excluded from the
2783
+ * type-changing set: it is a member of BOTH the phrasing and the flow
2784
+ * unions, so an `html` node can be a child of a `Root`, `Blockquote` or
2785
+ * `ListItem`, where only flow content is valid — its handler must return
2786
+ * an `Html`, and `map` of an `Html` returns an `Html`. See
2787
+ * {@link MarkdownTransformHandlers}. A handler that throws propagates the
2788
+ * exception unchanged; `map` has no error channel of its own.
2789
+ *
2790
+ * The walk recurses once per nesting level. Every tree `Markdown.parse` or
2791
+ * `Mdast.fromMdast` produces stays within the package's nesting cap, but a
2792
+ * hand-built tree nested far deeper than that can overflow the call stack.
2793
+ *
2794
+ * **Two signatures.** This one covers every node whose handler cannot
2795
+ * change its type — a `Root`, any flow node, a list item, a table row or
2796
+ * cell, an `html` node — and returns `N` itself, so a generic caller
2797
+ * constrained to, say, `Root | FlowContent` gets its own type parameter
2798
+ * back. A lone phrasing-only node resolves to the second signature, whose
2799
+ * result widens to `PhrasingContent`.
2800
+ *
2801
+ * @param node - The node to transform — typically a {@link Root}.
2802
+ * @param handlers - At most one function per node `type` tag.
2803
+ * @returns The transformed node, of the input's own type: `node` itself
2804
+ * when nothing changed.
2805
+ */
2806
+ static map<N extends Exclude<MarkdownNode, Exclude<PhrasingContent, FlowContent>>>(node: N, handlers: MarkdownTransformHandlers): N;
2807
+ /**
2808
+ * Rebuild a tree by applying `handlers` to its nodes, bottom-up — the
2809
+ * signature for any node, including a lone phrasing-only node whose own
2810
+ * handler may change its type.
2811
+ *
2812
+ * @remarks
2813
+ * Walk order, structural sharing, handler typing and the recursion limit
2814
+ * are exactly as documented on the first signature. The difference is the
2815
+ * result type, {@link MarkdownTransformResult}: for a phrasing-only node
2816
+ * (a `link`, `text`, `emphasis`, ...) it is any `PhrasingContent`, since
2817
+ * that node's handler may return, say, a `linkReference` for a `link`. For
2818
+ * every other node it is `N`, but a caller holding a node of a known
2819
+ * non-phrasing type resolves to the first signature, which returns `N`
2820
+ * without the conditional type.
2821
+ *
2822
+ * @param node - Any node.
2823
+ * @param handlers - At most one function per node `type` tag.
2824
+ * @returns The transformed node: `node` itself when nothing changed.
2825
+ */
2826
+ static map<N extends MarkdownNode>(node: N, handlers: MarkdownTransformHandlers): MarkdownTransformResult<N>;
2827
+ /**
2828
+ * Shift every heading's depth by `by`, clamped into `[min, max]`.
2829
+ *
2830
+ * @remarks
2831
+ * The new depth is `clamp(depth + by, min ?? 1, max ?? 6)`. A heading
2832
+ * whose new depth exceeds 2 loses `headingStyle: "setext"` (the key is
2833
+ * omitted), since setext spells depths 1 and 2 only; it then serializes
2834
+ * as ATX. A heading whose depth does not change, and needs no setext
2835
+ * drop, keeps its identity; structural sharing follows
2836
+ * {@link MarkdownTransform.(map:1)}, on which this is built. Accepts any node,
2837
+ * including a lone {@link Heading}.
2838
+ *
2839
+ * Built on `map`, it recurses the same way: a hand-built tree nested far
2840
+ * deeper than anything `Markdown.parse` or `Mdast.fromMdast` produces can
2841
+ * overflow the call stack.
2842
+ *
2843
+ * @param node - The node whose headings to shift.
2844
+ * @param by - The signed depth offset; must be an integer.
2845
+ * @param options - Optional {@link ShiftHeadingsOptions} clamp bounds.
2846
+ * @returns The node with headings shifted.
2847
+ * @throws `RangeError` when `by` is not an integer, or `min` / `max` is not
2848
+ * a heading depth or `min > max` — a wiring error, not a data error.
2849
+ */
2850
+ static shiftHeadings<N extends MarkdownNode>(node: N, by: number, options?: ShiftHeadingsOptions): N;
2851
+ /**
2852
+ * Locate the root-level sections of one heading depth by child index —
2853
+ * no source text needed, so it works on a tree a
2854
+ * `MarkdownTransform.map` has reshaped.
2855
+ *
2856
+ * @remarks
2857
+ * Returns the root-level headings of exactly `depth` whose index lies in
2858
+ * `[start, end)`, in document order. Each section ends at the next
2859
+ * root-level heading of equal or shallower depth, or at `end` — the same
2860
+ * boundary rule as `MarkdownDocument.sections`, from one shared
2861
+ * implementation. The window is what lets a caller nest: find the depth-2
2862
+ * blocks, then the depth-3 sections inside one block's
2863
+ * `[headingIndex + 1, end)`.
2864
+ *
2865
+ * **A shallower heading ends a section too.** Querying depth 2 on
2866
+ * `## Features`, a list, a stray `# Top` and another list yields one span
2867
+ * whose `end` is the `# Top` heading's index, so neither `# Top` nor the
2868
+ * list after it belongs to the section. That is the outline reading. A
2869
+ * caller that wants each section to run to the next heading of the SAME
2870
+ * depth (slicing a changeset or changelog by entry, where a stray H1 is
2871
+ * content) computes each body's end from the next span's `headingIndex`
2872
+ * instead, or the window's end for the last span, as in the example.
2873
+ *
2874
+ * @example
2875
+ * Bodies that run to the next same-depth heading:
2876
+ *
2877
+ * ```ts
2878
+ * import { Markdown, MarkdownTransform } from "@effected/markdown";
2879
+ * import { Result } from "effect";
2880
+ *
2881
+ * const root = Result.getOrThrow(Markdown.parseResult("## Features\n\n- a\n\n# Top\n\n- b\n"));
2882
+ * const spans = MarkdownTransform.sections(root, { depth: 2 });
2883
+ * // spans[0].end === 2: the outline section stops at `# Top`
2884
+ * const bodies = spans.map((span, index) =>
2885
+ * root.children.slice(span.headingIndex + 1, spans[index + 1]?.headingIndex ?? root.children.length),
2886
+ * );
2887
+ * // bodies[0] holds the first list, `# Top` and the second list
2888
+ * ```
2889
+ *
2890
+ * @param root - The tree whose root-level headings delimit sections.
2891
+ * @param options - The {@link SectionSpanOptions} depth and window.
2892
+ * @returns The matching {@link SectionSpan}s, in document order.
2893
+ * @throws `RangeError` when `depth` is not an integer from 1 to 6, when
2894
+ * `start` or `end` is not an integer in `[0, root.children.length]`, or
2895
+ * when `start > end`.
2896
+ */
2897
+ static sections(root: Root, options: SectionSpanOptions): ReadonlyArray<SectionSpan>;
2898
+ }
2665
2899
  /**
2666
2900
  * The discriminated union of markdown visitor events, in document pre-order.
2667
2901
  * `Enter` fires when the walk reaches a node, `Exit` when it leaves — for a
@@ -2856,8 +3090,6 @@ export declare class MarkdownVisitor {
2856
3090
  */
2857
3091
  static visit(root: Root): Stream.Stream<MarkdownVisitorEvent>;
2858
3092
  }
2859
- //#endregion
2860
- //#region src/Mdast.d.ts
2861
3093
  /**
2862
3094
  * A plain mdast node: a `type` tag plus whatever fields that type carries.
2863
3095
  *
@@ -2872,6 +3104,17 @@ interface MdastNode {
2872
3104
  readonly type: string;
2873
3105
  readonly [key: string]: unknown;
2874
3106
  }
3107
+ /**
3108
+ * Options for {@link Mdast.toText}, matching `mdast-util-to-string`'s.
3109
+ *
3110
+ * @public
3111
+ */
3112
+ interface MdastToTextOptions {
3113
+ /** Whether an `image` or `imageReference` contributes its `alt` text. Defaults to `true`. */
3114
+ readonly includeImageAlt?: boolean;
3115
+ /** Whether an `html` node contributes its raw value. Defaults to `true`. */
3116
+ readonly includeHtml?: boolean;
3117
+ }
2875
3118
  declare const MdastDecodeError_base: Schema.Class<MdastDecodeError, Schema.TaggedStruct<"MdastDecodeError", {
2876
3119
  /** The structured schema issue. Never a string. */
2877
3120
  readonly issue: Schema.Defect;
@@ -2993,9 +3236,40 @@ export declare class Mdast {
2993
3236
  * fails with {@link MdastDecodeError}.
2994
3237
  */
2995
3238
  static readonly fromMdast: (input: unknown) => Effect.Effect<Root, MdastDecodeError, never>;
3239
+ /**
3240
+ * The plain-text content of a node, defined as `mdast-util-to-string`
3241
+ * applied to the node's {@link Mdast.toMdast} projection.
3242
+ *
3243
+ * @remarks
3244
+ * A node carrying a string `value` yields it (an `html` node only when
3245
+ * `includeHtml`); an `image` or `imageReference` yields its non-empty
3246
+ * `alt` when `includeImageAlt`; a node with children yields the
3247
+ * concatenation of its children's text; anything else — a `break`, a
3248
+ * `thematicBreak`, a `definition` — yields `""`. Both options default to
3249
+ * `true`. Pure and total.
3250
+ *
3251
+ * **Values are read as the projection spells them.** The one value the
3252
+ * projection changes is a `code` node's: this package keeps the final line
3253
+ * ending of a code block's content, plain mdast does not, so `toText`
3254
+ * strips it exactly as {@link Mdast.toMdast} does — `"let x"`, not
3255
+ * `"let x\n"`, for a fenced block holding one line.
3256
+ *
3257
+ * This is the remark-ecosystem derivation, and it deliberately differs
3258
+ * from the display derivation behind `DocumentHeading.text` and
3259
+ * `DocumentSection.text`, which render a hard break as a space and drop
3260
+ * raw HTML. Reach for `toText` when matching what remark tooling computes
3261
+ * (slugs, search indexes); reach for the document accessors for display.
3262
+ *
3263
+ * The walk recurses once per nesting level. Every tree `Markdown.parse` or
3264
+ * `Mdast.fromMdast` produces stays within the package's nesting cap, but a
3265
+ * hand-built tree nested far deeper than that can overflow the call stack.
3266
+ *
3267
+ * @param node - The node to flatten.
3268
+ * @param options - Optional {@link MdastToTextOptions}.
3269
+ * @returns The node's text content.
3270
+ */
3271
+ static toText(node: MarkdownNode, options?: MdastToTextOptions): string;
2996
3272
  }
2997
- //#endregion
2998
- //#region src/TomlFrontmatter.d.ts
2999
3273
  /**
3000
3274
  * The toml frontmatter codec, over `@effected/toml`.
3001
3275
  *
@@ -3019,8 +3293,6 @@ export declare class Mdast {
3019
3293
  * @public
3020
3294
  */
3021
3295
  export declare const TomlFrontmatter: FrontmatterCodec;
3022
- //#endregion
3023
- //#region src/YamlFrontmatter.d.ts
3024
3296
  /**
3025
3297
  * The yaml frontmatter codec, over `@effected/yaml`.
3026
3298
  *
@@ -3045,6 +3317,5 @@ export declare const TomlFrontmatter: FrontmatterCodec;
3045
3317
  * @public
3046
3318
  */
3047
3319
  export declare const YamlFrontmatter: FrontmatterCodec;
3048
- //#endregion
3049
- export type { DocumentHeading, DocumentLink, FrontmatterCodec, FrontmatterResolveError, FrontmatterSchemaError, FrontmatterSchemaResolver, FrontmatterWriteError, LinkBearingNode, MarkdownNodeOfType, MarkdownNodeType, MarkdownPath, MarkdownRangeLike, MarkdownSegment, MdastNode, SectionHeadingMatch, SectionQueryOptions };
3320
+ export type { DocumentHeading, DocumentLink, FrontmatterCodec, FrontmatterResolveError, FrontmatterSchemaError, FrontmatterSchemaResolver, FrontmatterWriteError, LinkBearingNode, MarkdownNodeOfType, MarkdownNodeType, MarkdownPath, MarkdownPhrasingOnlyType, MarkdownRangeLike, MarkdownSegment, MarkdownTransformHandlers, MarkdownTransformResult, MdastNode, MdastToTextOptions, SectionHeadingMatch, SectionQueryOptions, SectionSpan, SectionSpanOptions, ShiftHeadingsOptions };
3050
3321
  //# sourceMappingURL=index.d.ts.map
package/index.js CHANGED
@@ -8,9 +8,10 @@ import { MarkdownDiagnostic, MarkdownParseErrorCode } from "./MarkdownDiagnostic
8
8
  import { Markdown, MarkdownDialect, MarkdownParseError, MarkdownParseOptions, MarkdownStringifyError } from "./Markdown.js";
9
9
  import { DocumentSection, MarkdownDocument } from "./MarkdownDocument.js";
10
10
  import { CodeBlockStyle, MarkdownFormat, MarkdownFormattingOptions, MarkdownModificationError, MarkdownModificationErrorCode } from "./MarkdownFormat.js";
11
+ import { MarkdownTransform } from "./MarkdownTransform.js";
11
12
  import { MarkdownVisitor, MarkdownVisitorEvent } from "./MarkdownVisitor.js";
12
13
  import { Mdast, MdastDecodeError } from "./Mdast.js";
13
14
  import { TomlFrontmatter } from "./TomlFrontmatter.js";
14
15
  import { YamlFrontmatter } from "./YamlFrontmatter.js";
15
16
 
16
- export { Blockquote, Break, BreakStyle, BulletChar, Code, CodeBlockStyle, Definition, Delete, DocumentSection, Emphasis, EmphasisChar, FenceChar, FlowContent, FootnoteDefinition, FootnoteReference, Frontmatter, FrontmatterContent, FrontmatterDecodeError, FrontmatterEncodeError, FrontmatterFormat, FrontmatterFormatMismatchError, FrontmatterMissingError, FrontmatterMissingReason, FrontmatterNewline, FrontmatterSource, FrontmatterSourceBlock, FrontmatterSourceSplit, FrontmatterValidationError, Heading, HeadingDepth, HeadingStyle, Html, Image, ImageReference, InlineCode, JsonFrontmatter, Link, LinkReference, List, ListContent, ListDelimiter, ListItem, Markdown, MarkdownDiagnostic, MarkdownDialect, MarkdownDocument, MarkdownEdit, MarkdownFormat, MarkdownFormattingOptions, MarkdownFrontmatter, MarkdownModificationError, MarkdownModificationErrorCode, MarkdownNode, MarkdownParseError, MarkdownParseErrorCode, MarkdownParseOptions, MarkdownRange, MarkdownStringifyError, MarkdownVisitor, MarkdownVisitorEvent, Mdast, MdastDecodeError, MdxFlowExpression, MdxJsxAttribute, MdxJsxAttributeContent, MdxJsxAttributeValueExpression, MdxJsxExpressionAttribute, MdxJsxFlowElement, MdxJsxTextElement, MdxTextExpression, MdxjsEsm, Paragraph, PhrasingContent, Point, Position, ReferenceType, Root, RowContent, SchemaDeclaration, SchemaDeclarationByName, SchemaDeclarationByPath, SchemaDeclarationByUrl, SchemaDeclarationInline, SchemaDeclarationInvalidError, SchemaDeclarationMissingError, SchemaNameUnknownError, SchemaResolver, SchemaVersionUnresolvableError, Strong, Table, TableAlign, TableCell, TableContent, TableRow, Text, ThematicBreak, ThematicBreakChar, TomlFrontmatter, YamlFrontmatter };
17
+ export { Blockquote, Break, BreakStyle, BulletChar, Code, CodeBlockStyle, Definition, Delete, DocumentSection, Emphasis, EmphasisChar, FenceChar, FlowContent, FootnoteDefinition, FootnoteReference, Frontmatter, FrontmatterContent, FrontmatterDecodeError, FrontmatterEncodeError, FrontmatterFormat, FrontmatterFormatMismatchError, FrontmatterMissingError, FrontmatterMissingReason, FrontmatterNewline, FrontmatterSource, FrontmatterSourceBlock, FrontmatterSourceSplit, FrontmatterValidationError, Heading, HeadingDepth, HeadingStyle, Html, Image, ImageReference, InlineCode, JsonFrontmatter, Link, LinkReference, List, ListContent, ListDelimiter, ListItem, Markdown, MarkdownDiagnostic, MarkdownDialect, MarkdownDocument, MarkdownEdit, MarkdownFormat, MarkdownFormattingOptions, MarkdownFrontmatter, MarkdownModificationError, MarkdownModificationErrorCode, MarkdownNode, MarkdownParseError, MarkdownParseErrorCode, MarkdownParseOptions, MarkdownRange, MarkdownStringifyError, MarkdownTransform, MarkdownVisitor, MarkdownVisitorEvent, Mdast, MdastDecodeError, MdxFlowExpression, MdxJsxAttribute, MdxJsxAttributeContent, MdxJsxAttributeValueExpression, MdxJsxExpressionAttribute, MdxJsxFlowElement, MdxJsxTextElement, MdxTextExpression, MdxjsEsm, Paragraph, PhrasingContent, Point, Position, ReferenceType, Root, RowContent, SchemaDeclaration, SchemaDeclarationByName, SchemaDeclarationByPath, SchemaDeclarationByUrl, SchemaDeclarationInline, SchemaDeclarationInvalidError, SchemaDeclarationMissingError, SchemaNameUnknownError, SchemaResolver, SchemaVersionUnresolvableError, Strong, Table, TableAlign, TableCell, TableContent, TableRow, Text, ThematicBreak, ThematicBreakChar, TomlFrontmatter, YamlFrontmatter };
@@ -8,7 +8,7 @@ import { GuardExceeded } from "./carriers.js";
8
8
  import { LineIndex } from "./lineIndex.js";
9
9
  import { prepareInline } from "./rawInline.js";
10
10
 
11
- //#region src/internal/blockParser.ts
11
+ // src/internal/blockParser.ts
12
12
  /** Upstream's cheap pre-filter: a line that cannot start any block. */
13
13
  const reMaybeSpecial = /^[#`~*+_=<>0-9-]/;
14
14
  var BlockParser = class {
@@ -415,5 +415,4 @@ var BlockParser = class {
415
415
  */
416
416
  const parseBlocks = (text, dialect = "commonmark", frontmatter = false) => new BlockParser(text, blockDialect(dialect), dialect, frontmatter).parse();
417
417
 
418
- //#endregion
419
418
  export { parseBlocks };
@@ -14,7 +14,7 @@ import { tableCellConstruct, tableConstruct, tableHeaderStart, tableRowConstruct
14
14
  import { taskListItemStart } from "./blocks/taskListItem.js";
15
15
  import { thematicBreakConstruct, thematicBreakStart } from "./blocks/thematicBreak.js";
16
16
 
17
- //#region src/internal/blockRegistry.ts
17
+ // src/internal/blockRegistry.ts
18
18
  const constructTable = (constructs) => new Map(constructs.map((construct) => [construct.type, construct]));
19
19
  const commonmarkDialect = {
20
20
  constructs: constructTable([
@@ -86,5 +86,4 @@ const blockDialect = (dialect) => {
86
86
  return found;
87
87
  };
88
88
 
89
- //#endregion
90
89
  export { blockDialect };
@@ -1,4 +1,4 @@
1
- //#region src/internal/blockTypes.ts
1
+ // src/internal/blockTypes.ts
2
2
  /** Narrow materialized children to the flow content most constructs contain. */
3
3
  const flowChildren = (children) => children.filter((child) => child.type !== "root" && child.type !== "listItem" && child.type !== "tableRow" && child.type !== "tableCell");
4
4
  /** Narrow materialized children to the list items a list contains. */
@@ -23,5 +23,4 @@ const makeBlockNode = (type, startOffset, startLine, depth = 0) => ({
23
23
  data: {}
24
24
  });
25
25
 
26
- //#endregion
27
26
  export { flowChildren, listItemChildren, makeBlockNode, tableCellChildren, tableRowChildren };
@@ -1,6 +1,6 @@
1
1
  import { Heading } from "../../MarkdownNode.js";
2
2
 
3
- //#region src/internal/blocks/atxHeading.ts
3
+ // src/internal/blocks/atxHeading.ts
4
4
  const reATXHeadingMarker = /^#{1,6}(?:[ \t]+|$)/;
5
5
  const reOnlyTrailingHashes = /^[ \t]*#+[ \t]*$/;
6
6
  const reClosingHashes = /[ \t]+#+[ \t]*$/;
@@ -50,5 +50,4 @@ const atxHeadingStart = {
50
50
  }
51
51
  };
52
52
 
53
- //#endregion
54
53
  export { atxHeadingStart, headingConstruct };
@@ -2,7 +2,7 @@ import { Blockquote } from "../../MarkdownNode.js";
2
2
  import { flowChildren } from "../blockTypes.js";
3
3
  import { isSpaceOrTab, peekCode } from "../preprocess.js";
4
4
 
5
- //#region src/internal/blocks/blockquote.ts
5
+ // src/internal/blocks/blockquote.ts
6
6
  const C_GREATERTHAN = 62;
7
7
  /** Blockquote: continues while each line carries its `>` marker. */
8
8
  const blockquoteConstruct = {
@@ -36,5 +36,4 @@ const blockquoteStart = {
36
36
  }
37
37
  };
38
38
 
39
- //#endregion
40
39
  export { blockquoteConstruct, blockquoteStart };
@@ -2,7 +2,7 @@ import { Code } from "../../MarkdownNode.js";
2
2
  import { isSpaceOrTab, peekCode } from "../preprocess.js";
3
3
  import { unescapeString } from "../unescape.js";
4
4
 
5
- //#region src/internal/blocks/code.ts
5
+ // src/internal/blocks/code.ts
6
6
  const reBlankLine = /^[ \t]*$/;
7
7
  const reClosingCodeFence = /^(?:`{3,}|~{3,})(?=[ \t]*$)/;
8
8
  /**
@@ -86,5 +86,4 @@ const codeConstruct = {
86
86
  }
87
87
  };
88
88
 
89
- //#endregion
90
89
  export { codeConstruct };
@@ -1,6 +1,6 @@
1
1
  import { Root } from "../../MarkdownNode.js";
2
2
 
3
- //#region src/internal/blocks/document.ts
3
+ // src/internal/blocks/document.ts
4
4
  /** The document root: contains everything except a bare list item. */
5
5
  const documentConstruct = {
6
6
  type: "document",
@@ -13,5 +13,4 @@ const documentConstruct = {
13
13
  })
14
14
  };
15
15
 
16
- //#endregion
17
16
  export { documentConstruct };
@@ -1,4 +1,4 @@
1
- //#region src/internal/blocks/fencedCode.ts
1
+ // src/internal/blocks/fencedCode.ts
2
2
  const reCodeFence = /^`{3,}(?!.*`)|^~{3,}/;
3
3
  const fenceCharOf = (char) => char === "`" || char === "~" ? char : void 0;
4
4
  /** The fenced-code block start: three or more backticks or tildes. */
@@ -22,5 +22,4 @@ const fencedCodeStart = {
22
22
  }
23
23
  };
24
24
 
25
- //#endregion
26
25
  export { fencedCodeStart };
@@ -3,7 +3,7 @@ import { flowChildren } from "../blockTypes.js";
3
3
  import { stickyOf } from "../patterns.js";
4
4
  import { normalizeLabelText } from "../references.js";
5
5
 
6
- //#region src/internal/blocks/footnoteDefinition.ts
6
+ // src/internal/blocks/footnoteDefinition.ts
7
7
  /**
8
8
  * `_scan_footnote_definition` from `src/scanners.re`:
9
9
  *
@@ -80,5 +80,4 @@ const footnoteDefinitionStart = {
80
80
  }
81
81
  };
82
82
 
83
- //#endregion
84
83
  export { footnoteDefinitionConstruct, footnoteDefinitionStart };
@@ -1,4 +1,4 @@
1
- //#region src/internal/blocks/frontmatter.ts
1
+ // src/internal/blocks/frontmatter.ts
2
2
  const FENCES = /* @__PURE__ */ new Map([
3
3
  ["---", {
4
4
  format: "yaml",
@@ -98,5 +98,4 @@ const scanRawFrontmatter = (source) => {
98
98
  return null;
99
99
  };
100
100
 
101
- //#endregion
102
101
  export { scanFrontmatter, scanRawFrontmatter };
@@ -2,7 +2,7 @@ import { Html } from "../../MarkdownNode.js";
2
2
  import { peekCode } from "../preprocess.js";
3
3
  import { CLOSETAG, OPENTAG } from "../htmlTags.js";
4
4
 
5
- //#region src/internal/blocks/htmlBlock.ts
5
+ // src/internal/blocks/htmlBlock.ts
6
6
  const C_LESSTHAN = 60;
7
7
  const reHtmlBlockOpen = [
8
8
  /./,
@@ -68,5 +68,4 @@ const htmlBlockStart = {
68
68
  }
69
69
  };
70
70
 
71
- //#endregion
72
71
  export { htmlBlockConstruct, htmlBlockStart, isHtmlBlockEnd };