@remigius42/morg 0.4.0 → 0.6.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 (46) hide show
  1. package/README.md +36 -30
  2. package/dist/cli/args.d.ts +2 -0
  3. package/dist/cli/args.js +22 -60
  4. package/dist/cli/error.js +1 -1
  5. package/dist/cli/flags.d.ts +27 -0
  6. package/dist/cli/flags.js +127 -0
  7. package/dist/cli/formats.js +18 -4
  8. package/dist/cli/help.d.ts +1 -0
  9. package/dist/cli/help.js +28 -0
  10. package/dist/cli.js +24 -1
  11. package/dist/conversionOptions.d.ts +2 -2
  12. package/dist/conversionOptions.js +2 -2
  13. package/dist/core/bracedScripts.d.ts +12 -0
  14. package/dist/core/bracedScripts.js +159 -0
  15. package/dist/core/footnoteReferences.d.ts +10 -0
  16. package/dist/core/footnoteReferences.js +24 -0
  17. package/dist/core/lineSyntax.d.ts +12 -0
  18. package/dist/core/lineSyntax.js +206 -0
  19. package/dist/core/markupBoundary.d.ts +12 -0
  20. package/dist/core/markupBoundary.js +195 -0
  21. package/dist/core/mdastToUniorg/blocks.js +1 -1
  22. package/dist/core/mdastToUniorg/index.js +10 -2
  23. package/dist/core/mdastToUniorg/lists.d.ts +1 -1
  24. package/dist/core/mdastToUniorg/lists.js +21 -2
  25. package/dist/core/mdastToUniorg/phrasing.js +145 -12
  26. package/dist/core/orgPath.d.ts +9 -0
  27. package/dist/core/orgPath.js +24 -0
  28. package/dist/core/render.d.ts +33 -0
  29. package/dist/core/render.js +101 -0
  30. package/dist/core/tablePipes.d.ts +8 -0
  31. package/dist/core/tablePipes.js +14 -0
  32. package/dist/core/underscoreBullets.d.ts +10 -0
  33. package/dist/core/underscoreBullets.js +32 -0
  34. package/dist/core/uniorgToMdast/elements.js +1 -1
  35. package/dist/core/uniorgToMdast/lists.js +12 -1
  36. package/dist/core/uniorgToMdast/objects.js +38 -6
  37. package/dist/core/uniorgToMdast/tables.js +12 -6
  38. package/dist/fileNames.d.ts +1 -1
  39. package/dist/fileNames.js +1 -1
  40. package/dist/markdownToOrg.js +19 -1
  41. package/dist/normalize.d.ts +2 -2
  42. package/dist/normalize.js +1 -1
  43. package/dist/options.d.ts +3 -3
  44. package/dist/orgToMarkdown.js +29 -5
  45. package/dist/presets/logseq.js +1 -1
  46. package/package.json +4 -1
@@ -7,6 +7,11 @@ import { uniorgStringify } from "uniorg-stringify";
7
7
  import { visit } from "unist-util-visit";
8
8
  import { transformMdastToUniorgAst } from "./core/mdastToUniorg/index.js";
9
9
  import { detectMarkdownStyle, STYLE_KEYWORD } from "./core/markdownStyle.js";
10
+ import { escapeOrgMarkup } from "./core/markupBoundary.js";
11
+ import { escapeLineSyntax } from "./core/lineSyntax.js";
12
+ import { escapeFootnoteReferences } from "./core/footnoteReferences.js";
13
+ import { escapeTablePipes } from "./core/tablePipes.js";
14
+ import { requireBracedScripts } from "./core/bracedScripts.js";
10
15
  /**
11
16
  * Converts a Markdown string to an Org-mode string.
12
17
  * @param markdown The Markdown string to convert.
@@ -40,7 +45,7 @@ export function convertMarkdownToOrg(markdown, options = {}) {
40
45
  canonical
41
46
  ]));
42
47
  restoreOrgisms(uniorgAst, canonicalKeys);
43
- // Phase 2c: formatting-as-structure — adjacent lists need two blank
48
+ // Phase 2c: formatting-as-structure. Adjacent lists need two blank
44
49
  // lines between them, or org's parser merges them into one list.
45
50
  separateAdjacentLists(uniorgAst);
46
51
  // Phase 2d: record the source's own style markers, so the return trip
@@ -52,6 +57,19 @@ export function convertMarkdownToOrg(markdown, options = {}) {
52
57
  if (options.preset?.applyToUniorg) {
53
58
  uniorgAst = options.preset.applyToUniorg(uniorgAst);
54
59
  }
60
+ // Phase 3b: literal footnote references, paragraph lines org would
61
+ // read as line syntax, literal markers org would read as markup, and
62
+ // markup touching a word character, need a zero-width space escape;
63
+ // a pipe in a table cell an entity
64
+ escapeTablePipes(uniorgAst);
65
+ escapeFootnoteReferences(uniorgAst);
66
+ escapeLineSyntax(uniorgAst);
67
+ escapeOrgMarkup(uniorgAst);
68
+ // Phase 3c: md text has no sub/superscripts; keep org from reading
69
+ // bare underscores and carets as such. Checked on the text as
70
+ // rendered: after the preset, whose text rewrites (wikilink aliases)
71
+ // org parses too, and after the escapes, next to which org reads them
72
+ requireBracedScripts(uniorgAst);
55
73
  // Phase 4: Render uniorg-ast to Org-mode string
56
74
  const processor = unified().use(uniorgStringify);
57
75
  const orgContent = processor.stringify(uniorgAst);
@@ -1,7 +1,7 @@
1
1
  import type { MarkdownToOrgOptions, OrgToMarkdownOptions } from "./options.js";
2
2
  /**
3
3
  * The full option set of both directions: normalization uses the same
4
- * configuration as conversion — Convergence is per-config (see ADR
4
+ * configuration as conversion, because Convergence is per-config (ADR
5
5
  * 0002), so a file must be normalized with the exact config (preset,
6
6
  * style, key names, toggles) it will be converted with.
7
7
  */
@@ -9,7 +9,7 @@ export type NormalizeOptions = MarkdownToOrgOptions & OrgToMarkdownOptions;
9
9
  /**
10
10
  * Normalizes a Markdown string to morg's canonical form: one full round
11
11
  * trip (`md → org → md`), whose output is a fixed point (see ADR 0001).
12
- * This canonicalizes, it does not just re-style — org-isms and md-isms
12
+ * This canonicalizes, it does not just re-style: org-isms and md-isms
13
13
  * are rewritten the same way a conversion would rewrite them.
14
14
  * @param markdown The Markdown string to normalize.
15
15
  * @param options Warning callback and dialect preset.
package/dist/normalize.js CHANGED
@@ -3,7 +3,7 @@ import { convertOrgToMarkdown } from "./orgToMarkdown.js";
3
3
  /**
4
4
  * Normalizes a Markdown string to morg's canonical form: one full round
5
5
  * trip (`md → org → md`), whose output is a fixed point (see ADR 0001).
6
- * This canonicalizes, it does not just re-style — org-isms and md-isms
6
+ * This canonicalizes, it does not just re-style: org-isms and md-isms
7
7
  * are rewritten the same way a conversion would rewrite them.
8
8
  * @param markdown The Markdown string to normalize.
9
9
  * @param options Warning callback and dialect preset.
package/dist/options.d.ts CHANGED
@@ -33,7 +33,7 @@ export interface MarkdownToOrgOptions {
33
33
  /**
34
34
  * Custom names for org-ism `key::` lines, canonical → custom (e.g.
35
35
  * `{ todo: "state" }`). Must match the mapping the file was written
36
- * with — Convergence is per-config (ADR 0002).
36
+ * with, since Convergence is per-config (ADR 0002).
37
37
  */
38
38
  orgismKeys?: Record<string, string>;
39
39
  /** Called for each construct dropped without an equivalent. */
@@ -46,7 +46,7 @@ export interface MarkdownToOrgOptions {
46
46
  * Canonical form is parameterized by these (see ADR 0001): round trips
47
47
  * must use the same style, and files formatted under one style are not
48
48
  * a fixed point under another. Defaults: `-` bullet, `*` emphasis /
49
- * `*` strong (i.e. `**bold**`), backtick fences, `-` rule — prettier's
49
+ * `*` strong (i.e. `**bold**`), backtick fences, `-` rule: prettier's
50
50
  * choices except emphasis (`emphasis: "_"` aligns with prettier).
51
51
  */
52
52
  export interface MarkdownStyleOptions {
@@ -78,7 +78,7 @@ export interface OrgToMarkdownOptions {
78
78
  * Render Org constructs without a Markdown equivalent as raw HTML
79
79
  * (`<u>`, `<sup>`, `<sub>`, `<dl>`) instead of keeping their org markup
80
80
  * verbatim. HTML round-trips as a preserved md-ism (export blocks and
81
- * snippets), not back to native org markup — a one-way door unless
81
+ * snippets), not back to native org markup, a one-way door unless
82
82
  * the return trip enables its inverse, `interpretHtml`.
83
83
  * Default: `false`.
84
84
  */
@@ -1,11 +1,24 @@
1
1
  import { unified } from "unified";
2
- import uniorgParse from "uniorg-parse";
3
2
  import remarkStringify from "remark-stringify";
4
3
  import remarkGfm from "remark-gfm";
5
4
  import remarkFrontmatter from "remark-frontmatter";
6
5
  import remarkMath from "remark-math";
7
6
  import { transformUniorgAstToMdast } from "./core/uniorgToMdast/index.js";
8
7
  import { takeRecordedStyle } from "./core/markdownStyle.js";
8
+ import { unescapeOrgMarkup } from "./core/markupBoundary.js";
9
+ import { unescapeLineSyntax } from "./core/lineSyntax.js";
10
+ import { unescapeFootnoteReferences } from "./core/footnoteReferences.js";
11
+ import { parseOrg } from "./core/bracedScripts.js";
12
+ import { dropUnderscoreBulletGuards, guardUnderscoreBullets } from "./core/underscoreBullets.js";
13
+ // a list item's paragraph after its nested list needs a blank line, or
14
+ // md reads it as a lazy continuation of the nested list's last item
15
+ function separateTextAfterNestedList(left, right, parent) {
16
+ return parent.type === "listItem" &&
17
+ left.type === "list" &&
18
+ right.type === "paragraph"
19
+ ? 1
20
+ : undefined;
21
+ }
9
22
  /**
10
23
  * Converts an Org-mode string to a Markdown string.
11
24
  * @param org The Org-mode string to convert.
@@ -14,10 +27,17 @@ import { takeRecordedStyle } from "./core/markdownStyle.js";
14
27
  */
15
28
  export function convertOrgToMarkdown(org, options = {}) {
16
29
  // Phase 1: Parse Org-mode to uniorg-ast
17
- let uniorgAst = unified().use(uniorgParse).parse(org);
18
- // Phase 1b: a recorded style is morg's own (ADR 0004) — consume it so
30
+ // md text has no scripts, so ^:{} is implied there and consumed here
31
+ // (see markdownToOrg); uniorg misreads `_.` lines (see underscoreBullets)
32
+ let uniorgAst = parseOrg(guardUnderscoreBullets(org));
33
+ dropUnderscoreBulletGuards(uniorgAst);
34
+ // Phase 1b: a recorded style is morg's own (ADR 0004), so consume it so
19
35
  // it does not travel on as frontmatter; explicit options still win
20
36
  const recordedStyle = takeRecordedStyle(uniorgAst);
37
+ // Phase 1c: markdown needs no zero-width space escapes (inverse of md→org)
38
+ unescapeOrgMarkup(uniorgAst);
39
+ unescapeLineSyntax(uniorgAst);
40
+ unescapeFootnoteReferences(uniorgAst);
21
41
  // Phase 2: Extract dialect preset conventions, if any
22
42
  if (options.preset?.extractFromUniorg) {
23
43
  uniorgAst = options.preset.extractFromUniorg(uniorgAst);
@@ -46,11 +66,15 @@ export function convertOrgToMarkdown(org, options = {}) {
46
66
  rule: "-",
47
67
  ...recordedStyle,
48
68
  ...options.markdownStyle,
69
+ join: [separateTextAfterNestedList],
49
70
  handlers: {
50
71
  // key:: value blocks and preset inline passthroughs (e.g.
51
- // wikilinks) are emitted verbatim, unescaped
72
+ // wikilinks) are emitted verbatim, unescaped; only a pipe inside
73
+ // a table cell is escaped, or it would split the cell
52
74
  keyValue: (node) => node.value,
53
- verbatimInline: (node) => node.value
75
+ verbatimInline: (node, _parent, state) => state.stack.includes("tableCell")
76
+ ? node.value.replaceAll("|", "\\|")
77
+ : node.value
54
78
  }
55
79
  })
56
80
  .use(remarkGfm)
@@ -191,7 +191,7 @@ function repairHighlights(uniorgAst) {
191
191
  }
192
192
  // Logseq page and block references: [[page]] stays a wikilink,
193
193
  // [[page][label]] becomes [label]([[page]]), [[((uuid))][label]]
194
- // becomes [label](((uuid))) — emitted unescaped via verbatim-inline
194
+ // becomes [label](((uuid))), emitted unescaped via verbatim-inline
195
195
  const BLOCK_REF_RE = /^\(\(.*\)\)$/;
196
196
  function pageRefValue(label, target) {
197
197
  if (!label) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remigius42/morg",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "Bidirectional Markdown ↔ Org-mode converter with round-trip convergence",
5
5
  "keywords": [
6
6
  "org-mode",
@@ -46,6 +46,7 @@
46
46
  "prepare": "husky",
47
47
  "prepack": "npm run build",
48
48
  "version": "node scripts/release-changelog.mjs && git add CHANGELOG.md",
49
+ "postversion": "printf '\\nNext:\\n git push origin v%s\\n wait for the Release run to pass\\n git push\\n' \"$npm_package_version\"",
49
50
  "build": "tsc -p tsconfig.build.json",
50
51
  "build:web": "vite build web",
51
52
  "dev:web": "vite web",
@@ -61,6 +62,8 @@
61
62
  "lint:fix": "npm run lint:fix:prettier && npm run lint:fix:eslint",
62
63
  "lint:fix:prettier": "prettier --write '**/*'",
63
64
  "lint:fix:eslint": "eslint --fix '**/*.{ts,mts,js}'",
65
+ "perf": "npm run build && node scripts/perf/perf.mjs",
66
+ "perf:record": "node scripts/perf/perf.mjs --record",
64
67
  "test:unit": "vitest",
65
68
  "test:unit:ci": "vitest run --reporter default --coverage",
66
69
  "test:coverage": "vitest run --coverage",