@remigius42/morg 0.6.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +23 -11
  2. package/dist/core/affiliated.d.ts +13 -0
  3. package/dist/core/affiliated.js +43 -0
  4. package/dist/core/backslashCommands.d.ts +10 -0
  5. package/dist/core/backslashCommands.js +43 -0
  6. package/dist/core/bracedScripts.d.ts +7 -0
  7. package/dist/core/bracedScripts.js +10 -0
  8. package/dist/core/frontmatterBlock.d.ts +89 -0
  9. package/dist/core/frontmatterBlock.js +448 -0
  10. package/dist/core/keyValueLines.d.ts +6 -0
  11. package/dist/core/keyValueLines.js +18 -0
  12. package/dist/core/lineSyntax.d.ts +16 -0
  13. package/dist/core/lineSyntax.js +25 -2
  14. package/dist/core/mdastToUniorg/blocks.d.ts +0 -1
  15. package/dist/core/mdastToUniorg/blocks.js +7 -32
  16. package/dist/core/mdastToUniorg/index.d.ts +6 -0
  17. package/dist/core/mdastToUniorg/index.js +72 -7
  18. package/dist/core/passthroughSource.d.ts +21 -0
  19. package/dist/core/passthroughSource.js +94 -0
  20. package/dist/core/tablePipes.d.ts +12 -3
  21. package/dist/core/tablePipes.js +38 -5
  22. package/dist/core/uniorgToMdast/elements.js +6 -1
  23. package/dist/core/uniorgToMdast/index.d.ts +3 -2
  24. package/dist/core/uniorgToMdast/index.js +100 -31
  25. package/dist/core/uniorgToMdast/lists.js +1 -1
  26. package/dist/core/uniorgToMdast/objects.js +6 -0
  27. package/dist/core/uniorgToMdast/shared.d.ts +5 -0
  28. package/dist/core/uniorgToMdast/shared.js +9 -13
  29. package/dist/index.d.ts +1 -2
  30. package/dist/markdownToOrg.js +41 -25
  31. package/dist/orgToMarkdown.js +18 -2
  32. package/dist/presets/logseq.d.ts +3 -22
  33. package/dist/presets/logseq.js +269 -186
  34. package/dist/presets/logseqOutline.d.ts +22 -0
  35. package/dist/presets/logseqOutline.js +251 -0
  36. package/dist/presets/types.d.ts +12 -1
  37. package/package.json +1 -1
@@ -5,13 +5,17 @@ import remarkFrontmatter from "remark-frontmatter";
5
5
  import remarkMath from "remark-math";
6
6
  import { uniorgStringify } from "uniorg-stringify";
7
7
  import { visit } from "unist-util-visit";
8
- import { transformMdastToUniorgAst } from "./core/mdastToUniorg/index.js";
8
+ import { transformMdastToUniorgDraft } from "./core/mdastToUniorg/index.js";
9
9
  import { detectMarkdownStyle, STYLE_KEYWORD } from "./core/markdownStyle.js";
10
10
  import { escapeOrgMarkup } from "./core/markupBoundary.js";
11
+ import { renderFileHeader } from "./core/frontmatterBlock.js";
11
12
  import { escapeLineSyntax } from "./core/lineSyntax.js";
12
13
  import { escapeFootnoteReferences } from "./core/footnoteReferences.js";
14
+ import { escapeBackslashCommands } from "./core/backslashCommands.js";
13
15
  import { escapeTablePipes } from "./core/tablePipes.js";
14
16
  import { requireBracedScripts } from "./core/bracedScripts.js";
17
+ import { keepPassthroughSource } from "./core/passthroughSource.js";
18
+ import { keyValueEntries } from "./core/keyValueLines.js";
15
19
  /**
16
20
  * Converts a Markdown string to an Org-mode string.
17
21
  * @param markdown The Markdown string to convert.
@@ -19,15 +23,21 @@ import { requireBracedScripts } from "./core/bracedScripts.js";
19
23
  * @returns The converted Org-mode string.
20
24
  */
21
25
  export function convertMarkdownToOrg(markdown, options = {}) {
26
+ const convertMarkdown = options.preset?.convertMarkdown;
27
+ return convertMarkdown
28
+ ? // a fragment's style is the preset's, not one to record
29
+ convertMarkdown(markdown, (fragment, preset) => convertMarkdownToOrg(fragment, {
30
+ ...options,
31
+ preset,
32
+ recordStyle: false
33
+ }))
34
+ : convertMarkdownDocument(markdown, options);
35
+ }
36
+ function convertMarkdownDocument(markdown, options) {
22
37
  // Phase 1: Parse Markdown to mdast
23
- const mdast = unified()
24
- .use(remarkParse)
25
- .use(remarkGfm)
26
- .use(remarkFrontmatter)
27
- .use(remarkMath)
28
- .parse(markdown);
38
+ const mdast = parseMarkdown(markdown, options.preset);
29
39
  // Phase 2: Generic mdast to uniorg-ast transformation
30
- let uniorgAst = transformMdastToUniorgAst(mdast, {
40
+ let uniorgAst = transformMdastToUniorgDraft(mdast, {
31
41
  ...(options.preserveMdisms !== undefined && {
32
42
  preserveMdisms: options.preserveMdisms
33
43
  }),
@@ -60,8 +70,10 @@ export function convertMarkdownToOrg(markdown, options = {}) {
60
70
  // Phase 3b: literal footnote references, paragraph lines org would
61
71
  // read as line syntax, literal markers org would read as markup, and
62
72
  // markup touching a word character, need a zero-width space escape;
63
- // a pipe in a table cell an entity
64
- escapeTablePipes(uniorgAst);
73
+ // a pipe in a table cell an entity; a literal backslash before a
74
+ // letter first, the entity being none
75
+ escapeBackslashCommands(uniorgAst);
76
+ escapeTablePipes(uniorgAst, options.onWarning);
65
77
  escapeFootnoteReferences(uniorgAst);
66
78
  escapeLineSyntax(uniorgAst);
67
79
  escapeOrgMarkup(uniorgAst);
@@ -70,12 +82,29 @@ export function convertMarkdownToOrg(markdown, options = {}) {
70
82
  // rendered: after the preset, whose text rewrites (wikilink aliases)
71
83
  // org parses too, and after the escapes, next to which org reads them
72
84
  requireBracedScripts(uniorgAst);
85
+ // Phase 3d: the file's header (ADR 0005): mode line and file-level
86
+ // drawer first, keywords apart from what follows, the frontmatter as
87
+ // a marked comment block; raw text, past every pass that rewrites text
88
+ renderFileHeader(uniorgAst);
73
89
  // Phase 4: Render uniorg-ast to Org-mode string
74
90
  const processor = unified().use(uniorgStringify);
75
91
  const orgContent = processor.stringify(uniorgAst);
76
92
  return orgContent;
77
93
  }
78
- // leads the document so it survives the frontmatter keywords following it
94
+ // a preset's dialect conventions that need the Markdown source apply
95
+ // to the parse, before the generic transform
96
+ function parseMarkdown(markdown, preset) {
97
+ const mdast = unified()
98
+ .use(remarkParse)
99
+ .use(remarkGfm)
100
+ .use(remarkFrontmatter)
101
+ .use(remarkMath)
102
+ .parse(markdown);
103
+ keepPassthroughSource(mdast, markdown);
104
+ preset?.applyToMdast?.(mdast, markdown);
105
+ return mdast;
106
+ }
107
+ // leads the document, ahead of restored keywords and the frontmatter block
79
108
  function recordStyleKeyword(uniorgAst, style) {
80
109
  if (!Object.keys(style).length) {
81
110
  return;
@@ -86,7 +115,6 @@ function recordStyleKeyword(uniorgAst, style) {
86
115
  value: JSON.stringify(style)
87
116
  });
88
117
  }
89
- const KEY_VALUE_LINE_RE = /^([\w-]+):: (.*)$/;
90
118
  function parseKeyValueParagraph(node) {
91
119
  if (node?.type !== "paragraph" || !node.children?.length) {
92
120
  return null;
@@ -94,19 +122,7 @@ function parseKeyValueParagraph(node) {
94
122
  if (!node.children.every(child => child.type === "text")) {
95
123
  return null;
96
124
  }
97
- const lines = node.children
98
- .map(child => child.value ?? "")
99
- .join("")
100
- .split("\n");
101
- const entries = [];
102
- for (const line of lines) {
103
- const match = KEY_VALUE_LINE_RE.exec(line);
104
- if (!match) {
105
- return null;
106
- }
107
- entries.push([match[1], match[2]]);
108
- }
109
- return entries;
125
+ return keyValueEntries(node.children.map(child => child.value ?? "").join(""));
110
126
  }
111
127
  function makeTimestamp(rawValue) {
112
128
  return { type: "timestamp", rawValue };
@@ -5,9 +5,12 @@ import remarkFrontmatter from "remark-frontmatter";
5
5
  import remarkMath from "remark-math";
6
6
  import { transformUniorgAstToMdast } from "./core/uniorgToMdast/index.js";
7
7
  import { takeRecordedStyle } from "./core/markdownStyle.js";
8
+ import { takeFileHeader } from "./core/frontmatterBlock.js";
8
9
  import { unescapeOrgMarkup } from "./core/markupBoundary.js";
9
10
  import { unescapeLineSyntax } from "./core/lineSyntax.js";
10
11
  import { unescapeFootnoteReferences } from "./core/footnoteReferences.js";
12
+ import { unescapeBackslashCommands } from "./core/backslashCommands.js";
13
+ import { unescapeTablePipes } from "./core/tablePipes.js";
11
14
  import { parseOrg } from "./core/bracedScripts.js";
12
15
  import { dropUnderscoreBulletGuards, guardUnderscoreBullets } from "./core/underscoreBullets.js";
13
16
  // a list item's paragraph after its nested list needs a blank line, or
@@ -26,18 +29,30 @@ function separateTextAfterNestedList(left, right, parent) {
26
29
  * @returns The converted Markdown string.
27
30
  */
28
31
  export function convertOrgToMarkdown(org, options = {}) {
32
+ const convertOrg = options.preset?.convertOrg;
33
+ return convertOrg
34
+ ? convertOrg(org, (fragment, preset) => convertOrgToMarkdown(fragment, { ...options, preset }))
35
+ : convertOrgDocument(org, options);
36
+ }
37
+ function convertOrgDocument(org, options) {
29
38
  // Phase 1: Parse Org-mode to uniorg-ast
30
39
  // md text has no scripts, so ^:{} is implied there and consumed here
31
40
  // (see markdownToOrg); uniorg misreads `_.` lines (see underscoreBullets)
32
- let uniorgAst = parseOrg(guardUnderscoreBullets(org));
41
+ const guarded = guardUnderscoreBullets(org);
42
+ let uniorgAst = parseOrg(guarded);
33
43
  dropUnderscoreBulletGuards(uniorgAst);
34
44
  // Phase 1b: a recorded style is morg's own (ADR 0004), so consume it so
35
45
  // it does not travel on as frontmatter; explicit options still win
36
46
  const recordedStyle = takeRecordedStyle(uniorgAst);
47
+ // frontmatter travels in a block marked as morg's (ADR 0005)
48
+ // and so does a file-level drawer (org-roam's :ID:)
49
+ const fileHeader = takeFileHeader(uniorgAst, guarded, options.onWarning);
37
50
  // Phase 1c: markdown needs no zero-width space escapes (inverse of md→org)
38
51
  unescapeOrgMarkup(uniorgAst);
39
52
  unescapeLineSyntax(uniorgAst);
40
53
  unescapeFootnoteReferences(uniorgAst);
54
+ unescapeBackslashCommands(uniorgAst);
55
+ unescapeTablePipes(uniorgAst);
41
56
  // Phase 2: Extract dialect preset conventions, if any
42
57
  if (options.preset?.extractFromUniorg) {
43
58
  uniorgAst = options.preset.extractFromUniorg(uniorgAst);
@@ -54,7 +69,8 @@ export function convertOrgToMarkdown(org, options = {}) {
54
69
  ...(options.orgismKeys !== undefined && {
55
70
  orgismKeys: options.orgismKeys
56
71
  }),
57
- ...(options.onWarning !== undefined && { onWarning: options.onWarning })
72
+ ...(options.onWarning !== undefined && { onWarning: options.onWarning }),
73
+ ...fileHeader
58
74
  });
59
75
  // Phase 4: Render mdast to Markdown string
60
76
  // bullet and rule "-" (not remark's default "*") are morg's canonical
@@ -1,25 +1,6 @@
1
- import type { OrgData } from "uniorg";
2
1
  import type { Preset } from "./types.js";
3
- export interface LogseqPresetOptions {
4
- /**
5
- * Content following a heading becomes children of that heading's block
6
- * in Logseq's outline: paragraphs turn into child block headlines one
7
- * level deeper; other constructs stay in the preceding block's body.
8
- * Default: `true`.
9
- */
10
- nestUnderHeadings?: boolean;
11
- }
12
2
  /**
13
- * Logseq dialect preset: `heading::` properties and outline nesting.
3
+ * Logseq dialect preset: the outline of blocks, page properties, page
4
+ * and block references, highlights and hiccup.
14
5
  */
15
- export declare function logseq(options?: LogseqPresetOptions): Preset;
16
- /**
17
- * Applies Logseq-specific conventions to a uniorg AST: every heading gets
18
- * a `:heading:` property drawer, and with `nestUnderHeadings` following
19
- * paragraphs become child block headlines (in Logseq's org format every
20
- * outline block is a headline).
21
- * @param uniorgAst The uniorg AST to transform.
22
- * @param nestUnderHeadings Nest content as child blocks. Default: `true`.
23
- * @returns The Logseq-flavored uniorg AST.
24
- */
25
- export declare function applyLogseqSpecificsToUniorgAst(uniorgAst: OrgData, nestUnderHeadings?: boolean): OrgData;
6
+ export declare function logseq(): Preset;