@remigius42/morg 0.8.0 → 0.9.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 +22 -6
- package/dist/cli/args.d.ts +2 -0
- package/dist/cli/args.js +2 -0
- package/dist/cli/conversion.d.ts +3 -3
- package/dist/cli/conversion.js +4 -4
- package/dist/cli/flags.d.ts +1 -1
- package/dist/cli/flags.js +15 -1
- package/dist/cli/formats.d.ts +2 -1
- package/dist/cli/formats.js +1 -0
- package/dist/cli/presets.d.ts +13 -2
- package/dist/cli/presets.js +17 -3
- package/dist/cli.js +4 -5
- package/dist/config.d.ts +9 -4
- package/dist/config.js +2 -0
- package/dist/conversionOptions.d.ts +19 -3
- package/dist/conversionOptions.js +61 -0
- package/dist/core/frontmatterBlock.d.ts +7 -0
- package/dist/core/frontmatterBlock.js +7 -2
- package/dist/core/lineSyntax.d.ts +6 -0
- package/dist/core/lineSyntax.js +6 -2
- package/dist/index.d.ts +1 -1
- package/dist/markdownToOrg.js +15 -15
- package/dist/normalize.js +16 -2
- package/dist/options.d.ts +3 -7
- package/dist/orgToMarkdown.js +13 -9
- package/dist/presets/hooks.d.ts +49 -0
- package/dist/presets/hooks.js +72 -0
- package/dist/presets/logseq.js +187 -29
- package/dist/presets/logseqOutline.d.ts +29 -4
- package/dist/presets/logseqOutline.js +268 -91
- package/dist/presets/logseqVanillaMarkdown.d.ts +9 -0
- package/dist/presets/logseqVanillaMarkdown.js +295 -0
- package/dist/presets/obsidian.js +4 -2
- package/dist/presets/registry.d.ts +1 -1
- package/dist/presets/registry.js +5 -3
- package/dist/presets/sides.d.ts +40 -0
- package/dist/presets/sides.js +42 -0
- package/dist/presets/types.d.ts +49 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -60,7 +60,10 @@ color schemes.
|
|
|
60
60
|
Try morg without installing anything at
|
|
61
61
|
[morg.binarypoetry.ch](https://morg.binarypoetry.ch). All conversion
|
|
62
62
|
happens in your browser, nothing is uploaded (see [ADR
|
|
63
|
-
0003](docs/adr/0003-client-side-web-ui-on-github-pages.md)).
|
|
63
|
+
0003](docs/adr/0003-client-side-web-ui-on-github-pages.md)). Each side
|
|
64
|
+
names its format and dialect (Input: Org (Logseq), Output: Markdown);
|
|
65
|
+
the direction follows from the two, the same on both sides normalizes,
|
|
66
|
+
and ⇄ swaps them. The
|
|
64
67
|
chrome-less embed page (`/embed.html`, optionally with
|
|
65
68
|
`?theme=dark|light`) can be iframed into other sites. It posts its
|
|
66
69
|
content height to the host on every change, so the frame can follow it
|
|
@@ -121,6 +124,9 @@ echo "# Hello" | morg --from markdown
|
|
|
121
124
|
# Apply a dialect preset
|
|
122
125
|
morg --input page.md --output page.org --preset logseq
|
|
123
126
|
|
|
127
|
+
# Or one per side: read Logseq org, write Vanilla Markdown (ADR 0006)
|
|
128
|
+
morg --input page.org --output page.md --input-preset logseq
|
|
129
|
+
|
|
124
130
|
# Dropped constructs are reported on stderr; -s / --silent suppresses.
|
|
125
131
|
# Boolean flags take an optional value, so --silent false overrides a
|
|
126
132
|
# morg.toml that sets it
|
|
@@ -141,7 +147,7 @@ morg normalize --input notes.org --output notes.org
|
|
|
141
147
|
|
|
142
148
|
Options can live in a `morg.toml` (auto-discovered in the working
|
|
143
149
|
directory, or passed via `--config path`). Precedence: CLI flags >
|
|
144
|
-
config file > defaults:
|
|
150
|
+
config file > defaults, per side for the presets:
|
|
145
151
|
|
|
146
152
|
```toml
|
|
147
153
|
preset = "logseq"
|
|
@@ -217,10 +223,17 @@ preset })`: `preserveOrgisms` default `true`; `useHtml` (default
|
|
|
217
223
|
`[[((uuid))][label]]`, and `^^highlight^^` markup survives verbatim
|
|
218
224
|
(it would otherwise re-parse as superscripts).
|
|
219
225
|
- `obsidian()`: wikilinks `[[Page]]` / `[[Page|alias]]` ↔ org fuzzy links
|
|
226
|
+
- `inputPreset` / `outputPreset` (on both conversions): the dialect
|
|
227
|
+
the input is read in and the one the output is written in (ADR
|
|
228
|
+
0006); leaving one out is Vanilla. `preset` sets both, but leaves a
|
|
229
|
+
side Vanilla whose format the preset has no dialect for (Obsidian
|
|
230
|
+
writes no org); a side preset without one throws, and so does
|
|
231
|
+
`preset` naming another preset than a side preset.
|
|
220
232
|
|
|
221
233
|
- `normalizeMarkdown(md, { preset })` / `normalizeOrg(org, { preset })`
|
|
222
234
|
(CLI: `morg normalize`): one full round trip to morg's canonical
|
|
223
|
-
form, a fixed point
|
|
235
|
+
form, a fixed point, within one dialect: different presets per side
|
|
236
|
+
throw. Canonicalization, not styling: org-isms and
|
|
224
237
|
md-isms are rewritten exactly as a conversion would rewrite them.
|
|
225
238
|
Normalize with the same preset/config you will convert with, since
|
|
226
239
|
convergence is per-config (ADR 0002).
|
|
@@ -246,8 +259,8 @@ Two pipelines, each with a two-phase transformation separating the
|
|
|
246
259
|
dialect-agnostic core from dialect presets:
|
|
247
260
|
|
|
248
261
|
```text
|
|
249
|
-
md → org: remark-parse → mdast→uniorg (core) →
|
|
250
|
-
org → md: uniorg-parse →
|
|
262
|
+
md → org: remark-parse → mdast→uniorg (core) → read md dialect → write org dialect → uniorg-stringify
|
|
263
|
+
org → md: uniorg-parse → read org dialect → write md dialect → uniorg→mdast (core) → remark-stringify
|
|
251
264
|
```
|
|
252
265
|
|
|
253
266
|
Formatting is controlled by shaping the AST (e.g. inserting newline text nodes),
|
|
@@ -261,7 +274,10 @@ Project vocabulary lives in [CONTEXT.md](CONTEXT.md); design decisions in
|
|
|
261
274
|
|
|
262
275
|
The core conversion surface is feature-complete and validated against
|
|
263
276
|
real-world Logseq org vaults (edge cases found there live on as
|
|
264
|
-
anonymized fixtures, e.g. `tests/fixtures/logseq-vault.org`)
|
|
277
|
+
anonymized fixtures, e.g. `tests/fixtures/logseq-vault.org`), and
|
|
278
|
+
the conversions between a Logseq dialect and Vanilla Org or Markdown
|
|
279
|
+
are checked by round trips of that vault and of public Markdown and
|
|
280
|
+
Org documentation from either side; the
|
|
265
281
|
client-side [Web UI](https://morg.binarypoetry.ch) is deployed from
|
|
266
282
|
`main`. The npm package is `@remigius42/morg`, since the bare `morg`
|
|
267
283
|
name is taken, and pushing a `v*` tag publishes it. Most of the code is
|
package/dist/cli/args.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ export interface CliArgs {
|
|
|
7
7
|
inputFile: string | undefined;
|
|
8
8
|
outputFile: string | undefined;
|
|
9
9
|
presetName: string | undefined;
|
|
10
|
+
inputPresetName: string | undefined;
|
|
11
|
+
outputPresetName: string | undefined;
|
|
10
12
|
silent: boolean | undefined;
|
|
11
13
|
taskCheckboxes: boolean | undefined;
|
|
12
14
|
interpretHtml: boolean | undefined;
|
package/dist/cli/args.js
CHANGED
package/dist/cli/conversion.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { MorgConfig } from "../config.js";
|
|
2
2
|
import type { MarkdownStyleOptions } from "../options.js";
|
|
3
|
-
import type {
|
|
3
|
+
import type { PresetOptions } from "../presets/sides.js";
|
|
4
4
|
import type { CliArgs } from "./args.js";
|
|
5
|
-
export declare function buildConversionOptions(cli: CliArgs, config: MorgConfig,
|
|
5
|
+
export declare function buildConversionOptions(cli: CliArgs, config: MorgConfig, presets: PresetOptions): {
|
|
6
6
|
mdToOrgOptions: import("../options.js").MarkdownToOrgOptions;
|
|
7
7
|
orgToMdOptions: import("../options.js").OrgToMarkdownOptions & {
|
|
8
8
|
markdownStyle: MarkdownStyleOptions;
|
|
9
9
|
};
|
|
10
10
|
};
|
|
11
|
-
export declare function convert(inputContent: string, fromFormat: string, normalize: boolean, cli: CliArgs, config: MorgConfig,
|
|
11
|
+
export declare function convert(inputContent: string, fromFormat: string, normalize: boolean, cli: CliArgs, config: MorgConfig, presets: PresetOptions): string;
|
package/dist/cli/conversion.js
CHANGED
|
@@ -3,7 +3,7 @@ import { convertOrgToMarkdown } from "../orgToMarkdown.js";
|
|
|
3
3
|
import { normalizeMarkdown, normalizeOrg } from "../normalize.js";
|
|
4
4
|
import { buildConversionOptions as layerOptions } from "../conversionOptions.js";
|
|
5
5
|
import { CliError } from "./error.js";
|
|
6
|
-
export function buildConversionOptions(cli, config,
|
|
6
|
+
export function buildConversionOptions(cli, config, presets) {
|
|
7
7
|
// an explicit --silent wins over the config; dropped constructs are
|
|
8
8
|
// reported on stderr unless it ends up on
|
|
9
9
|
const silent = cli.silent ?? config.silent ?? false;
|
|
@@ -23,14 +23,14 @@ export function buildConversionOptions(cli, config, preset) {
|
|
|
23
23
|
recordStyle: cli.recordStyle,
|
|
24
24
|
markdownStyle
|
|
25
25
|
}, config, {
|
|
26
|
-
|
|
26
|
+
...presets,
|
|
27
27
|
onWarning,
|
|
28
28
|
...(config.orgismKeys && { orgismKeys: config.orgismKeys })
|
|
29
29
|
});
|
|
30
30
|
}
|
|
31
|
-
export function convert(inputContent, fromFormat, normalize, cli, config,
|
|
31
|
+
export function convert(inputContent, fromFormat, normalize, cli, config, presets) {
|
|
32
32
|
try {
|
|
33
|
-
const { mdToOrgOptions, orgToMdOptions } = buildConversionOptions(cli, config,
|
|
33
|
+
const { mdToOrgOptions, orgToMdOptions } = buildConversionOptions(cli, config, presets);
|
|
34
34
|
if (normalize) {
|
|
35
35
|
return fromFormat === "markdown"
|
|
36
36
|
? normalizeMarkdown(inputContent, {
|
package/dist/cli/flags.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
type StringOption = "fromFormat" | "toFormat" | "inputFile" | "outputFile" | "presetName" | "configPath";
|
|
1
|
+
type StringOption = "fromFormat" | "toFormat" | "inputFile" | "outputFile" | "presetName" | "inputPresetName" | "outputPresetName" | "configPath";
|
|
2
2
|
type BooleanOption = "silent" | "taskCheckboxes" | "interpretHtml" | "recordStyle";
|
|
3
3
|
type InfoOption = "help" | "version";
|
|
4
4
|
export type FlagSpec = ({
|
package/dist/cli/flags.js
CHANGED
|
@@ -34,10 +34,24 @@ export const FLAGS = [
|
|
|
34
34
|
{
|
|
35
35
|
names: ["--preset"],
|
|
36
36
|
arg: "<name>",
|
|
37
|
-
description: "
|
|
37
|
+
description: "Read and write an editor's dialect, for example logseq",
|
|
38
38
|
kind: "string",
|
|
39
39
|
key: "presetName"
|
|
40
40
|
},
|
|
41
|
+
{
|
|
42
|
+
names: ["--input-preset"],
|
|
43
|
+
arg: "<name>",
|
|
44
|
+
description: "Read the input in a preset's dialect",
|
|
45
|
+
kind: "string",
|
|
46
|
+
key: "inputPresetName"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
names: ["--output-preset"],
|
|
50
|
+
arg: "<name>",
|
|
51
|
+
description: "Write the output in a preset's dialect",
|
|
52
|
+
kind: "string",
|
|
53
|
+
key: "outputPresetName"
|
|
54
|
+
},
|
|
41
55
|
{
|
|
42
56
|
names: ["--config"],
|
|
43
57
|
arg: "<file>",
|
package/dist/cli/formats.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import type { CliArgs } from "./args.js";
|
|
2
|
+
import type { Format } from "../presets/sides.js";
|
|
2
3
|
export declare function inferFormats(cli: CliArgs): [fromFormat: string | undefined, toFormat: string | undefined];
|
|
3
|
-
export declare function validateFormats(fromFormat: string | undefined, toFormat: string | undefined, normalize: boolean):
|
|
4
|
+
export declare function validateFormats(fromFormat: string | undefined, toFormat: string | undefined, normalize: boolean): [fromFormat: Format, toFormat: Format];
|
package/dist/cli/formats.js
CHANGED
package/dist/cli/presets.d.ts
CHANGED
|
@@ -1,2 +1,13 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
1
|
+
import type { MorgConfig } from "../config.js";
|
|
2
|
+
import type { PresetOptions } from "../presets/sides.js";
|
|
3
|
+
import type { CliArgs } from "./args.js";
|
|
4
|
+
/**
|
|
5
|
+
* Resolves the presets per side, flags over the config (ADR 0006).
|
|
6
|
+
* @param cli The parsed arguments.
|
|
7
|
+
* @param config The parsed `morg.toml`.
|
|
8
|
+
* @param from The input's format.
|
|
9
|
+
* @param to The output's format.
|
|
10
|
+
* @returns The conversion's preset options.
|
|
11
|
+
* @throws {CliError} If the names conflict, are unknown or lack a dialect.
|
|
12
|
+
*/
|
|
13
|
+
export declare function resolvePresets(cli: CliArgs, config: MorgConfig, from: "markdown" | "org", to: "markdown" | "org"): PresetOptions;
|
package/dist/cli/presets.js
CHANGED
|
@@ -1,8 +1,22 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { resolvePresetOptions } from "../conversionOptions.js";
|
|
2
2
|
import { CliError } from "./error.js";
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Resolves the presets per side, flags over the config (ADR 0006).
|
|
5
|
+
* @param cli The parsed arguments.
|
|
6
|
+
* @param config The parsed `morg.toml`.
|
|
7
|
+
* @param from The input's format.
|
|
8
|
+
* @param to The output's format.
|
|
9
|
+
* @returns The conversion's preset options.
|
|
10
|
+
* @throws {CliError} If the names conflict, are unknown or lack a dialect.
|
|
11
|
+
*/
|
|
12
|
+
export function resolvePresets(cli, config, from, to) {
|
|
13
|
+
const flags = {
|
|
14
|
+
...(cli.presetName && { preset: cli.presetName }),
|
|
15
|
+
...(cli.inputPresetName && { inputPreset: cli.inputPresetName }),
|
|
16
|
+
...(cli.outputPresetName && { outputPreset: cli.outputPresetName })
|
|
17
|
+
};
|
|
4
18
|
try {
|
|
5
|
-
return
|
|
19
|
+
return resolvePresetOptions([flags, config], from, to);
|
|
6
20
|
}
|
|
7
21
|
catch (error) {
|
|
8
22
|
throw new CliError(`Error: ${error.message}`);
|
package/dist/cli.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
3
|
import { parseArgs } from "./cli/args.js";
|
|
4
4
|
import { loadConfig } from "./cli/configFile.js";
|
|
5
|
-
import {
|
|
5
|
+
import { resolvePresets } from "./cli/presets.js";
|
|
6
6
|
import { inferFormats, validateFormats } from "./cli/formats.js";
|
|
7
7
|
import { convert } from "./cli/conversion.js";
|
|
8
8
|
import { CliError } from "./cli/error.js";
|
|
@@ -46,11 +46,10 @@ async function main() {
|
|
|
46
46
|
return;
|
|
47
47
|
}
|
|
48
48
|
const config = loadConfig(cli.configPath);
|
|
49
|
-
const
|
|
50
|
-
const
|
|
51
|
-
validateFormats(fromFormat, toFormat, cli.normalize);
|
|
49
|
+
const [fromFormat, toFormat] = validateFormats(...inferFormats(cli), cli.normalize);
|
|
50
|
+
const presets = resolvePresets(cli, config, fromFormat, toFormat);
|
|
52
51
|
const inputContent = await readInput(cli.inputFile);
|
|
53
|
-
const outputContent = convert(inputContent, fromFormat, cli.normalize, cli, config,
|
|
52
|
+
const outputContent = convert(inputContent, fromFormat, cli.normalize, cli, config, presets);
|
|
54
53
|
if (cli.outputFile) {
|
|
55
54
|
fs.writeFileSync(cli.outputFile, outputContent, "utf8");
|
|
56
55
|
}
|
package/dist/config.d.ts
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
import type { MarkdownToOrgOptions, OrgToMarkdownOptions } from "./options.js";
|
|
2
|
+
type PresetKey = "preset" | "inputPreset" | "outputPreset";
|
|
2
3
|
/**
|
|
3
4
|
* Shape of `morg.toml`. Sections mirror the library options objects;
|
|
4
|
-
* `preset` and `silent` mirror their CLI
|
|
5
|
-
* by both directions. Precedence: CLI >
|
|
5
|
+
* `preset`, `inputPreset`, `outputPreset` and `silent` mirror their CLI
|
|
6
|
+
* flags; `orgismKeys` is shared by both directions. Precedence: CLI >
|
|
7
|
+
* config > defaults, per side for the presets.
|
|
6
8
|
*/
|
|
7
9
|
export interface MorgConfig {
|
|
8
10
|
preset?: string;
|
|
11
|
+
inputPreset?: string;
|
|
12
|
+
outputPreset?: string;
|
|
9
13
|
silent?: boolean;
|
|
10
14
|
orgismKeys?: Record<string, string>;
|
|
11
|
-
markdownToOrg?: Omit<MarkdownToOrgOptions,
|
|
12
|
-
orgToMarkdown?: Omit<OrgToMarkdownOptions,
|
|
15
|
+
markdownToOrg?: Omit<MarkdownToOrgOptions, PresetKey | "onWarning">;
|
|
16
|
+
orgToMarkdown?: Omit<OrgToMarkdownOptions, PresetKey | "onWarning" | "orgismKeys">;
|
|
13
17
|
}
|
|
14
18
|
/**
|
|
15
19
|
* Parses a `morg.toml` source string. Unknown top-level keys are
|
|
@@ -18,3 +22,4 @@ export interface MorgConfig {
|
|
|
18
22
|
* @returns The parsed configuration.
|
|
19
23
|
*/
|
|
20
24
|
export declare function parseConfig(source: string): MorgConfig;
|
|
25
|
+
export {};
|
package/dist/config.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { MorgConfig } from "./config.js";
|
|
2
2
|
import type { MarkdownStyleOptions, MarkdownToOrgOptions, OrgToMarkdownOptions, Toggle } from "./options.js";
|
|
3
|
-
import type
|
|
3
|
+
import { type Format, type PresetOptions } from "./presets/sides.js";
|
|
4
4
|
/** Explicitly requested option values; `undefined` leaves it to config. */
|
|
5
5
|
export interface ConversionOverrides {
|
|
6
6
|
taskCheckboxes?: boolean;
|
|
@@ -10,8 +10,7 @@ export interface ConversionOverrides {
|
|
|
10
10
|
markdownStyle?: MarkdownStyleOptions;
|
|
11
11
|
}
|
|
12
12
|
/** Values that apply to both directions. */
|
|
13
|
-
export interface SharedConversionOptions {
|
|
14
|
-
preset?: Preset;
|
|
13
|
+
export interface SharedConversionOptions extends PresetOptions {
|
|
15
14
|
onWarning?: (message: string) => void;
|
|
16
15
|
orgismKeys?: Record<string, string>;
|
|
17
16
|
}
|
|
@@ -30,3 +29,20 @@ export declare function buildConversionOptions(overrides: ConversionOverrides, c
|
|
|
30
29
|
markdownStyle: MarkdownStyleOptions;
|
|
31
30
|
};
|
|
32
31
|
};
|
|
32
|
+
/** Preset names as one layer (CLI, form or config) sets them. */
|
|
33
|
+
export interface PresetNames {
|
|
34
|
+
preset?: string;
|
|
35
|
+
inputPreset?: string;
|
|
36
|
+
outputPreset?: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Resolves the presets of a conversion over layers of names (ADR 0006):
|
|
40
|
+
* per side, the first layer that sets the side or `preset` wins.
|
|
41
|
+
* @param layers Preset names by layer, highest first.
|
|
42
|
+
* @param from The input's format.
|
|
43
|
+
* @param to The output's format; the input's for normalizing.
|
|
44
|
+
* @returns The conversion's preset options.
|
|
45
|
+
* @throws If a layer sets `preset` and another side preset, or a side
|
|
46
|
+
* preset has no dialect for its side's format, or normalizing names two.
|
|
47
|
+
*/
|
|
48
|
+
export declare function resolvePresetOptions(layers: PresetNames[], from: Format, to: Format): PresetOptions;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { resolveSide } from "./presets/sides.js";
|
|
2
|
+
import { createPreset } from "./presets/registry.js";
|
|
1
3
|
/**
|
|
2
4
|
* Layers explicit overrides over the config file, per direction. Used by
|
|
3
5
|
* both adapters (CLI and Web UI) so the documented precedence (CLI or
|
|
@@ -32,3 +34,62 @@ export function buildConversionOptions(overrides, config, shared) {
|
|
|
32
34
|
};
|
|
33
35
|
return { mdToOrgOptions, orgToMdOptions };
|
|
34
36
|
}
|
|
37
|
+
function sideName(layers, key) {
|
|
38
|
+
for (const layer of layers) {
|
|
39
|
+
if (layer[key]) {
|
|
40
|
+
return { name: layer[key], shorthand: false };
|
|
41
|
+
}
|
|
42
|
+
if (layer.preset) {
|
|
43
|
+
return { name: layer.preset, shorthand: true };
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
// across layers the higher one wins; within one, two names conflict
|
|
49
|
+
function rejectConflict(layer) {
|
|
50
|
+
for (const key of ["inputPreset", "outputPreset"]) {
|
|
51
|
+
const side = layer[key];
|
|
52
|
+
if (layer.preset && side && side !== layer.preset) {
|
|
53
|
+
throw new Error(`preset '${layer.preset}' conflicts with ${key} '${side}'`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function sidePreset(name, format, side) {
|
|
58
|
+
const preset = createPreset(name?.name);
|
|
59
|
+
return name?.shorthand
|
|
60
|
+
? resolveSide(undefined, preset, format, side)
|
|
61
|
+
: resolveSide(preset, undefined, format, side);
|
|
62
|
+
}
|
|
63
|
+
// normalizing goes there and back within one dialect (ADR 0006), so
|
|
64
|
+
// both sides must name the same preset, which then sets both ways
|
|
65
|
+
function normalizePresetOptions(layers) {
|
|
66
|
+
const input = sideName(layers, "inputPreset")?.name ?? "vanilla";
|
|
67
|
+
const output = sideName(layers, "outputPreset")?.name ?? "vanilla";
|
|
68
|
+
if (input !== output) {
|
|
69
|
+
throw new Error(`normalize takes one preset; got inputPreset '${input}' and outputPreset '${output}'`);
|
|
70
|
+
}
|
|
71
|
+
const preset = createPreset(input);
|
|
72
|
+
return preset ? { preset } : {};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Resolves the presets of a conversion over layers of names (ADR 0006):
|
|
76
|
+
* per side, the first layer that sets the side or `preset` wins.
|
|
77
|
+
* @param layers Preset names by layer, highest first.
|
|
78
|
+
* @param from The input's format.
|
|
79
|
+
* @param to The output's format; the input's for normalizing.
|
|
80
|
+
* @returns The conversion's preset options.
|
|
81
|
+
* @throws If a layer sets `preset` and another side preset, or a side
|
|
82
|
+
* preset has no dialect for its side's format, or normalizing names two.
|
|
83
|
+
*/
|
|
84
|
+
export function resolvePresetOptions(layers, from, to) {
|
|
85
|
+
layers.forEach(rejectConflict);
|
|
86
|
+
if (from === to) {
|
|
87
|
+
return normalizePresetOptions(layers);
|
|
88
|
+
}
|
|
89
|
+
const input = sidePreset(sideName(layers, "inputPreset"), from, "input");
|
|
90
|
+
const output = sidePreset(sideName(layers, "outputPreset"), to, "output");
|
|
91
|
+
return {
|
|
92
|
+
...(input && { inputPreset: input }),
|
|
93
|
+
...(output && { outputPreset: output })
|
|
94
|
+
};
|
|
95
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { OrgData } from "uniorg";
|
|
2
2
|
import { type Scalar } from "yaml";
|
|
3
|
+
export declare const FRONTMATTER_BLOCK_BEGIN = "#+begin_comment morg_frontmatter";
|
|
3
4
|
export interface FrontmatterNode {
|
|
4
5
|
type: "morg-frontmatter";
|
|
5
6
|
yaml: string;
|
|
@@ -15,6 +16,12 @@ export declare function isFrontmatterNode(node: {
|
|
|
15
16
|
* @param uniorgAst The document.
|
|
16
17
|
*/
|
|
17
18
|
export declare function renderFileHeader(uniorgAst: OrgData): void;
|
|
19
|
+
/**
|
|
20
|
+
* The Frontmatter Block carrying YAML through org (ADR 0005).
|
|
21
|
+
* @param yaml The YAML source, without its `---` fences.
|
|
22
|
+
* @returns The block's org text.
|
|
23
|
+
*/
|
|
24
|
+
export declare function frontmatterBlock(yaml: string): string;
|
|
18
25
|
/**
|
|
19
26
|
* md→org: takes the top-level entries `take` maps to keywords out of
|
|
20
27
|
* the frontmatter; the rest stays verbatim, comments included.
|
|
@@ -4,7 +4,7 @@ import { CST, isMap, isScalar, isSeq, Lexer, parseDocument, visit } from "yaml";
|
|
|
4
4
|
// frontmatter travels verbatim in a comment block marked as morg's
|
|
5
5
|
// (ADR 0005); uniorg keeps neither the block's parameter nor its
|
|
6
6
|
// unescaped value, so the block is rendered as raw text
|
|
7
|
-
const FRONTMATTER_BLOCK_BEGIN = "#+begin_comment morg_frontmatter";
|
|
7
|
+
export const FRONTMATTER_BLOCK_BEGIN = "#+begin_comment morg_frontmatter";
|
|
8
8
|
export function isFrontmatterNode(node) {
|
|
9
9
|
return node.type === "morg-frontmatter";
|
|
10
10
|
}
|
|
@@ -51,7 +51,12 @@ function detachLeadingKeywords(uniorgAst) {
|
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
|
-
|
|
54
|
+
/**
|
|
55
|
+
* The Frontmatter Block carrying YAML through org (ADR 0005).
|
|
56
|
+
* @param yaml The YAML source, without its `---` fences.
|
|
57
|
+
* @returns The block's org text.
|
|
58
|
+
*/
|
|
59
|
+
export function frontmatterBlock(yaml) {
|
|
55
60
|
const body = yaml ? `${escapeBlockLines(yaml)}\n` : "";
|
|
56
61
|
return `${FRONTMATTER_BLOCK_BEGIN}\n${body}#+end_comment\n`;
|
|
57
62
|
}
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import type { Parent } from "unist";
|
|
2
2
|
import { type Node } from "./render.js";
|
|
3
|
+
/**
|
|
4
|
+
* Whether org reads a line as anything but a plain paragraph.
|
|
5
|
+
* @param line The line, without its line break.
|
|
6
|
+
* @returns Whether it reads as org line syntax.
|
|
7
|
+
*/
|
|
8
|
+
export declare function readsAsLineSyntax(line: string): boolean;
|
|
3
9
|
/**
|
|
4
10
|
* md→org: a paragraph line org would read as line syntax (an escaped
|
|
5
11
|
* `1\.` or `\*`, or a lazy continuation line) gets a leading zero-width
|
package/dist/core/lineSyntax.js
CHANGED
|
@@ -9,8 +9,12 @@ import { delimiters, isInline, locate, positionParser, renderInline, tryParse }
|
|
|
9
9
|
// `:` (`1.`, `a)`, `CLOCK:`, `_.`); any other line (one starting with a
|
|
10
10
|
// link, markup or code, say) is text, and skips the parse
|
|
11
11
|
const MAY_BE_LINE_SYNTAX_RE = /^(?:[-+]|\*+)(?:\s|$)|^(?:-{5}|\+-|#(?:\s|$|\+)|[|:]|\[fn:|\\begin\{|%%\()|^[\p{L}\p{N}_]+[.):]/u;
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Whether org reads a line as anything but a plain paragraph.
|
|
14
|
+
* @param line The line, without its line break.
|
|
15
|
+
* @returns Whether it reads as org line syntax.
|
|
16
|
+
*/
|
|
17
|
+
export function readsAsLineSyntax(line) {
|
|
14
18
|
if (!MAY_BE_LINE_SYNTAX_RE.test(line)) {
|
|
15
19
|
return false;
|
|
16
20
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -8,5 +8,5 @@ export { transformMdastToUniorgAst } from "./core/mdastToUniorg/index.js";
|
|
|
8
8
|
export { transformUniorgAstToMdast } from "./core/uniorgToMdast/index.js";
|
|
9
9
|
export { logseq } from "./presets/logseq.js";
|
|
10
10
|
export { obsidian } from "./presets/obsidian.js";
|
|
11
|
-
export type { FragmentConverter, Preset } from "./presets/types.js";
|
|
11
|
+
export type { ConversionContext, FragmentConverter, MarkdownDialect, OrgDialect, Preset } from "./presets/types.js";
|
|
12
12
|
export type { Toggle, MarkdownToOrgOptions, OrgToMarkdownOptions, MarkdownStyleOptions } from "./options.js";
|
package/dist/markdownToOrg.js
CHANGED
|
@@ -15,6 +15,8 @@ import { escapeBackslashCommands } from "./core/backslashCommands.js";
|
|
|
15
15
|
import { escapeTablePipes } from "./core/tablePipes.js";
|
|
16
16
|
import { requireBracedScripts } from "./core/bracedScripts.js";
|
|
17
17
|
import { keepPassthroughSource } from "./core/passthroughSource.js";
|
|
18
|
+
import { conversionContext, fragmentSides, readMarkdownWriteOrg, takeOver } from "./presets/hooks.js";
|
|
19
|
+
import { resolveSides } from "./presets/sides.js";
|
|
18
20
|
import { keyValueEntries } from "./core/keyValueLines.js";
|
|
19
21
|
/**
|
|
20
22
|
* Converts a Markdown string to an Org-mode string.
|
|
@@ -23,19 +25,18 @@ import { keyValueEntries } from "./core/keyValueLines.js";
|
|
|
23
25
|
* @returns The converted Org-mode string.
|
|
24
26
|
*/
|
|
25
27
|
export function convertMarkdownToOrg(markdown, options = {}) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
return convertMarkdownSides(markdown, options, resolveSides(options, "markdown", "org"));
|
|
29
|
+
}
|
|
30
|
+
function convertMarkdownSides(markdown, options, sides) {
|
|
31
|
+
const over = takeOver(sides, "convertMarkdown");
|
|
32
|
+
return over
|
|
28
33
|
? // a fragment's style is the preset's, not one to record
|
|
29
|
-
convertMarkdown(markdown, (fragment, preset) =>
|
|
30
|
-
|
|
31
|
-
preset,
|
|
32
|
-
recordStyle: false
|
|
33
|
-
}))
|
|
34
|
-
: convertMarkdownDocument(markdown, options);
|
|
34
|
+
over.preset.convertMarkdown(markdown, (fragment, preset, carried) => convertMarkdownSides(fragment, { ...options, recordStyle: false }, fragmentSides(sides, over.side, preset, carried)), conversionContext(over, options))
|
|
35
|
+
: convertMarkdownDocument(markdown, options, sides);
|
|
35
36
|
}
|
|
36
|
-
function convertMarkdownDocument(markdown, options) {
|
|
37
|
+
function convertMarkdownDocument(markdown, options, sides) {
|
|
37
38
|
// Phase 1: Parse Markdown to mdast
|
|
38
|
-
const mdast = parseMarkdown(markdown,
|
|
39
|
+
const mdast = parseMarkdown(markdown, sides.input);
|
|
39
40
|
// Phase 2: Generic mdast to uniorg-ast transformation
|
|
40
41
|
let uniorgAst = transformMdastToUniorgDraft(mdast, {
|
|
41
42
|
...(options.preserveMdisms !== undefined && {
|
|
@@ -63,10 +64,9 @@ function convertMarkdownDocument(markdown, options) {
|
|
|
63
64
|
if (options.recordStyle) {
|
|
64
65
|
recordStyleKeyword(uniorgAst, detectMarkdownStyle(mdast, markdown, options.onWarning));
|
|
65
66
|
}
|
|
66
|
-
// Phase 3: Apply dialect preset, if any
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
67
|
+
// Phase 3: Apply dialect preset, if any: read the md dialect, then
|
|
68
|
+
// write the org dialect
|
|
69
|
+
uniorgAst = readMarkdownWriteOrg(uniorgAst, sides);
|
|
70
70
|
// Phase 3b: literal footnote references, paragraph lines org would
|
|
71
71
|
// read as line syntax, literal markers org would read as markup, and
|
|
72
72
|
// markup touching a word character, need a zero-width space escape;
|
|
@@ -101,7 +101,7 @@ function parseMarkdown(markdown, preset) {
|
|
|
101
101
|
.use(remarkMath)
|
|
102
102
|
.parse(markdown);
|
|
103
103
|
keepPassthroughSource(mdast, markdown);
|
|
104
|
-
preset?.
|
|
104
|
+
preset?.markdown?.read?.mdast?.(mdast, markdown);
|
|
105
105
|
return mdast;
|
|
106
106
|
}
|
|
107
107
|
// leads the document, ahead of restored keywords and the frontmatter block
|
package/dist/normalize.js
CHANGED
|
@@ -10,7 +10,8 @@ import { convertOrgToMarkdown } from "./orgToMarkdown.js";
|
|
|
10
10
|
* @returns The canonical-form Markdown string.
|
|
11
11
|
*/
|
|
12
12
|
export function normalizeMarkdown(markdown, options = {}) {
|
|
13
|
-
|
|
13
|
+
const single = onePreset(options);
|
|
14
|
+
return convertOrgToMarkdown(convertMarkdownToOrg(markdown, single), single);
|
|
14
15
|
}
|
|
15
16
|
/**
|
|
16
17
|
* Normalizes an Org string to morg's canonical form: one full round trip
|
|
@@ -20,5 +21,18 @@ export function normalizeMarkdown(markdown, options = {}) {
|
|
|
20
21
|
* @returns The canonical-form Org string.
|
|
21
22
|
*/
|
|
22
23
|
export function normalizeOrg(org, options = {}) {
|
|
23
|
-
|
|
24
|
+
const single = onePreset(options);
|
|
25
|
+
return convertMarkdownToOrg(convertOrgToMarkdown(org, single), single);
|
|
26
|
+
}
|
|
27
|
+
// normalizing canonicalizes within one dialect (ADR 0006), so both
|
|
28
|
+
// sides name the same preset, which then sets both ways of the trip
|
|
29
|
+
function onePreset(options) {
|
|
30
|
+
const { preset, inputPreset, outputPreset, ...rest } = options;
|
|
31
|
+
const input = inputPreset ?? preset;
|
|
32
|
+
const output = outputPreset ?? preset;
|
|
33
|
+
if (input?.name !== output?.name) {
|
|
34
|
+
const name = (key, side) => `${key} '${side?.name ?? "vanilla"}'`;
|
|
35
|
+
throw new Error(`normalize takes one preset; got ${name("inputPreset", input)} and ${name("outputPreset", output)}`);
|
|
36
|
+
}
|
|
37
|
+
return input ? { ...rest, preset: input } : rest;
|
|
24
38
|
}
|
package/dist/options.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { PresetOptions } from "./presets/sides.js";
|
|
2
2
|
/**
|
|
3
3
|
* A feature toggle: `true`/`false` switches everything on/off, a record
|
|
4
4
|
* toggles individual constructs by name. Unlisted keys fall back to the
|
|
@@ -6,7 +6,7 @@ import type { Preset } from "./presets/types.js";
|
|
|
6
6
|
*/
|
|
7
7
|
export type Toggle = boolean | Record<string, boolean>;
|
|
8
8
|
export declare function toggleEnabled(toggle: Toggle | undefined, key: string, defaultValue?: boolean): boolean;
|
|
9
|
-
export interface MarkdownToOrgOptions {
|
|
9
|
+
export interface MarkdownToOrgOptions extends PresetOptions {
|
|
10
10
|
/**
|
|
11
11
|
* Preserve Markdown constructs without a native Org equivalent (e.g.
|
|
12
12
|
* raw HTML as export blocks/snippets, see ADR 0002). Default: `true`.
|
|
@@ -38,8 +38,6 @@ export interface MarkdownToOrgOptions {
|
|
|
38
38
|
orgismKeys?: Record<string, string>;
|
|
39
39
|
/** Called for each construct dropped without an equivalent. */
|
|
40
40
|
onWarning?: (message: string) => void;
|
|
41
|
-
/** Dialect preset applied on top of the generic conversion. */
|
|
42
|
-
preset?: Preset;
|
|
43
41
|
}
|
|
44
42
|
/**
|
|
45
43
|
* Markdown stringifier style knobs (subset of remark-stringify options).
|
|
@@ -67,7 +65,7 @@ export interface MarkdownStyleOptions {
|
|
|
67
65
|
*/
|
|
68
66
|
ruleRepetition?: number;
|
|
69
67
|
}
|
|
70
|
-
export interface OrgToMarkdownOptions {
|
|
68
|
+
export interface OrgToMarkdownOptions extends PresetOptions {
|
|
71
69
|
/**
|
|
72
70
|
* Serialize Org constructs without a native Markdown equivalent as
|
|
73
71
|
* `key:: value` lines and verbatim passthroughs (see ADR 0002).
|
|
@@ -97,6 +95,4 @@ export interface OrgToMarkdownOptions {
|
|
|
97
95
|
orgismKeys?: Record<string, string>;
|
|
98
96
|
/** Called for each construct dropped without an equivalent. */
|
|
99
97
|
onWarning?: (message: string) => void;
|
|
100
|
-
/** Dialect preset extracted before the generic conversion. */
|
|
101
|
-
preset?: Preset;
|
|
102
98
|
}
|