@effected/markdown 0.6.3 → 0.7.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/FrontmatterSource.js +188 -0
- package/Markdown.js +86 -0
- package/MarkdownDocument.js +4 -3
- package/MarkdownNode.js +154 -3
- package/Mdast.js +66 -1
- package/README.md +38 -0
- package/index.d.ts +375 -8
- package/index.js +3 -2
- package/internal/blockParser.js +1 -1
- package/internal/blocks/frontmatter.js +47 -1
- package/internal/phrasing.js +57 -0
- package/internal/rawInline.js +3 -1
- package/internal/stringify.js +143 -3
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -174,6 +174,8 @@ The three codecs — `YamlFrontmatter` (`---`), `TomlFrontmatter` (`+++`) and `J
|
|
|
174
174
|
|
|
175
175
|
Frontmatter blocks can also describe their own schema. `SchemaResolver.classify` sorts a `$schema` value into a tagged union — `ByUrl`, `ByPath`, `Inline` and `ByName` — and `SchemaResolver.fromRegistry` resolves `ByName` declarations like `skill@2.1.0` against schemas you register. URLs, paths and inline documents are carried as data and never fetched: this is a pure package and it performs no IO.
|
|
176
176
|
|
|
177
|
+
When the body should not go through the CommonMark engine at all — an MDX page, a template, a snapshot-hash contract that needs byte-exact boundaries — `FrontmatterSource.split` runs the same closed fence grammar over the raw string and nothing else. It is total: it returns the block's format, the exact bytes between the fence lines and the body remainder (`body` is always exactly `source.slice(bodyOffset)`), with absence a representable result rather than an error. `FrontmatterSource.join` serializes the parts back, reproducing the original bytes for an unmodified round-trip. Two edges normalize instead of reproducing: a closing fence that ends the document without a line terminator gains one, and fence lines with mismatched terminators (say `---\r\n … ---\n`) re-emit both with the opening fence's. The value and body bytes survive verbatim in every case, and neither is ever parsed at this level — decoding stays the codec modules' business.
|
|
178
|
+
|
|
177
179
|
## Working with the tree
|
|
178
180
|
|
|
179
181
|
`MarkdownDocument` derives its navigation accessors from the tree, so they can never disagree with it. `links` collects every URL-bearing node and passes `url` through exactly as written — bundle-relative hrefs are never normalized:
|
|
@@ -261,6 +263,37 @@ if (Result.isSuccess(parsed)) {
|
|
|
261
263
|
|
|
262
264
|
`Mdast.fromMdast` goes the other way as a checked admission boundary: it validates a foreign tree and synthesizes zero-width sentinel positions where one is absent or incomplete. Trees admitted that way serve tree-level workflows and canonical `stringify`; offset-splice editing needs the real positions only a parse produces.
|
|
263
265
|
|
|
266
|
+
## MDX trees
|
|
267
|
+
|
|
268
|
+
The MDX node vocabulary — `MdxJsxFlowElement` and `MdxJsxTextElement` (with `MdxJsxAttribute`, `MdxJsxExpressionAttribute` and `MdxJsxAttributeValueExpression` as their attribute carriers), `MdxFlowExpression`, `MdxTextExpression` and `MdxjsEsm` — is shaped exactly to the `mdast-util-mdx` contracts and constructs like every other node class. The parser does not read MDX syntax; these nodes exist so a **synthesized** tree can carry JSX and serialize to valid MDX instead of joining strings:
|
|
269
|
+
|
|
270
|
+
```ts
|
|
271
|
+
import { Markdown, MdxJsxAttribute, MdxJsxAttributeValueExpression, MdxJsxFlowElement, Root } from "@effected/markdown";
|
|
272
|
+
import { Result } from "effect";
|
|
273
|
+
|
|
274
|
+
const page = Root.make({
|
|
275
|
+
children: [
|
|
276
|
+
MdxJsxFlowElement.make({
|
|
277
|
+
name: "ApiSignature",
|
|
278
|
+
attributes: [
|
|
279
|
+
MdxJsxAttribute.make({
|
|
280
|
+
name: "code",
|
|
281
|
+
value: MdxJsxAttributeValueExpression.make({ value: JSON.stringify({ lang: "ts" }) }),
|
|
282
|
+
}),
|
|
283
|
+
],
|
|
284
|
+
children: [],
|
|
285
|
+
}),
|
|
286
|
+
],
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
const mdx = Markdown.stringifyResult(page);
|
|
290
|
+
if (Result.isSuccess(mdx)) {
|
|
291
|
+
console.log(mdx.success); // <ApiSignature code={{"lang":"ts"}} />
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Serialization matches `mdxJsxToMarkdown`'s defaults — `"` quotes, spaced self-closing `<a />`, `<></>` fragments, flow children indented two spaces per JSX ancestor, `{expr}` expressions, ESM values verbatim — and a tree containing any MDX node escapes `{` in text the way MDX requires, while a tree with none serializes byte-identically to the table below. The shapes that have no MDX spelling are unconstructible rather than serialization errors: a fragment cannot carry attributes and an attribute requires a non-empty name, refused at `make` and at decode.
|
|
296
|
+
|
|
264
297
|
## The canonical form is stable
|
|
265
298
|
|
|
266
299
|
`Markdown.stringify` takes no options. It emits one canonical form, that form is pinned by byte-level tests, the engine behind it is cross-checked against commonmark.js over the full CommonMark 0.31.2 corpus, and **changing any of it is a breaking change to this package rather than a patch**. So a test may assert on these bytes, and a pipeline that needs stable rendered markdown should serialize through here instead of a third-party stringifier whose defaults are free to move between releases.
|
|
@@ -278,6 +311,8 @@ For a node carrying no fidelity field:
|
|
|
278
311
|
| Block separation | exactly one blank line |
|
|
279
312
|
| Document | a single trailing newline |
|
|
280
313
|
|
|
314
|
+
MDX nodes are part of the same commitment: the serialization choices listed under [MDX trees](#mdx-trees) are pinned the same way, and only trees that actually carry an MDX node pay the extra `{` escape.
|
|
315
|
+
|
|
281
316
|
**Representability wins over the table.** The canonical form never emits text that would re-parse as something else, so a row yields where the two conflict. The case that reaches a consumer is the indented code block: an indented block directly after a list is absorbed as list content, so a `Code` node with neither `lang` nor `fenceChar` emits fenced in that position and indented everywhere else. A byte-level assertion over synthesized code blocks therefore depends on the preceding sibling.
|
|
282
317
|
|
|
283
318
|
The posture that makes this a non-issue — and the one to adopt if you build trees and assert on their bytes — is to set `fenceChar` on every `Code` node carrying neither a `lang` nor one already, in a post-decode walk that reaches nested nodes. The choice then leaves the emitter entirely and no output depends on a node's neighbours.
|
|
@@ -291,8 +326,11 @@ To *normalize* an existing document to different choices, use `MarkdownFormat` w
|
|
|
291
326
|
## Features
|
|
292
327
|
|
|
293
328
|
- `Markdown` — `parse`/`stringify` as `Effect`s with typed `MarkdownParseError`/`MarkdownStringifyError` channels, the pure `parseResult`/`stringifyResult` twins for synchronous callers, and the `MarkdownFromString` two-way codec.
|
|
329
|
+
- `Markdown.parsePhrasing`/`parsePhrasingResult` — parse a prose fragment as a single paragraph's inline content, without a full document parse and a paragraph splice: blank lines stay inline, references never form (no reference context), and positions are correct relative to the input string.
|
|
294
330
|
- `MarkdownDocument` — source, tree, diagnostics and the link-definition index, plus the derived `headings`, `sections` and `links` accessors, the `firstSection`/`sectionByHeading` finders over a `DocumentSection` (heading, depth, range, body), the `find`/`findAll` tree queries over type-narrowed selectors, and the `frontmatter` capture with `hasFrontmatterBlock` to tell an absent block from an uncaptured one.
|
|
295
331
|
- The mdast-shaped node classes — the CommonMark types plus GFM's `delete`, `table`, `tableRow`, `tableCell`, `footnoteDefinition`, `footnoteReference` and task-list `checked`, each carrying unist positions with byte offsets and this package's fidelity fields. `position` defaults to the zero-width synthetic sentinel, so a replacement fragment constructs in one line — `Text.make({ value: "shipped" })`.
|
|
332
|
+
- The MDX node vocabulary — `MdxJsxFlowElement`/`MdxJsxTextElement` with their attribute carriers, `MdxFlowExpression`/`MdxTextExpression` and `MdxjsEsm`, shaped to the `mdast-util-mdx` contracts for construction and serialization (the parser reads no MDX syntax).
|
|
333
|
+
- `FrontmatterSource` — string-level frontmatter `split`/`join` over the same closed fence grammar, byte-exact boundaries, no parsing of value or body; for bodies the CommonMark engine should not touch.
|
|
296
334
|
- `MarkdownEdit` / `MarkdownRange` (with `applyAll`) — the non-mutating text-edit vocabulary, field-identical to `@effected/jsonc`'s, `@effected/yaml`'s and `@effected/toml`'s.
|
|
297
335
|
- `MarkdownFormat` — `format`/`formatToString` compute conservative marker-normalization edits; `modify`/`modifyToString` replace a node by identity through the canonical stringifier.
|
|
298
336
|
- `MarkdownVisitor` — walk a parsed tree as a lazy `Stream` of `Enter`/`Exit` events with child-index paths and depth.
|
package/index.d.ts
CHANGED
|
@@ -353,11 +353,14 @@ declare class FootnoteReference extends FootnoteReference_base {}
|
|
|
353
353
|
*/
|
|
354
354
|
declare const PhrasingContent: Schema.Codec<PhrasingContent>;
|
|
355
355
|
/**
|
|
356
|
-
* The union of all phrasing-content node types.
|
|
356
|
+
* The union of all phrasing-content node types. Widened for MDX with
|
|
357
|
+
* {@link MdxJsxTextElement} and {@link MdxTextExpression}, per
|
|
358
|
+
* mdast-util-mdx's `PhrasingContentMap` registrations — the parser never
|
|
359
|
+
* produces either; they serve constructed trees.
|
|
357
360
|
*
|
|
358
361
|
* @public
|
|
359
362
|
*/
|
|
360
|
-
type PhrasingContent = Break | Delete | Emphasis | FootnoteReference | Html | Image | ImageReference | InlineCode | Link | LinkReference | Strong | Text;
|
|
363
|
+
type PhrasingContent = Break | Delete | Emphasis | FootnoteReference | Html | Image | ImageReference | InlineCode | Link | LinkReference | MdxJsxTextElement | MdxTextExpression | Strong | Text;
|
|
361
364
|
declare const ThematicBreak_base: Schema.Class<ThematicBreak, Schema.Struct<{
|
|
362
365
|
readonly type: Schema.tag<"thematicBreak">;
|
|
363
366
|
readonly position: Schema.withConstructorDefault<typeof Position>;
|
|
@@ -618,11 +621,14 @@ declare const FlowContent: Schema.Codec<FlowContent>;
|
|
|
618
621
|
/**
|
|
619
622
|
* The union of all flow-content node types. Includes mdast's `Content`
|
|
620
623
|
* category (`Definition | Paragraph`) inline, as the spec's `FlowContent`
|
|
621
|
-
* definition does,
|
|
624
|
+
* definition does, the GFM extras `FootnoteDefinition` and `Table`, and the
|
|
625
|
+
* MDX extras {@link MdxJsxFlowElement} and {@link MdxFlowExpression} per
|
|
626
|
+
* mdast-util-mdx's `BlockContentMap` registrations — the parser never
|
|
627
|
+
* produces the MDX members; they serve constructed trees.
|
|
622
628
|
*
|
|
623
629
|
* @public
|
|
624
630
|
*/
|
|
625
|
-
type FlowContent = Blockquote | Code | Definition | FootnoteDefinition | Heading | Html | List | Paragraph | Table | ThematicBreak;
|
|
631
|
+
type FlowContent = Blockquote | Code | Definition | FootnoteDefinition | Heading | Html | List | MdxFlowExpression | MdxJsxFlowElement | Paragraph | Table | ThematicBreak;
|
|
626
632
|
/**
|
|
627
633
|
* The union of every node that may appear where mdast expects **list**
|
|
628
634
|
* content. A one-member union, kept because mdast names the category and
|
|
@@ -638,6 +644,155 @@ declare const ListContent: Schema.Codec<ListContent>;
|
|
|
638
644
|
* @public
|
|
639
645
|
*/
|
|
640
646
|
type ListContent = ListItem;
|
|
647
|
+
declare const MdxJsxAttributeValueExpression_base: Schema.Class<MdxJsxAttributeValueExpression, Schema.Struct<{
|
|
648
|
+
readonly type: Schema.tag<"mdxJsxAttributeValueExpression">;
|
|
649
|
+
readonly value: Schema.String;
|
|
650
|
+
readonly position: Schema.withConstructorDefault<typeof Position>;
|
|
651
|
+
}>, {}>;
|
|
652
|
+
/**
|
|
653
|
+
* MdxJsxAttributeValueExpression — a JSX attribute value written as an
|
|
654
|
+
* expression (`<a b={c} />`); `value` holds the expression source text
|
|
655
|
+
* between the braces, never evaluated or parsed.
|
|
656
|
+
*
|
|
657
|
+
* The primary construction path is a JSON-encoded prop:
|
|
658
|
+
* `MdxJsxAttributeValueExpression.make({ value: JSON.stringify(props) })`.
|
|
659
|
+
*
|
|
660
|
+
* @public
|
|
661
|
+
*/
|
|
662
|
+
declare class MdxJsxAttributeValueExpression extends MdxJsxAttributeValueExpression_base {}
|
|
663
|
+
declare const MdxJsxAttribute_base: Schema.Class<MdxJsxAttribute, Schema.Struct<{
|
|
664
|
+
readonly type: Schema.tag<"mdxJsxAttribute">;
|
|
665
|
+
readonly name: Schema.String;
|
|
666
|
+
readonly value: Schema.optionalKey<Schema.NullOr<Schema.Union<readonly [typeof MdxJsxAttributeValueExpression, Schema.String]>>>;
|
|
667
|
+
readonly position: Schema.withConstructorDefault<typeof Position>;
|
|
668
|
+
}>, {}>;
|
|
669
|
+
/**
|
|
670
|
+
* MdxJsxAttribute — a named JSX attribute (`<a b="c" />`). `value` is a
|
|
671
|
+
* string literal, a {@link MdxJsxAttributeValueExpression}, or — for a
|
|
672
|
+
* boolean attribute (`<a b />`) — absent or `null`, both of which the
|
|
673
|
+
* mdast-util-mdx-jsx contract spells (its parser writes `null`; absence is
|
|
674
|
+
* the constructed-tree spelling). The serializer treats the two identically.
|
|
675
|
+
*
|
|
676
|
+
* `name` must be non-empty — an attribute without a name has no MDX spelling,
|
|
677
|
+
* so the schema refuses it at construction and decode (the oracle's
|
|
678
|
+
* serialize-time crash, moved to the admission boundary).
|
|
679
|
+
*
|
|
680
|
+
* @public
|
|
681
|
+
*/
|
|
682
|
+
declare class MdxJsxAttribute extends MdxJsxAttribute_base {}
|
|
683
|
+
declare const MdxJsxExpressionAttribute_base: Schema.Class<MdxJsxExpressionAttribute, Schema.Struct<{
|
|
684
|
+
readonly type: Schema.tag<"mdxJsxExpressionAttribute">;
|
|
685
|
+
readonly value: Schema.String;
|
|
686
|
+
readonly position: Schema.withConstructorDefault<typeof Position>;
|
|
687
|
+
}>, {}>;
|
|
688
|
+
/**
|
|
689
|
+
* MdxJsxExpressionAttribute — a JSX attribute written whole as an expression
|
|
690
|
+
* (`<a {...b} />`); `value` holds the expression source text between the
|
|
691
|
+
* braces.
|
|
692
|
+
*
|
|
693
|
+
* @public
|
|
694
|
+
*/
|
|
695
|
+
declare class MdxJsxExpressionAttribute extends MdxJsxExpressionAttribute_base {}
|
|
696
|
+
/**
|
|
697
|
+
* The union of every node that may appear in a JSX element's `attributes`
|
|
698
|
+
* array. A real `Schema.Union` for the construction pass-through documented
|
|
699
|
+
* on `RowContent`.
|
|
700
|
+
*
|
|
701
|
+
* Attribute carriers are node-shaped values — they carry `type` and
|
|
702
|
+
* `position` per the mdast-util-mdx-jsx contract — but they are **not tree
|
|
703
|
+
* content**: they never appear in a `children` array, so they are excluded
|
|
704
|
+
* from `MarkdownNode` and invisible to the visitor and to
|
|
705
|
+
* `MarkdownDocument.find`.
|
|
706
|
+
*
|
|
707
|
+
* @public
|
|
708
|
+
*/
|
|
709
|
+
declare const MdxJsxAttributeContent: Schema.Codec<MdxJsxAttributeContent>;
|
|
710
|
+
/**
|
|
711
|
+
* The union of all JSX attribute node types.
|
|
712
|
+
*
|
|
713
|
+
* @public
|
|
714
|
+
*/
|
|
715
|
+
type MdxJsxAttributeContent = MdxJsxAttribute | MdxJsxExpressionAttribute;
|
|
716
|
+
declare const MdxJsxFlowElement_base: Schema.Class<MdxJsxFlowElement, Schema.Struct<{
|
|
717
|
+
readonly type: Schema.tag<"mdxJsxFlowElement">;
|
|
718
|
+
readonly name: Schema.NullOr<Schema.String>;
|
|
719
|
+
readonly attributes: Schema.$Array<Schema.Codec<MdxJsxAttributeContent, MdxJsxAttributeContent, never, never>>;
|
|
720
|
+
readonly children: Schema.$Array<Schema.suspend<Schema.Codec<FlowContent, FlowContent, never, never>>>;
|
|
721
|
+
readonly position: Schema.withConstructorDefault<typeof Position>;
|
|
722
|
+
}>, {}>;
|
|
723
|
+
/**
|
|
724
|
+
* MdxJsxFlowElement — a JSX element in flow (block) position (`<Component />`
|
|
725
|
+
* on its own lines). `name` is `null` for a fragment (`<></>`); children are
|
|
726
|
+
* flow content, per the oracle's `BlockContent | DefinitionContent` model.
|
|
727
|
+
*
|
|
728
|
+
* A fragment cannot carry attributes — that shape has no MDX spelling — so
|
|
729
|
+
* the schema refuses it at construction and decode. A **named** element's
|
|
730
|
+
* name must be non-empty on the same terms: `""` has no MDX spelling either
|
|
731
|
+
* (the oracle's parser only ever produces a real name or `null`, and its
|
|
732
|
+
* serializer treats a falsy name as the fragment), so `null` is the one
|
|
733
|
+
* fragment spelling and the empty string fails typed.
|
|
734
|
+
*
|
|
735
|
+
* @public
|
|
736
|
+
*/
|
|
737
|
+
declare class MdxJsxFlowElement extends MdxJsxFlowElement_base {}
|
|
738
|
+
declare const MdxJsxTextElement_base: Schema.Class<MdxJsxTextElement, Schema.Struct<{
|
|
739
|
+
readonly type: Schema.tag<"mdxJsxTextElement">;
|
|
740
|
+
readonly name: Schema.NullOr<Schema.String>;
|
|
741
|
+
readonly attributes: Schema.$Array<Schema.Codec<MdxJsxAttributeContent, MdxJsxAttributeContent, never, never>>;
|
|
742
|
+
readonly children: Schema.$Array<Schema.suspend<Schema.Codec<PhrasingContent, PhrasingContent, never, never>>>;
|
|
743
|
+
readonly position: Schema.withConstructorDefault<typeof Position>;
|
|
744
|
+
}>, {}>;
|
|
745
|
+
/**
|
|
746
|
+
* MdxJsxTextElement — a JSX element in text (phrasing) position
|
|
747
|
+
* (`a <b>c</b> d`). `name` is `null` for a fragment; children are phrasing
|
|
748
|
+
* content. Refuses attributes on a fragment and an empty-string name, on the
|
|
749
|
+
* same terms as {@link MdxJsxFlowElement}.
|
|
750
|
+
*
|
|
751
|
+
* @public
|
|
752
|
+
*/
|
|
753
|
+
declare class MdxJsxTextElement extends MdxJsxTextElement_base {}
|
|
754
|
+
declare const MdxFlowExpression_base: Schema.Class<MdxFlowExpression, Schema.Struct<{
|
|
755
|
+
readonly type: Schema.tag<"mdxFlowExpression">;
|
|
756
|
+
readonly value: Schema.String;
|
|
757
|
+
readonly position: Schema.withConstructorDefault<typeof Position>;
|
|
758
|
+
}>, {}>;
|
|
759
|
+
/**
|
|
760
|
+
* MdxFlowExpression — an expression in flow (block) position (`{a + b}` on
|
|
761
|
+
* its own lines); `value` holds the expression source text between the
|
|
762
|
+
* braces.
|
|
763
|
+
*
|
|
764
|
+
* @public
|
|
765
|
+
*/
|
|
766
|
+
declare class MdxFlowExpression extends MdxFlowExpression_base {}
|
|
767
|
+
declare const MdxTextExpression_base: Schema.Class<MdxTextExpression, Schema.Struct<{
|
|
768
|
+
readonly type: Schema.tag<"mdxTextExpression">;
|
|
769
|
+
readonly value: Schema.String;
|
|
770
|
+
readonly position: Schema.withConstructorDefault<typeof Position>;
|
|
771
|
+
}>, {}>;
|
|
772
|
+
/**
|
|
773
|
+
* MdxTextExpression — an expression in text (phrasing) position
|
|
774
|
+
* (`a {b} c`); `value` holds the expression source text between the braces.
|
|
775
|
+
*
|
|
776
|
+
* @public
|
|
777
|
+
*/
|
|
778
|
+
declare class MdxTextExpression extends MdxTextExpression_base {}
|
|
779
|
+
declare const MdxjsEsm_base: Schema.Class<MdxjsEsm, Schema.Struct<{
|
|
780
|
+
readonly type: Schema.tag<"mdxjsEsm">;
|
|
781
|
+
readonly value: Schema.String;
|
|
782
|
+
readonly position: Schema.withConstructorDefault<typeof Position>;
|
|
783
|
+
}>, {}>;
|
|
784
|
+
/**
|
|
785
|
+
* MdxjsEsm — an MDX ESM block (`import`/`export` statements); `value` holds
|
|
786
|
+
* the statement source verbatim.
|
|
787
|
+
*
|
|
788
|
+
* Only ever a child of {@link Root}, per the mdast-util-mdxjs-esm content
|
|
789
|
+
* registration — ESM cannot nest inside a JSX element or any other
|
|
790
|
+
* container. As with the frontmatter head node, the constraint is structural
|
|
791
|
+
* (the `Root` children union admits it, no other union does), not validated.
|
|
792
|
+
*
|
|
793
|
+
* @public
|
|
794
|
+
*/
|
|
795
|
+
declare class MdxjsEsm extends MdxjsEsm_base {}
|
|
641
796
|
/**
|
|
642
797
|
* The frontmatter formats the capture recognizes, keyed by their opening
|
|
643
798
|
* fence: `---` is yaml, `+++` is toml and `---json` is json.
|
|
@@ -694,7 +849,7 @@ declare const FrontmatterContent: Schema.Codec<FrontmatterContent>;
|
|
|
694
849
|
type FrontmatterContent = Frontmatter;
|
|
695
850
|
declare const Root_base: Schema.Class<Root, Schema.Struct<{
|
|
696
851
|
readonly type: Schema.tag<"root">;
|
|
697
|
-
readonly children: Schema.$Array<Schema.suspend<Schema.Codec<Frontmatter | FlowContent, Frontmatter | FlowContent, never, never>>>;
|
|
852
|
+
readonly children: Schema.$Array<Schema.suspend<Schema.Codec<Frontmatter | MdxjsEsm | FlowContent, Frontmatter | MdxjsEsm | FlowContent, never, never>>>;
|
|
698
853
|
readonly position: Schema.withConstructorDefault<typeof Position>;
|
|
699
854
|
}>, {}>;
|
|
700
855
|
/**
|
|
@@ -703,7 +858,8 @@ declare const Root_base: Schema.Class<Root, Schema.Struct<{
|
|
|
703
858
|
* mdast leaves a root's content model open; a parsed markdown document is
|
|
704
859
|
* flow content, optionally headed by one {@link Frontmatter} node — mdast's
|
|
705
860
|
* `FlowContentFrontmatter` merge, which admits frontmatter at the root and
|
|
706
|
-
* nowhere else.
|
|
861
|
+
* nowhere else. {@link MdxjsEsm} is likewise admitted at the root and nowhere
|
|
862
|
+
* else, per mdast-util-mdxjs-esm's `RootContentMap` registration.
|
|
707
863
|
*
|
|
708
864
|
* @public
|
|
709
865
|
*/
|
|
@@ -714,7 +870,7 @@ declare class Root extends Root_base {}
|
|
|
714
870
|
*
|
|
715
871
|
* @public
|
|
716
872
|
*/
|
|
717
|
-
type MarkdownNode = Root | FrontmatterContent | FlowContent | ListContent | PhrasingContent | RowContent | TableContent;
|
|
873
|
+
type MarkdownNode = Root | FrontmatterContent | FlowContent | ListContent | MdxjsEsm | PhrasingContent | RowContent | TableContent;
|
|
718
874
|
/**
|
|
719
875
|
* A schema matching any node in the tree.
|
|
720
876
|
*
|
|
@@ -929,6 +1085,67 @@ declare class Markdown {
|
|
|
929
1085
|
* fails with {@link MarkdownParseError}.
|
|
930
1086
|
*/
|
|
931
1087
|
static readonly parse: (text: string, options?: MarkdownParseOptions | undefined) => Effect.Effect<Root, MarkdownParseError, never>;
|
|
1088
|
+
/**
|
|
1089
|
+
* Parse a text fragment as a single paragraph's inline content,
|
|
1090
|
+
* synchronously, as a `Result` — the phrasing-level twin of
|
|
1091
|
+
* {@link Markdown.parseResult}, for callers holding already-markdown
|
|
1092
|
+
* prose (a link-carrying sentence, a backtick span) who want its
|
|
1093
|
+
* phrasing nodes without a full document parse and a paragraph splice.
|
|
1094
|
+
*
|
|
1095
|
+
* @remarks
|
|
1096
|
+
* The whole input is prepared exactly the way the block pass prepares one
|
|
1097
|
+
* paragraph's content — leading and trailing whitespace trimmed, line
|
|
1098
|
+
* terminators normalized to `\n` — and handed to the inline pass, so the
|
|
1099
|
+
* result matches the `children` of the paragraph a full parse of the same
|
|
1100
|
+
* fragment would produce. Node positions are correct relative to the
|
|
1101
|
+
* input string.
|
|
1102
|
+
*
|
|
1103
|
+
* Two consequences of the single-paragraph contract:
|
|
1104
|
+
*
|
|
1105
|
+
* - **Blank lines do not break blocks.** A `\n\n` in the input stays
|
|
1106
|
+
* inline content (literal newlines in a text node); nothing at this
|
|
1107
|
+
* level opens a heading, list or code block — `# foo` is the text
|
|
1108
|
+
* `# foo`.
|
|
1109
|
+
* - **No reference context exists.** The fragment carries no definitions,
|
|
1110
|
+
* so `[foo]`, `![foo]` and `[^foo]` remain literal text — the same
|
|
1111
|
+
* result a full parse of the isolated fragment produces.
|
|
1112
|
+
*
|
|
1113
|
+
* `options.dialect` selects the inline dialect (default `"gfm"`);
|
|
1114
|
+
* `options.frontmatter` has no effect at phrasing level. Failure is rare
|
|
1115
|
+
* by design, on {@link Markdown.parseResult}'s terms: only a
|
|
1116
|
+
* hardening-guard trip (delimiter or bracket nesting past the cap) fails.
|
|
1117
|
+
*
|
|
1118
|
+
* @example
|
|
1119
|
+
* ```ts
|
|
1120
|
+
* import { Markdown } from "@effected/markdown";
|
|
1121
|
+
* import { Result } from "effect";
|
|
1122
|
+
*
|
|
1123
|
+
* const ok = Markdown.parsePhrasingResult("see [the docs](./docs.md)");
|
|
1124
|
+
* if (Result.isSuccess(ok)) {
|
|
1125
|
+
* console.log(ok.success.map((node) => node.type)); // => ["text", "link"]
|
|
1126
|
+
* }
|
|
1127
|
+
* ```
|
|
1128
|
+
*
|
|
1129
|
+
* @param text - The prose fragment to parse.
|
|
1130
|
+
* @param options - Optional {@link MarkdownParseOptions}; the dialect
|
|
1131
|
+
* defaults to `"gfm"`.
|
|
1132
|
+
* @returns A `Result` succeeding with the fragment's phrasing content, or
|
|
1133
|
+
* failing with {@link MarkdownParseError}.
|
|
1134
|
+
*/
|
|
1135
|
+
static parsePhrasingResult(text: string, options?: MarkdownParseOptions): Result.Result<ReadonlyArray<PhrasingContent>, MarkdownParseError>;
|
|
1136
|
+
/**
|
|
1137
|
+
* Parse a text fragment as a single paragraph's inline content. Defined
|
|
1138
|
+
* in terms of {@link Markdown.parsePhrasingResult} — synchronous callers
|
|
1139
|
+
* can use that variant directly; the single-paragraph contract (blank
|
|
1140
|
+
* lines stay inline, references never form) is documented there.
|
|
1141
|
+
*
|
|
1142
|
+
* @param text - The prose fragment to parse.
|
|
1143
|
+
* @param options - Optional {@link MarkdownParseOptions}; the dialect
|
|
1144
|
+
* defaults to `"gfm"`.
|
|
1145
|
+
* @returns An `Effect` that succeeds with the fragment's phrasing
|
|
1146
|
+
* content, or fails with {@link MarkdownParseError}.
|
|
1147
|
+
*/
|
|
1148
|
+
static readonly parsePhrasing: (text: string, options?: MarkdownParseOptions | undefined) => Effect.Effect<readonly PhrasingContent[], MarkdownParseError, never>;
|
|
932
1149
|
/**
|
|
933
1150
|
* Serialize a {@link Root} tree to canonical markdown, synchronously, as a
|
|
934
1151
|
* `Result`. The pure primitive twin of {@link Markdown.stringify}, on the
|
|
@@ -989,6 +1206,18 @@ declare class Markdown {
|
|
|
989
1206
|
* post-decode walk that reaches nested nodes. The choice then leaves the
|
|
990
1207
|
* emitter entirely and no output depends on a node's neighbours.
|
|
991
1208
|
*
|
|
1209
|
+
* **MDX nodes (constructed trees only — the parser reads no MDX syntax)**
|
|
1210
|
+
* serialize to valid MDX with the mdast-util-mdx defaults, and these
|
|
1211
|
+
* choices are part of the same stability commitment: attribute values
|
|
1212
|
+
* quote with `"` (the quote escaped as `"`), an empty element
|
|
1213
|
+
* self-closes spaced (`<a />`), a fragment is `<></>`, flow children take
|
|
1214
|
+
* block layout indented two spaces per JSX ancestor, attributes move onto
|
|
1215
|
+
* their own lines only when one carries a line ending, expressions emit
|
|
1216
|
+
* `{expr}` with two-space continuation indent, and ESM values emit
|
|
1217
|
+
* verbatim. A tree containing any MDX node additionally escapes `{` in
|
|
1218
|
+
* text — MDX makes it significant — while a tree with none serializes
|
|
1219
|
+
* byte-identically to the table above.
|
|
1220
|
+
*
|
|
992
1221
|
* To *normalize* a document to different choices — a `-` list rewritten to
|
|
993
1222
|
* `*`, setext headings rewritten to ATX — use `MarkdownFormat` with
|
|
994
1223
|
* `MarkdownFormattingOptions`. That is the configurable surface; this one
|
|
@@ -1963,6 +2192,144 @@ declare class SchemaResolver {
|
|
|
1963
2192
|
static fromRegistry(registrations: Readonly<Record<string, Schema.Top>>): FrontmatterSchemaResolver;
|
|
1964
2193
|
}
|
|
1965
2194
|
//#endregion
|
|
2195
|
+
//#region src/FrontmatterSource.d.ts
|
|
2196
|
+
/**
|
|
2197
|
+
* The three line-terminator spellings a fence line may end with. Recorded by
|
|
2198
|
+
* {@link FrontmatterSource.split} as fidelity — what the source's fence
|
|
2199
|
+
* lines actually used — and consumed by {@link FrontmatterSource.join}.
|
|
2200
|
+
*
|
|
2201
|
+
* @public
|
|
2202
|
+
*/
|
|
2203
|
+
declare const FrontmatterNewline: Schema.Literals<readonly ["\n", "\r\n", "\r"]>;
|
|
2204
|
+
/**
|
|
2205
|
+
* The union of all fence line-terminator literals.
|
|
2206
|
+
*
|
|
2207
|
+
* @public
|
|
2208
|
+
*/
|
|
2209
|
+
type FrontmatterNewline = typeof FrontmatterNewline.Type;
|
|
2210
|
+
declare const FrontmatterSourceBlock_base: Schema.Class<FrontmatterSourceBlock, Schema.Struct<{
|
|
2211
|
+
readonly format: Schema.Literals<readonly ["yaml", "toml", "json"]>;
|
|
2212
|
+
readonly value: Schema.String;
|
|
2213
|
+
readonly newline: Schema.optionalKey<Schema.Literals<readonly ["\n", "\r\n", "\r"]>>;
|
|
2214
|
+
}>, {}>;
|
|
2215
|
+
/**
|
|
2216
|
+
* A frontmatter block at string level: the format its fence declared, the
|
|
2217
|
+
* exact bytes between the fence lines, and the fence lines' terminator.
|
|
2218
|
+
*
|
|
2219
|
+
* @remarks
|
|
2220
|
+
* `value` is the exact source slice strictly between the opening fence line
|
|
2221
|
+
* (its terminator included) and the closing fence line: every value line
|
|
2222
|
+
* WITH its own terminator, verbatim — so a block whose value is one blank
|
|
2223
|
+
* line (`"\n"`) stays distinct from a block with no value lines (`""`), and
|
|
2224
|
+
* interior CRLF terminators survive untouched. This deliberately differs
|
|
2225
|
+
* from the parsed `Frontmatter` NODE's `value`, which drops the final
|
|
2226
|
+
* terminator; the node feeds codecs, this feeds byte-exact reconstruction.
|
|
2227
|
+
* {@link FrontmatterSource.join} accepts a value without a trailing
|
|
2228
|
+
* terminator too, appending `newline` so a hand-built block cannot fuse its
|
|
2229
|
+
* last line into the closing fence.
|
|
2230
|
+
*
|
|
2231
|
+
* `newline` is a fidelity field: `split` always records the terminator
|
|
2232
|
+
* following the opening fence; when absent (a hand-built block), `join`
|
|
2233
|
+
* uses `"\n"`.
|
|
2234
|
+
*
|
|
2235
|
+
* @public
|
|
2236
|
+
*/
|
|
2237
|
+
declare class FrontmatterSourceBlock extends FrontmatterSourceBlock_base {}
|
|
2238
|
+
declare const FrontmatterSourceSplit_base: Schema.Class<FrontmatterSourceSplit, Schema.Struct<{
|
|
2239
|
+
readonly frontmatter: Schema.optionalKey<typeof FrontmatterSourceBlock>;
|
|
2240
|
+
readonly body: Schema.String;
|
|
2241
|
+
readonly bodyOffset: Schema.withConstructorDefault<Schema.Number>;
|
|
2242
|
+
}>, {}>;
|
|
2243
|
+
/**
|
|
2244
|
+
* The result of {@link FrontmatterSource.split}: the frontmatter block when
|
|
2245
|
+
* one exists, the exact body remainder, and the body's byte offset.
|
|
2246
|
+
*
|
|
2247
|
+
* @remarks
|
|
2248
|
+
* `body` is exactly `source.slice(bodyOffset)` — the string and the offset
|
|
2249
|
+
* can never disagree. With no block, `body` is the whole source and
|
|
2250
|
+
* `bodyOffset` is `0`. With a block, `bodyOffset` sits immediately after
|
|
2251
|
+
* the closing fence line and its line terminator (the terminator belongs to
|
|
2252
|
+
* the fence line, as every line's terminator belongs to its line), which is
|
|
2253
|
+
* gray-matter's body boundary; when the closing fence ends the document
|
|
2254
|
+
* there is no terminator and the body is empty.
|
|
2255
|
+
*
|
|
2256
|
+
* `bodyOffset` is informational output of `split`; `join` never reads it,
|
|
2257
|
+
* and it constructor-defaults to `0` so a hand-built value for `join` can
|
|
2258
|
+
* omit it.
|
|
2259
|
+
*
|
|
2260
|
+
* @public
|
|
2261
|
+
*/
|
|
2262
|
+
declare class FrontmatterSourceSplit extends FrontmatterSourceSplit_base {}
|
|
2263
|
+
/**
|
|
2264
|
+
* The string-level frontmatter facade: split raw source at the fence
|
|
2265
|
+
* boundaries and join it back, without parsing anything.
|
|
2266
|
+
*
|
|
2267
|
+
* @public
|
|
2268
|
+
*/
|
|
2269
|
+
declare class FrontmatterSource {
|
|
2270
|
+
private constructor();
|
|
2271
|
+
/**
|
|
2272
|
+
* Split raw source into its frontmatter block and body, at string level.
|
|
2273
|
+
* Total: never fails, never parses — absence of a block (including an
|
|
2274
|
+
* opening fence with no closing fence, which per the grammar is not
|
|
2275
|
+
* frontmatter) is a representable result, not an error.
|
|
2276
|
+
*
|
|
2277
|
+
* @remarks
|
|
2278
|
+
* The grammar is the parser's own offset-0 pre-scan, over raw bytes:
|
|
2279
|
+
* the fence set is closed (`---` yaml, `+++` toml, `---json` json), a
|
|
2280
|
+
* fence line is exactly the fence with no surrounding whitespace, and a
|
|
2281
|
+
* leading BOM means offset 0 is not a fence — strip one before calling
|
|
2282
|
+
* if the source may carry it. Unlike the parse path there is no capture
|
|
2283
|
+
* toggle here, so absence is one fact: the source has no closed block.
|
|
2284
|
+
* (`MarkdownDocument`'s two-reason absence exists because parsing can be
|
|
2285
|
+
* asked not to look; this surface always looks.)
|
|
2286
|
+
*
|
|
2287
|
+
* Byte exactness: `source.slice(0, result.bodyOffset) + result.body`
|
|
2288
|
+
* reassembles `source` exactly, and {@link FrontmatterSource.join}
|
|
2289
|
+
* reproduces the original bytes for an unmodified round-trip — see
|
|
2290
|
+
* `join` for the two normalizations at the contract's edge.
|
|
2291
|
+
*
|
|
2292
|
+
* @example
|
|
2293
|
+
* ```ts
|
|
2294
|
+
* import { FrontmatterSource } from "@effected/markdown";
|
|
2295
|
+
*
|
|
2296
|
+
* const split = FrontmatterSource.split("---\ntitle: hi\n---\n\n# Body\n");
|
|
2297
|
+
* console.log(split.frontmatter?.format); // => "yaml"
|
|
2298
|
+
* console.log(split.frontmatter?.value); // => "title: hi\n"
|
|
2299
|
+
* console.log(split.body); // => "\n# Body\n"
|
|
2300
|
+
* ```
|
|
2301
|
+
*
|
|
2302
|
+
* @param source - The raw document source, body format irrelevant.
|
|
2303
|
+
* @returns A {@link FrontmatterSourceSplit}; `frontmatter` is absent when
|
|
2304
|
+
* the source opens with no closed fence block.
|
|
2305
|
+
*/
|
|
2306
|
+
static split(source: string): FrontmatterSourceSplit;
|
|
2307
|
+
/**
|
|
2308
|
+
* Serialize a split back to one source string. Total and pure.
|
|
2309
|
+
*
|
|
2310
|
+
* @remarks
|
|
2311
|
+
* With no block the body is returned verbatim. With a block the output
|
|
2312
|
+
* is the opening fence, `newline` (default `"\n"`), the value — a
|
|
2313
|
+
* non-empty value missing a final line terminator gets `newline`
|
|
2314
|
+
* appended, so the closing fence always starts its own line — the
|
|
2315
|
+
* closing fence, `newline`, then the body verbatim.
|
|
2316
|
+
*
|
|
2317
|
+
* **Round-trip contract:** `join(split(source)) === source`, byte for
|
|
2318
|
+
* byte, for every source whose closing fence line ends with a line
|
|
2319
|
+
* terminator matching the opening fence's. The two edges where join
|
|
2320
|
+
* normalizes instead: a closing fence at end-of-document gains a final
|
|
2321
|
+
* `newline` (the block's one unterminated spelling), and fence lines
|
|
2322
|
+
* with MISMATCHED terminators (say `---\r\n … ---\n`) re-emit both with
|
|
2323
|
+
* the opening one. Value and body bytes survive verbatim in every case.
|
|
2324
|
+
*
|
|
2325
|
+
* @param split - The parts to serialize, typically from
|
|
2326
|
+
* {@link FrontmatterSource.split} or built with
|
|
2327
|
+
* `FrontmatterSourceSplit.make`.
|
|
2328
|
+
* @returns The combined source string.
|
|
2329
|
+
*/
|
|
2330
|
+
static join(split: FrontmatterSourceSplit): string;
|
|
2331
|
+
}
|
|
2332
|
+
//#endregion
|
|
1966
2333
|
//#region src/JsonFrontmatter.d.ts
|
|
1967
2334
|
/**
|
|
1968
2335
|
* The json frontmatter codec, over `@effected/jsonc`.
|
|
@@ -2480,5 +2847,5 @@ declare const TomlFrontmatter: FrontmatterCodec;
|
|
|
2480
2847
|
*/
|
|
2481
2848
|
declare const YamlFrontmatter: FrontmatterCodec;
|
|
2482
2849
|
//#endregion
|
|
2483
|
-
export { Blockquote, Break, BreakStyle, BulletChar, Code, CodeBlockStyle, Definition, Delete, type DocumentHeading, type DocumentLink, DocumentSection, Emphasis, EmphasisChar, FenceChar, FlowContent, FootnoteDefinition, FootnoteReference, Frontmatter, type FrontmatterCodec, FrontmatterContent, FrontmatterDecodeError, FrontmatterEncodeError, FrontmatterFormat, FrontmatterFormatMismatchError, FrontmatterMissingError, FrontmatterMissingReason, type FrontmatterResolveError, type FrontmatterSchemaError, type FrontmatterSchemaResolver, FrontmatterValidationError, type FrontmatterWriteError, Heading, HeadingDepth, HeadingStyle, Html, Image, ImageReference, InlineCode, JsonFrontmatter, Link, type LinkBearingNode, LinkReference, List, ListContent, ListDelimiter, ListItem, Markdown, MarkdownDiagnostic, MarkdownDialect, MarkdownDocument, MarkdownEdit, MarkdownFormat, MarkdownFormattingOptions, MarkdownFrontmatter, MarkdownModificationError, MarkdownModificationErrorCode, MarkdownNode, type MarkdownNodeOfType, type MarkdownNodeType, MarkdownParseError, MarkdownParseErrorCode, MarkdownParseOptions, type MarkdownPath, MarkdownRange, type MarkdownRangeLike, type MarkdownSegment, MarkdownStringifyError, MarkdownVisitor, MarkdownVisitorEvent, Mdast, MdastDecodeError, type MdastNode, Paragraph, PhrasingContent, Point, Position, ReferenceType, Root, RowContent, SchemaDeclaration, SchemaDeclarationByName, SchemaDeclarationByPath, SchemaDeclarationByUrl, SchemaDeclarationInline, SchemaDeclarationInvalidError, SchemaDeclarationMissingError, SchemaNameUnknownError, SchemaResolver, SchemaVersionUnresolvableError, type SectionHeadingMatch, type SectionQueryOptions, Strong, Table, TableAlign, TableCell, TableContent, TableRow, Text, ThematicBreak, ThematicBreakChar, TomlFrontmatter, YamlFrontmatter };
|
|
2850
|
+
export { Blockquote, Break, BreakStyle, BulletChar, Code, CodeBlockStyle, Definition, Delete, type DocumentHeading, type DocumentLink, DocumentSection, Emphasis, EmphasisChar, FenceChar, FlowContent, FootnoteDefinition, FootnoteReference, Frontmatter, type FrontmatterCodec, FrontmatterContent, FrontmatterDecodeError, FrontmatterEncodeError, FrontmatterFormat, FrontmatterFormatMismatchError, FrontmatterMissingError, FrontmatterMissingReason, FrontmatterNewline, type FrontmatterResolveError, type FrontmatterSchemaError, type FrontmatterSchemaResolver, FrontmatterSource, FrontmatterSourceBlock, FrontmatterSourceSplit, FrontmatterValidationError, type FrontmatterWriteError, Heading, HeadingDepth, HeadingStyle, Html, Image, ImageReference, InlineCode, JsonFrontmatter, Link, type LinkBearingNode, LinkReference, List, ListContent, ListDelimiter, ListItem, Markdown, MarkdownDiagnostic, MarkdownDialect, MarkdownDocument, MarkdownEdit, MarkdownFormat, MarkdownFormattingOptions, MarkdownFrontmatter, MarkdownModificationError, MarkdownModificationErrorCode, MarkdownNode, type MarkdownNodeOfType, type MarkdownNodeType, MarkdownParseError, MarkdownParseErrorCode, MarkdownParseOptions, type MarkdownPath, MarkdownRange, type MarkdownRangeLike, type MarkdownSegment, MarkdownStringifyError, MarkdownVisitor, MarkdownVisitorEvent, Mdast, MdastDecodeError, type MdastNode, 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, type SectionHeadingMatch, type SectionQueryOptions, Strong, Table, TableAlign, TableCell, TableContent, TableRow, Text, ThematicBreak, ThematicBreakChar, TomlFrontmatter, YamlFrontmatter };
|
|
2484
2851
|
//# sourceMappingURL=index.d.ts.map
|
package/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { MarkdownEdit, MarkdownRange } from "./MarkdownEdit.js";
|
|
2
|
-
import { Blockquote, Break, BreakStyle, BulletChar, Code, Definition, Delete, Emphasis, EmphasisChar, FenceChar, FlowContent, FootnoteDefinition, FootnoteReference, Frontmatter, FrontmatterContent, FrontmatterFormat, Heading, HeadingDepth, HeadingStyle, Html, Image, ImageReference, InlineCode, Link, LinkReference, List, ListContent, ListDelimiter, ListItem, MarkdownNode, Paragraph, PhrasingContent, Point, Position, ReferenceType, Root, RowContent, Strong, Table, TableAlign, TableCell, TableContent, TableRow, Text, ThematicBreak, ThematicBreakChar } from "./MarkdownNode.js";
|
|
2
|
+
import { Blockquote, Break, BreakStyle, BulletChar, Code, Definition, Delete, Emphasis, EmphasisChar, FenceChar, FlowContent, FootnoteDefinition, FootnoteReference, Frontmatter, FrontmatterContent, FrontmatterFormat, Heading, HeadingDepth, HeadingStyle, Html, Image, ImageReference, InlineCode, Link, LinkReference, List, ListContent, ListDelimiter, ListItem, MarkdownNode, MdxFlowExpression, MdxJsxAttribute, MdxJsxAttributeContent, MdxJsxAttributeValueExpression, MdxJsxExpressionAttribute, MdxJsxFlowElement, MdxJsxTextElement, MdxTextExpression, MdxjsEsm, Paragraph, PhrasingContent, Point, Position, ReferenceType, Root, RowContent, Strong, Table, TableAlign, TableCell, TableContent, TableRow, Text, ThematicBreak, ThematicBreakChar } from "./MarkdownNode.js";
|
|
3
3
|
import { FrontmatterDecodeError, FrontmatterEncodeError, FrontmatterFormatMismatchError, FrontmatterMissingError, FrontmatterMissingReason, FrontmatterValidationError, MarkdownFrontmatter } from "./Frontmatter.js";
|
|
4
4
|
import { SchemaDeclaration, SchemaDeclarationByName, SchemaDeclarationByPath, SchemaDeclarationByUrl, SchemaDeclarationInline, SchemaDeclarationInvalidError, SchemaDeclarationMissingError, SchemaNameUnknownError, SchemaResolver, SchemaVersionUnresolvableError } from "./FrontmatterResolver.js";
|
|
5
|
+
import { FrontmatterNewline, FrontmatterSource, FrontmatterSourceBlock, FrontmatterSourceSplit } from "./FrontmatterSource.js";
|
|
5
6
|
import { JsonFrontmatter } from "./JsonFrontmatter.js";
|
|
6
7
|
import { MarkdownDiagnostic, MarkdownParseErrorCode } from "./MarkdownDiagnostic.js";
|
|
7
8
|
import { Markdown, MarkdownDialect, MarkdownParseError, MarkdownParseOptions, MarkdownStringifyError } from "./Markdown.js";
|
|
@@ -12,4 +13,4 @@ import { Mdast, MdastDecodeError } from "./Mdast.js";
|
|
|
12
13
|
import { TomlFrontmatter } from "./TomlFrontmatter.js";
|
|
13
14
|
import { YamlFrontmatter } from "./YamlFrontmatter.js";
|
|
14
15
|
|
|
15
|
-
export { Blockquote, Break, BreakStyle, BulletChar, Code, CodeBlockStyle, Definition, Delete, DocumentSection, Emphasis, EmphasisChar, FenceChar, FlowContent, FootnoteDefinition, FootnoteReference, Frontmatter, FrontmatterContent, FrontmatterDecodeError, FrontmatterEncodeError, FrontmatterFormat, FrontmatterFormatMismatchError, FrontmatterMissingError, FrontmatterMissingReason, 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, 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 };
|
|
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 };
|
package/internal/blockParser.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Frontmatter, Point, Position, Root } from "../MarkdownNode.js";
|
|
2
|
+
import { scanFrontmatter } from "./blocks/frontmatter.js";
|
|
2
3
|
import { makeBlockNode } from "./blockTypes.js";
|
|
3
4
|
import { columnsToNextTabStop, preprocessLines } from "./preprocess.js";
|
|
4
5
|
import { isHtmlBlockEnd } from "./blocks/htmlBlock.js";
|
|
5
6
|
import { blockDialect } from "./blockRegistry.js";
|
|
6
|
-
import { scanFrontmatter } from "./blocks/frontmatter.js";
|
|
7
7
|
import { GuardExceeded } from "./carriers.js";
|
|
8
8
|
import { LineIndex } from "./lineIndex.js";
|
|
9
9
|
import { prepareInline } from "./rawInline.js";
|
|
@@ -51,6 +51,52 @@ const scanFrontmatter = (lines, text) => {
|
|
|
51
51
|
}
|
|
52
52
|
return null;
|
|
53
53
|
};
|
|
54
|
+
/** The terminator starting at `offset`, or null when none does. */
|
|
55
|
+
const terminatorAt = (source, offset) => {
|
|
56
|
+
const code = source.charCodeAt(offset);
|
|
57
|
+
if (code === 10) return "\n";
|
|
58
|
+
if (code === 13) return source.charCodeAt(offset + 1) === 10 ? "\r\n" : "\r";
|
|
59
|
+
return null;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Scan raw source for a frontmatter block — the same closed fence grammar as
|
|
63
|
+
* {@link scanFrontmatter}, over unpreprocessed bytes, returning exact
|
|
64
|
+
* offsets. The two scans agree on every input about WHETHER a block exists
|
|
65
|
+
* (the preprocessor's substitutions are length-preserving and can only break
|
|
66
|
+
* a fence match both scans require); this one exists for the string-level
|
|
67
|
+
* split/join surface, whose contract is byte exactness.
|
|
68
|
+
*
|
|
69
|
+
* Returns `null` when the document has no frontmatter — no fence at offset
|
|
70
|
+
* 0, or an opening fence with no closing fence, which per the grammar is not
|
|
71
|
+
* frontmatter at all.
|
|
72
|
+
*/
|
|
73
|
+
const scanRawFrontmatter = (source) => {
|
|
74
|
+
let index = 0;
|
|
75
|
+
while (index < source.length && terminatorAt(source, index) === null) index += 1;
|
|
76
|
+
const openNewline = index < source.length ? terminatorAt(source, index) : null;
|
|
77
|
+
const rule = FENCES.get(source.slice(0, index));
|
|
78
|
+
if (rule === void 0 || openNewline === null) return null;
|
|
79
|
+
const valueStart = index + openNewline.length;
|
|
80
|
+
let lineStart = valueStart;
|
|
81
|
+
index = valueStart;
|
|
82
|
+
while (index <= source.length) {
|
|
83
|
+
const terminator = index === source.length ? null : terminatorAt(source, index);
|
|
84
|
+
if (terminator === null && index < source.length) {
|
|
85
|
+
index += 1;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (source.slice(lineStart, index) === rule.close) return {
|
|
89
|
+
format: rule.format,
|
|
90
|
+
value: source.slice(valueStart, lineStart),
|
|
91
|
+
newline: openNewline,
|
|
92
|
+
bodyOffset: terminator === null ? index : index + terminator.length
|
|
93
|
+
};
|
|
94
|
+
if (terminator === null) break;
|
|
95
|
+
index += terminator.length;
|
|
96
|
+
lineStart = index;
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
99
|
+
};
|
|
54
100
|
|
|
55
101
|
//#endregion
|
|
56
|
-
export { scanFrontmatter };
|
|
102
|
+
export { scanFrontmatter, scanRawFrontmatter };
|