@remigius42/morg 0.5.0 → 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/README.md +12 -1
- package/dist/core/affiliated.d.ts +13 -0
- package/dist/core/affiliated.js +43 -0
- package/dist/core/backslashCommands.d.ts +10 -0
- package/dist/core/backslashCommands.js +43 -0
- package/dist/core/bracedScripts.d.ts +12 -0
- package/dist/core/bracedScripts.js +159 -0
- package/dist/core/footnoteReferences.d.ts +10 -0
- package/dist/core/footnoteReferences.js +24 -0
- package/dist/core/frontmatterBlock.d.ts +89 -0
- package/dist/core/frontmatterBlock.js +448 -0
- package/dist/core/keyValueLines.d.ts +6 -0
- package/dist/core/keyValueLines.js +18 -0
- package/dist/core/lineSyntax.d.ts +21 -0
- package/dist/core/lineSyntax.js +221 -0
- package/dist/core/markupBoundary.d.ts +12 -0
- package/dist/core/markupBoundary.js +195 -0
- package/dist/core/mdastToUniorg/blocks.d.ts +0 -1
- package/dist/core/mdastToUniorg/blocks.js +3 -31
- package/dist/core/mdastToUniorg/index.d.ts +6 -0
- package/dist/core/mdastToUniorg/index.js +81 -8
- package/dist/core/mdastToUniorg/lists.d.ts +1 -1
- package/dist/core/mdastToUniorg/lists.js +21 -2
- package/dist/core/mdastToUniorg/phrasing.js +145 -12
- package/dist/core/orgPath.d.ts +9 -0
- package/dist/core/orgPath.js +24 -0
- package/dist/core/render.d.ts +33 -0
- package/dist/core/render.js +101 -0
- package/dist/core/tablePipes.d.ts +17 -0
- package/dist/core/tablePipes.js +47 -0
- package/dist/core/underscoreBullets.d.ts +10 -0
- package/dist/core/underscoreBullets.js +32 -0
- package/dist/core/uniorgToMdast/elements.js +1 -1
- package/dist/core/uniorgToMdast/index.d.ts +3 -2
- package/dist/core/uniorgToMdast/index.js +100 -31
- package/dist/core/uniorgToMdast/lists.js +12 -1
- package/dist/core/uniorgToMdast/objects.js +44 -6
- package/dist/core/uniorgToMdast/shared.d.ts +5 -0
- package/dist/core/uniorgToMdast/shared.js +3 -12
- package/dist/core/uniorgToMdast/tables.js +12 -6
- package/dist/markdownToOrg.js +44 -23
- package/dist/orgToMarkdown.js +39 -5
- package/dist/presets/logseq.js +130 -0
- package/dist/presets/types.d.ts +4 -1
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -37,6 +37,13 @@ guarantee is to be **semantically faithful and convergent** instead
|
|
|
37
37
|
org properties; `org → md` serializes Org-only constructs ("org-isms") as
|
|
38
38
|
`key:: value` conventions ([ADR
|
|
39
39
|
0002](docs/adr/0002-mdism-property-namespace.md)).
|
|
40
|
+
- Frontmatter travels verbatim and inert in a
|
|
41
|
+
`#+begin_comment morg_frontmatter` block, not as org keywords, which
|
|
42
|
+
can act in Emacs; an org file's own leading keywords travel as a
|
|
43
|
+
`morg_keywords` frontmatter entry and come back as keywords, a
|
|
44
|
+
file-level drawer (org-roam's `:ID:`) as `morg_properties`. The
|
|
45
|
+
Logseq preset instead maps page properties natively ([ADR
|
|
46
|
+
0005](docs/adr/0005-frontmatter-as-a-marked-comment-block.md)).
|
|
40
47
|
- The few constructs that cannot be carried are documented in the
|
|
41
48
|
[mapping reference](docs/mappings.md) and reported as warnings.
|
|
42
49
|
|
|
@@ -198,7 +205,11 @@ preset })`: `preserveOrgisms` default `true`; `useHtml` (default
|
|
|
198
205
|
reverse direction restores headings from `:heading:` properties and
|
|
199
206
|
turns plain block headlines back into paragraphs. Hiccup blocks
|
|
200
207
|
(`[:div …]`) pass through as plain text and are emitted unescaped in
|
|
201
|
-
Markdown.
|
|
208
|
+
Markdown. Page properties map both directions: a first block of
|
|
209
|
+
`key:: value` lines and flat frontmatter entries ↔ leading
|
|
210
|
+
`#+key: value` lines, which Logseq reads as page properties;
|
|
211
|
+
frontmatter keys that act in Emacs (`todo`, `include`, …) stay inert.
|
|
212
|
+
Logseq's own syntax maps both directions: `TODO`/`DONE`
|
|
202
213
|
text markers and `[#A]` priorities ↔ org keywords/priorities, page
|
|
203
214
|
references `[[page]]` and labeled forms `[label]([[page]])` ↔ org
|
|
204
215
|
fuzzy links `[[page][label]]`, block refs `[label](((uuid)))` ↔
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { AffiliatedKeywords } from "uniorg";
|
|
2
|
+
export declare const DUAL_NAMES = "CAPTION|RESULTS";
|
|
3
|
+
/**
|
|
4
|
+
* An element's affiliated keywords as key, value pairs, the way they are
|
|
5
|
+
* written above it: several values (#+CAPTION, #+HEADER, #+ATTR_*) as
|
|
6
|
+
* several pairs, parsed ones (#+CAPTION) as org text, a dual value as
|
|
7
|
+
* part of the key (`CAPTION[short]`).
|
|
8
|
+
* @param affiliated The element's affiliated keywords.
|
|
9
|
+
* @param asOrg Parsed values as org text, links and markup kept, not as
|
|
10
|
+
* plain text (a Markdown line above a body element reads markup anew).
|
|
11
|
+
* @returns The pairs in order.
|
|
12
|
+
*/
|
|
13
|
+
export declare function affiliatedEntries(affiliated?: AffiliatedKeywords, asOrg?: boolean): [string, string][];
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { toString } from "orgast-util-to-string";
|
|
2
|
+
import { renderInline } from "./render.js";
|
|
3
|
+
const MULTIPLE_RE = /^(?:CAPTION|HEADER|ATTR_.*)$/;
|
|
4
|
+
// the keywords org reads with a dual value (`#+CAPTION[short]: long`);
|
|
5
|
+
// aliases (RESULT) are not
|
|
6
|
+
export const DUAL_NAMES = "CAPTION|RESULTS";
|
|
7
|
+
const DUAL_RE = new RegExp(`^(?:${DUAL_NAMES})$`);
|
|
8
|
+
/**
|
|
9
|
+
* An element's affiliated keywords as key, value pairs, the way they are
|
|
10
|
+
* written above it: several values (#+CAPTION, #+HEADER, #+ATTR_*) as
|
|
11
|
+
* several pairs, parsed ones (#+CAPTION) as org text, a dual value as
|
|
12
|
+
* part of the key (`CAPTION[short]`).
|
|
13
|
+
* @param affiliated The element's affiliated keywords.
|
|
14
|
+
* @param asOrg Parsed values as org text, links and markup kept, not as
|
|
15
|
+
* plain text (a Markdown line above a body element reads markup anew).
|
|
16
|
+
* @returns The pairs in order.
|
|
17
|
+
*/
|
|
18
|
+
export function affiliatedEntries(affiliated = {}, asOrg = false) {
|
|
19
|
+
return Object.entries(affiliated).flatMap(([key, value]) => (MULTIPLE_RE.test(key) ? value : [value]).map(item => {
|
|
20
|
+
const [main, dual] = DUAL_RE.test(key) && isDual(item) ? item : [item];
|
|
21
|
+
return [
|
|
22
|
+
dual === undefined ? key : `${key}[${dual}]`,
|
|
23
|
+
asOrg ? orgText(main) : plainText(main)
|
|
24
|
+
];
|
|
25
|
+
}));
|
|
26
|
+
}
|
|
27
|
+
function isDual(value) {
|
|
28
|
+
return (Array.isArray(value) && value.length === 2 && typeof value[1] === "string");
|
|
29
|
+
}
|
|
30
|
+
function plainText(value) {
|
|
31
|
+
if (typeof value === "string") {
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
return Array.isArray(value)
|
|
35
|
+
? value.map(plainText).join("")
|
|
36
|
+
: toString(value);
|
|
37
|
+
}
|
|
38
|
+
function orgText(value) {
|
|
39
|
+
const items = Array.isArray(value) ? value : [value];
|
|
40
|
+
return items
|
|
41
|
+
.map(item => (typeof item === "string" ? item : renderInline(item)))
|
|
42
|
+
.join("");
|
|
43
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Parent } from "unist";
|
|
2
|
+
/**
|
|
3
|
+
* md→org: escapes a literal backslash org would read as a command, but
|
|
4
|
+
* not in a passthrough element's org text (`#+begin_export latex`).
|
|
5
|
+
*/
|
|
6
|
+
export declare function escapeBackslashCommands(tree: Parent): void;
|
|
7
|
+
/**
|
|
8
|
+
* org→md: drops the zero-width spaces `escapeBackslashCommands` inserts.
|
|
9
|
+
*/
|
|
10
|
+
export declare function unescapeBackslashCommands(tree: Parent): void;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { EXIT, SKIP, visit } from "unist-util-visit";
|
|
2
|
+
import { isPassthroughParagraph } from "./lineSyntax.js";
|
|
3
|
+
import { ZERO_WIDTH_SPACE } from "./markupBoundary.js";
|
|
4
|
+
// org reads a backslash before a letter as an entity (`\alpha`) or a
|
|
5
|
+
// LaTeX fragment (`\Users`, `\foo{x}`), before `(` or `[` as a LaTeX
|
|
6
|
+
// fragment too; a zero-width space after the backslash leaves it text
|
|
7
|
+
const COMMAND_RE = /\\(?=[A-Za-z([])/g;
|
|
8
|
+
const ESCAPED_RE = new RegExp(String.raw `\\${ZERO_WIDTH_SPACE}(?=[A-Za-z([])`, "g");
|
|
9
|
+
/**
|
|
10
|
+
* md→org: escapes a literal backslash org would read as a command, but
|
|
11
|
+
* not in a passthrough element's org text (`#+begin_export latex`).
|
|
12
|
+
*/
|
|
13
|
+
export function escapeBackslashCommands(tree) {
|
|
14
|
+
visit(tree, (node, index, parent) => {
|
|
15
|
+
if (holdsCommand(node) &&
|
|
16
|
+
isPassthroughParagraph(node, index ?? 0, parent)) {
|
|
17
|
+
return SKIP;
|
|
18
|
+
}
|
|
19
|
+
if (node.type === "text") {
|
|
20
|
+
const text = node;
|
|
21
|
+
text.value = text.value?.replace(COMMAND_RE, `\\${ZERO_WIDTH_SPACE}`);
|
|
22
|
+
}
|
|
23
|
+
return undefined;
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
// whether a text in `node` holds a backslash org would read as a
|
|
27
|
+
// command: the only paragraphs worth a passthrough check
|
|
28
|
+
function holdsCommand(node) {
|
|
29
|
+
let found = false;
|
|
30
|
+
visit(node, "text", (text) => {
|
|
31
|
+
found ||= new RegExp(COMMAND_RE.source).test(text.value ?? "");
|
|
32
|
+
return found ? EXIT : undefined;
|
|
33
|
+
});
|
|
34
|
+
return found;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* org→md: drops the zero-width spaces `escapeBackslashCommands` inserts.
|
|
38
|
+
*/
|
|
39
|
+
export function unescapeBackslashCommands(tree) {
|
|
40
|
+
visit(tree, "text", (node) => {
|
|
41
|
+
node.value = node.value?.replace(ESCAPED_RE, "\\");
|
|
42
|
+
});
|
|
43
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { OrgData } from "uniorg";
|
|
2
|
+
/**
|
|
3
|
+
* md→org: adds `^:{}` to the document's `#+OPTIONS:` when its text
|
|
4
|
+
* holds a bare underscore or caret org would read as a script.
|
|
5
|
+
*/
|
|
6
|
+
export declare function requireBracedScripts(uniorgAst: OrgData): void;
|
|
7
|
+
/**
|
|
8
|
+
* org→md: parses org, honoring its `^:` setting, and consuming `^:{}`
|
|
9
|
+
* where the text needs it: md→org adds it only then, so anywhere else
|
|
10
|
+
* it is the author's own setting.
|
|
11
|
+
*/
|
|
12
|
+
export declare function parseOrg(org: string): OrgData;
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { unified } from "unified";
|
|
2
|
+
import uniorgParse from "uniorg-parse";
|
|
3
|
+
import { EXIT, visit } from "unist-util-visit";
|
|
4
|
+
import { isInline, orgParser, positionParser, renderChildren, tryParse } from "./render.js";
|
|
5
|
+
// org's `#+OPTIONS: ^:{}` limits sub/superscripts to the braced form
|
|
6
|
+
// (`H_{2}O`), so a bare underscore or caret (`a_b`, `x^y`) stays text.
|
|
7
|
+
// Markdown has no script syntax, so md text needs it; the braced
|
|
8
|
+
// scripts morg itself emits are unaffected
|
|
9
|
+
const BRACED_SCRIPTS = "^:{}";
|
|
10
|
+
// built once: constructing a processor per parse dominates the cost
|
|
11
|
+
const bracedScriptsParser = unified()
|
|
12
|
+
.use(uniorgParse, { useSubSuperscripts: "{}" })
|
|
13
|
+
.freeze();
|
|
14
|
+
function isScript(node) {
|
|
15
|
+
return node.type === "subscript" || node.type === "superscript";
|
|
16
|
+
}
|
|
17
|
+
// a tree uniorg fails to read counts as holding none
|
|
18
|
+
function countScripts(tree) {
|
|
19
|
+
let count = 0;
|
|
20
|
+
if (tree) {
|
|
21
|
+
visit(tree, (node) => {
|
|
22
|
+
if (isScript(node)) {
|
|
23
|
+
count++;
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
return count;
|
|
28
|
+
}
|
|
29
|
+
// a block's inline content as org renders it; a block element inside
|
|
30
|
+
// (a list item's nested list) only ends a line
|
|
31
|
+
function renderedContent(node) {
|
|
32
|
+
if (isInline(node) ||
|
|
33
|
+
!("children" in node) ||
|
|
34
|
+
!node.children.some(isInline)) {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
return renderChildren(node.children).join("");
|
|
38
|
+
}
|
|
39
|
+
// a script org reads without `^:{}` only (uniorg's
|
|
40
|
+
// matchSubstringRegex without the braced form, loosened): a `_` or `^`
|
|
41
|
+
// after a non-blank, then `(`, `*`, or a word ending in a letter or
|
|
42
|
+
// digit. Text without one reads the same either way and skips the parses
|
|
43
|
+
const BARE_SCRIPT_RE = /\S[_^](?:[(*]|[+-]?[\p{L}\p{N}.,\\]*[\p{L}\p{N}])/u;
|
|
44
|
+
// whether `tree` (of `content`) holds a script not in the braced form.
|
|
45
|
+
// uniorg tries the braced form first, so without one both parsers read
|
|
46
|
+
// the same, and the braced parse can be skipped
|
|
47
|
+
function holdsBareScript(tree, content) {
|
|
48
|
+
let found = false;
|
|
49
|
+
visit(tree, (node) => {
|
|
50
|
+
const offset = node.position?.start.offset;
|
|
51
|
+
found =
|
|
52
|
+
isScript(node) && offset !== undefined && content[offset + 1] !== "{";
|
|
53
|
+
return found ? EXIT : undefined;
|
|
54
|
+
});
|
|
55
|
+
return found;
|
|
56
|
+
}
|
|
57
|
+
function readsBareScriptsIn(content) {
|
|
58
|
+
if (!BARE_SCRIPT_RE.test(content)) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
const tree = tryParse(content, positionParser);
|
|
62
|
+
return (tree !== undefined &&
|
|
63
|
+
holdsBareScript(tree, content) &&
|
|
64
|
+
countScripts(tree) > countScripts(tryParse(content, bracedScriptsParser)));
|
|
65
|
+
}
|
|
66
|
+
// whether org reads a script in the text that `^:{}` would keep text.
|
|
67
|
+
// Checked per block as rendered, not per text node: the char before a
|
|
68
|
+
// `_` or `^` may belong to a neighbor (a marker, a link's `]`, an
|
|
69
|
+
// escape); a script never spans blocks
|
|
70
|
+
function readsBareScripts(tree) {
|
|
71
|
+
let found = false;
|
|
72
|
+
visit(tree, (node) => {
|
|
73
|
+
const content = renderedContent(node);
|
|
74
|
+
found = content !== undefined && readsBareScriptsIn(content);
|
|
75
|
+
return found ? EXIT : undefined;
|
|
76
|
+
});
|
|
77
|
+
return found;
|
|
78
|
+
}
|
|
79
|
+
function isOptions(node) {
|
|
80
|
+
return (node.type === "keyword" && node.key.toUpperCase() === "OPTIONS");
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* md→org: adds `^:{}` to the document's `#+OPTIONS:` when its text
|
|
84
|
+
* holds a bare underscore or caret org would read as a script.
|
|
85
|
+
*/
|
|
86
|
+
export function requireBracedScripts(uniorgAst) {
|
|
87
|
+
if (!readsBareScripts(uniorgAst)) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const options = uniorgAst.children.find(isOptions);
|
|
91
|
+
if (!options) {
|
|
92
|
+
uniorgAst.children.unshift({
|
|
93
|
+
type: "keyword",
|
|
94
|
+
key: "OPTIONS",
|
|
95
|
+
value: BRACED_SCRIPTS
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
else if (!/(^|\s)\^:/.test(options.value)) {
|
|
99
|
+
// an explicit ^: setting is the author's call
|
|
100
|
+
options.value = `${options.value} ${BRACED_SCRIPTS}`;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// the `^:` setting in an `#+OPTIONS:` value, if any
|
|
104
|
+
function scriptsSetting(options) {
|
|
105
|
+
const items = options.split(/\s+/).filter(item => item.startsWith("^:"));
|
|
106
|
+
return items.at(-1)?.slice(2);
|
|
107
|
+
}
|
|
108
|
+
// org→md honors every `^:` setting: `{}` limits scripts to the braced
|
|
109
|
+
// form, `nil` turns them off, `t` (org's default) keeps them on
|
|
110
|
+
const scriptsParsers = {
|
|
111
|
+
"{}": bracedScriptsParser,
|
|
112
|
+
nil: unified().use(uniorgParse, { useSubSuperscripts: false }).freeze()
|
|
113
|
+
};
|
|
114
|
+
// the settings the document may use, which the parser has to know up
|
|
115
|
+
// front; only a top-level keyword counts, which takes a parse to tell
|
|
116
|
+
function mayUseScripts(org) {
|
|
117
|
+
const settings = [...org.matchAll(/^[ \t]*#\+options:(.*)$/gim)].map(([, value]) => scriptsSetting(value ?? ""));
|
|
118
|
+
return [...new Set(settings)].filter((setting) => setting !== undefined);
|
|
119
|
+
}
|
|
120
|
+
function usesScripts(uniorgAst, setting) {
|
|
121
|
+
return uniorgAst.children.some(node => isOptions(node) && scriptsSetting(node.value) === setting);
|
|
122
|
+
}
|
|
123
|
+
// drops `^:{}` from the top-level `#+OPTIONS:`, the whole keyword if
|
|
124
|
+
// nothing else is left; md text has no scripts, so the return trip
|
|
125
|
+
// re-adds it wherever it is needed
|
|
126
|
+
function takeBracedScripts(uniorgAst) {
|
|
127
|
+
uniorgAst.children = uniorgAst.children.filter(node => {
|
|
128
|
+
if (!isOptions(node)) {
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
node.value = node.value
|
|
132
|
+
.split(/\s+/)
|
|
133
|
+
.filter(item => item && item !== BRACED_SCRIPTS)
|
|
134
|
+
.join(" ");
|
|
135
|
+
return node.value !== "";
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* org→md: parses org, honoring its `^:` setting, and consuming `^:{}`
|
|
140
|
+
* where the text needs it: md→org adds it only then, so anywhere else
|
|
141
|
+
* it is the author's own setting.
|
|
142
|
+
*/
|
|
143
|
+
export function parseOrg(org) {
|
|
144
|
+
for (const setting of mayUseScripts(org)) {
|
|
145
|
+
const parser = scriptsParsers[setting];
|
|
146
|
+
if (!parser) {
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
// keywords parse the same either way
|
|
150
|
+
const uniorgAst = parser.parse(org);
|
|
151
|
+
if (usesScripts(uniorgAst, setting)) {
|
|
152
|
+
if (setting === "{}" && readsBareScripts(uniorgAst)) {
|
|
153
|
+
takeBracedScripts(uniorgAst);
|
|
154
|
+
}
|
|
155
|
+
return uniorgAst;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return orgParser.parse(org);
|
|
159
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Parent } from "unist";
|
|
2
|
+
/**
|
|
3
|
+
* md→org: escapes a literal `[fn:` in text.
|
|
4
|
+
*/
|
|
5
|
+
export declare function escapeFootnoteReferences(tree: Parent): void;
|
|
6
|
+
/**
|
|
7
|
+
* org→md: drops the zero-width spaces `escapeFootnoteReferences`
|
|
8
|
+
* inserts.
|
|
9
|
+
*/
|
|
10
|
+
export declare function unescapeFootnoteReferences(tree: Parent): void;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { visit } from "unist-util-visit";
|
|
2
|
+
import { ZERO_WIDTH_SPACE } from "./markupBoundary.js";
|
|
3
|
+
// org reads `[fn:` in text as a footnote reference (`[fn:1]`, `[fn::x]`,
|
|
4
|
+
// `[fn:a:x]`), at a line start as a definition; a zero-width space after
|
|
5
|
+
// the `[` leaves it text
|
|
6
|
+
const REFERENCE = "[fn:";
|
|
7
|
+
const ESCAPED = `[${ZERO_WIDTH_SPACE}fn:`;
|
|
8
|
+
/**
|
|
9
|
+
* md→org: escapes a literal `[fn:` in text.
|
|
10
|
+
*/
|
|
11
|
+
export function escapeFootnoteReferences(tree) {
|
|
12
|
+
visit(tree, "text", (node) => {
|
|
13
|
+
node.value = node.value?.replaceAll(REFERENCE, ESCAPED);
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* org→md: drops the zero-width spaces `escapeFootnoteReferences`
|
|
18
|
+
* inserts.
|
|
19
|
+
*/
|
|
20
|
+
export function unescapeFootnoteReferences(tree) {
|
|
21
|
+
visit(tree, "text", (node) => {
|
|
22
|
+
node.value = node.value?.replaceAll(ESCAPED, REFERENCE);
|
|
23
|
+
});
|
|
24
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { OrgData } from "uniorg";
|
|
2
|
+
import { type Scalar } from "yaml";
|
|
3
|
+
export interface FrontmatterNode {
|
|
4
|
+
type: "morg-frontmatter";
|
|
5
|
+
yaml: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function isFrontmatterNode(node: {
|
|
8
|
+
type: string;
|
|
9
|
+
}): node is FrontmatterNode;
|
|
10
|
+
/**
|
|
11
|
+
* md→org: puts the file's header in place, past keywords any pass put
|
|
12
|
+
* in front: an Emacs mode line and the file-level drawer lead the file,
|
|
13
|
+
* keywords org would attach to what follows stay apart, and the
|
|
14
|
+
* frontmatter node becomes the marked comment block.
|
|
15
|
+
* @param uniorgAst The document.
|
|
16
|
+
*/
|
|
17
|
+
export declare function renderFileHeader(uniorgAst: OrgData): void;
|
|
18
|
+
/**
|
|
19
|
+
* md→org: takes the top-level entries `take` maps to keywords out of
|
|
20
|
+
* the frontmatter; the rest stays verbatim, comments included.
|
|
21
|
+
* @param yaml The frontmatter's YAML text.
|
|
22
|
+
* @param take Keywords for an entry, or null to leave it in place.
|
|
23
|
+
* @returns The keywords in order, and the remaining YAML text.
|
|
24
|
+
*/
|
|
25
|
+
export declare function takeFrontmatterEntries(yaml: string, take: (key: Scalar, value: unknown, text: (scalar: Scalar) => string) => [string, string][] | null): {
|
|
26
|
+
keywords: [string, string][];
|
|
27
|
+
yaml: string;
|
|
28
|
+
};
|
|
29
|
+
export declare const KEYWORD_NAME: string;
|
|
30
|
+
export declare function fitsKeywordLine(key: string, value: string): boolean;
|
|
31
|
+
export declare function fitsPropertyLine(key: string, value: string): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* org→md: whether morg's own entries (`morg_properties`,
|
|
34
|
+
* `morg_keywords`) can join the frontmatter: whether the YAML with one
|
|
35
|
+
* more top-level entry appended still parses as a single block mapping
|
|
36
|
+
* holding none of them yet. Comments alone take them; an indented
|
|
37
|
+
* mapping, a list, a flow mapping or a document ending in `...` do not.
|
|
38
|
+
* @param yaml The frontmatter's YAML text.
|
|
39
|
+
*/
|
|
40
|
+
export declare function takesMorgEntries(yaml: string): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* md→org: one of morg's own entries, a sequence of one-entry maps
|
|
43
|
+
* (`morg_keywords`, `morg_properties`, ADR 0005), restored only if
|
|
44
|
+
* every item fits its org line.
|
|
45
|
+
* @param yaml The frontmatter's YAML text.
|
|
46
|
+
* @param name The entry's key.
|
|
47
|
+
* @param fits Whether a key and value fit the org line they go back to.
|
|
48
|
+
* @returns The key, value pairs in order, and the remaining YAML text.
|
|
49
|
+
*/
|
|
50
|
+
export declare function takeMorgEntry(yaml: string, name: string, fits: (key: string, value: string) => boolean): {
|
|
51
|
+
keywords: [string, string][];
|
|
52
|
+
yaml: string;
|
|
53
|
+
};
|
|
54
|
+
interface FileHeader {
|
|
55
|
+
frontmatter?: string;
|
|
56
|
+
fileProperties?: [string, string][];
|
|
57
|
+
takesMorgEntries: boolean;
|
|
58
|
+
startsWithModeLine: boolean;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* org→md: takes the file's header morg carries as frontmatter: the
|
|
62
|
+
* marked block and, where its YAML can take it, a file-level drawer.
|
|
63
|
+
* @param uniorgAst The parsed document.
|
|
64
|
+
* @param org The text it was parsed from.
|
|
65
|
+
* @param onWarning Reports a marked block morg cannot take.
|
|
66
|
+
* @returns The transform options holding them.
|
|
67
|
+
*/
|
|
68
|
+
export declare function takeFileHeader(uniorgAst: OrgData, org: string, onWarning?: (message: string) => void): FileHeader;
|
|
69
|
+
/**
|
|
70
|
+
* org→md, for a tree `transformMdastToUniorgAst` built rather than one
|
|
71
|
+
* parsed from org text: its file header, the block still raw text.
|
|
72
|
+
* @param uniorgAst The document; its header nodes are removed.
|
|
73
|
+
* @returns The transform options holding the header.
|
|
74
|
+
*/
|
|
75
|
+
export declare function takeRenderedFileHeader(uniorgAst: OrgData): FileHeader;
|
|
76
|
+
export declare function isModeLine(comment: string): boolean;
|
|
77
|
+
export declare function isModeLineComment(node: {
|
|
78
|
+
type: string;
|
|
79
|
+
}): boolean;
|
|
80
|
+
/**
|
|
81
|
+
* md→org: marks the comment made from the Markdown body's first node as
|
|
82
|
+
* the file's mode line, if it is a -*- comment: what passes do to the
|
|
83
|
+
* tree before the header is rendered cannot make another one lead.
|
|
84
|
+
* @param node The uniorg node made from the body's first node.
|
|
85
|
+
*/
|
|
86
|
+
export declare function markModeLine(node: {
|
|
87
|
+
type: string;
|
|
88
|
+
} | null): void;
|
|
89
|
+
export {};
|