md2org 1.1.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/CHANGELOG.md +71 -0
- package/LICENSE +30 -0
- package/README.md +134 -0
- package/bin/md2org +91 -0
- package/package.json +53 -0
- package/src/md2org.js +82 -0
- package/src/org-escape.js +70 -0
- package/src/org-render.js +500 -0
- package/src/vendor/LICENSE-commonmark +89 -0
- package/src/vendor/commonmark-entities.js +1907 -0
- package/src/vendor/commonmark.js +401 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
## [1.1.0] — 2026-10-03
|
|
5
|
+
|
|
6
|
+
### Added
|
|
7
|
+
- #8 HTML Entities translated for CLI and web as an option
|
|
8
|
+
- LaTeX math passes through unchanged
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- Warnings Footer improved
|
|
12
|
+
- iOS Shortcut deployment target transform.js reduced in size by completely
|
|
13
|
+
removing all HTML entity transformation logic.
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
- Comments in code blocks were not passing through
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
## [1.0.1] — 2026-09-06
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
- Entities md2org adds are now reported in the warnings footer.
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
- `shortcut/transform.js` header comment now shows md2org version.
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
- A bare relative link path such as `[foo](url)` or `[foo](a/b.md)` became a fuzzy Org link, which Org reads as a search for a headline of that name. Export failed. This affected 26 of the 652 CommonMark spec examples.
|
|
29
|
+
- The web page's Copy button did nothing in Chromium browsers over plain http,
|
|
30
|
+
where `navigator.clipboard` is undefined. It now falls back and reports failure.
|
|
31
|
+
- A code span containing `]]` inside a link description ended the link early,
|
|
32
|
+
producing invalid Org. The span now unwraps (monospace lost, characters kept).
|
|
33
|
+
|
|
34
|
+
## [1.0.0] — 2026-09-06
|
|
35
|
+
|
|
36
|
+
First release. Markdown to Org conversion at the command line, in the browser, and
|
|
37
|
+
via an iOS Shortcut.
|
|
38
|
+
|
|
39
|
+
### Added
|
|
40
|
+
- `md2org --version` / `-v`.
|
|
41
|
+
- `CHANGELOG.md`
|
|
42
|
+
- CI, and a CLI test tier.
|
|
43
|
+
|
|
44
|
+
### Changed
|
|
45
|
+
- DESIGN.md, MAPPING.md and README.md reconciled with the code: the escaping
|
|
46
|
+
table now lists every escape the program performs.
|
|
47
|
+
- specs in test directory moved to `specs/`
|
|
48
|
+
|
|
49
|
+
### Fixed
|
|
50
|
+
- The CLI ignored unknown options and then waited on stdin, so a typo hung.
|
|
51
|
+
Usage errors now exit 2.
|
|
52
|
+
- The CLI wrote no trailing newline, leaving a malformed text file.
|
|
53
|
+
- `npm test` needed esbuild and so could not run where esbuild has no binary. The
|
|
54
|
+
derived-copy check now compares behaviour instead of rebuilding; CI still
|
|
55
|
+
asserts byte-identity.
|
|
56
|
+
- `shortcut/transform.js` size reduced by removing dead code.
|
|
57
|
+
|
|
58
|
+
### Removed
|
|
59
|
+
- Dead code in `src/org-render.js` left from the pre-fork design
|
|
60
|
+
|
|
61
|
+
## Guidelines for updating this document
|
|
62
|
+
Only user facing changes to this project are documented here. Entries are very brief summaries.
|
|
63
|
+
|
|
64
|
+
For a converter, "the public API" is the mapping in [MAPPING.md](MAPPING.md) as much as it is the `md2org(string)` function. A change to what a given Markdown construct becomes in Org is a change users will notice in their files, so it is treated as breaking unless the previous output was invalid Org or plainly wrong.
|
|
65
|
+
|
|
66
|
+
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
|
|
67
|
+
this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
68
|
+
|
|
69
|
+
[1.1.0]: https://github.com/nicholascarroll/md2org.js/releases/tag/1.1.0
|
|
70
|
+
[1.0.1]: https://github.com/nicholascarroll/md2org.js/releases/tag/1.0.1
|
|
71
|
+
[1.0.0]: https://github.com/nicholascarroll/md2org.js/releases/tag/1.0.0
|
package/LICENSE
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nicholas Carroll
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Third-party code
|
|
26
|
+
|
|
27
|
+
This package bundles a modified copy of commonmark.js in `src/vendor/`,
|
|
28
|
+
Copyright (c) 2014, John MacFarlane, used under the BSD 2-Clause License.
|
|
29
|
+
Its full licence text is in `src/vendor/LICENSE-commonmark`, which is
|
|
30
|
+
distributed with this package.
|
package/README.md
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# md2org.js
|
|
2
|
+
|
|
3
|
+
md2org.js converts Markdown markup to Org markup.
|
|
4
|
+
|
|
5
|
+
- at the command line as a Unix filter 🙂
|
|
6
|
+
- on your phone via an iOS Shortcut 😎
|
|
7
|
+
- [in your browser](https://nicholascarroll.github.io/md2org.js/) with nothing uploaded anywhere 🥷
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
Source text that is not Markdown markup passes through unchanged. The conversion is one-way only.
|
|
11
|
+
|
|
12
|
+
## Use
|
|
13
|
+
|
|
14
|
+
### 1. Command line
|
|
15
|
+
|
|
16
|
+
A Unix filter. Needs [Node.js](https://nodejs.org).
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
npm install -g md2org
|
|
20
|
+
```
|
|
21
|
+
Usage:
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
md2org notes.md > notes.org
|
|
25
|
+
cat notes.md | md2org
|
|
26
|
+
md2org < notes.md
|
|
27
|
+
md2org --help
|
|
28
|
+
md2org --version
|
|
29
|
+
md2org -e # translate HTML entities
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Exit status is 0 on success, 1 if the input file can't be read, 2 for a usage
|
|
33
|
+
error.
|
|
34
|
+
|
|
35
|
+
### 2. iOS Shortcut
|
|
36
|
+
|
|
37
|
+
Copy Markdown, tap the md2org button in the Control Centre and your clipboard content converts to Org. See **[shortcut/README.md](shortcut/README.md)** for the two-minute setup, or get the [drop-in Shortcut definition](shortcut/)
|
|
38
|
+
|
|
39
|
+
### 3. In the browser
|
|
40
|
+
|
|
41
|
+
Open the [live page](https://nicholascarroll.github.io/md2org.js/). Paste Markdown, copy Org. Conversion happens entirely on your device; it sends nothing over the network.
|
|
42
|
+
|
|
43
|
+
## Warnings Footer
|
|
44
|
+
|
|
45
|
+
md2org will write any warnings to the tail of the Org mode output as comments. For example, if your source file had a line starting with `** foo`, you will see this at the bottom of your output.
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
# md2org warnings:
|
|
49
|
+
# heading: line 2
|
|
50
|
+
```
|
|
51
|
+
This is warning you that line 2 of your output was not a heading in the source but has become a heading in the output.
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
## Known lossy conversions
|
|
55
|
+
|
|
56
|
+
- Intraword emphasis `a**b**c` becomes `a*b*c`.
|
|
57
|
+
- Link titles: `[t](/u "title")` loses the title.
|
|
58
|
+
- `+ item` becomes `- item` and `_em_` becomes `/em/`.
|
|
59
|
+
- Raw HTML. Blocks become `#+BEGIN_EXPORT html`
|
|
60
|
+
- inline HTML becomes `@@html:…@@`.
|
|
61
|
+
- Character references pass through as you wrote them. `—` and `ℋ`, and numeric ones such as `A` and `A`, all reach the Org file untouched. For CLI and web deployment targets you can enable translation to actual Unicode codepoints.
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
## Correctness
|
|
65
|
+
|
|
66
|
+
md2org.js forks and modifies the reference [CommonMark](https://spec.commonmark.org/0.31.2/) parser, and passes 652 of the specification's 652 test cases when the HTML Entities option is enabled.
|
|
67
|
+
|
|
68
|
+
Tables and footnotes are [GitHub Flavoured Markdown](specs/gfm-spec-0.29.txt) extensions that CommonMark doesn't define.
|
|
69
|
+
|
|
70
|
+
If you encounter any nonconformities please log an issue or a PR even.
|
|
71
|
+
|
|
72
|
+
### Edge cases
|
|
73
|
+
|
|
74
|
+
The cases below are all tested:
|
|
75
|
+
|
|
76
|
+
| Markdown | Naive output would mean | What md2org emits |
|
|
77
|
+
|---|---|---|
|
|
78
|
+
| a language-tagged fence containing `#+END_SRC` | block ends early, rest of the file is body text | `,#+END_SRC` (comma-quoted) |
|
|
79
|
+
| `` `a=b` `` | verbatim ending at the first `=` | falls back to `~a=b~` |
|
|
80
|
+
| `]` in a link path | the link ends early | percent-encoded by the parser |
|
|
81
|
+
| `]]` in a link description | the link ends early | passed through and warned |
|
|
82
|
+
| `\|` in a table cell | table alignment thrown out | passed through and warned |
|
|
83
|
+
|
|
84
|
+
### Testing
|
|
85
|
+
|
|
86
|
+
```sh
|
|
87
|
+
npm test
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
`npm test` runs ten tiers, in order:
|
|
91
|
+
1. **`test/spec.js`** is the behavioural contract.
|
|
92
|
+
2. **`test/cli.js`** is the CLI contract, tests `bin/md2org` in the shell.
|
|
93
|
+
3. **`test/conformance.js`** is CommonMark conformance, 652/652, measured against
|
|
94
|
+
the spec's own Markdown/HTML pairs run as data with HTML entities option
|
|
95
|
+
enabled.
|
|
96
|
+
4. **`test/gfm.js`** is GFM table conformance, 8/8, against the GFM spec's
|
|
97
|
+
examples.
|
|
98
|
+
5. **`test/document.js`** is document-scale inputs.
|
|
99
|
+
6. **`test/invariant.js`** verifies invariant 1: every non-markup character in
|
|
100
|
+
the source appears in the output.
|
|
101
|
+
7. **`test/warnings.js`** verifies invariant 4, the Warnings Footer.
|
|
102
|
+
8. **`test/fuzz.js`** generates 5,000 documents from a fragment corpus and
|
|
103
|
+
asserts that every output is structurally valid Org. `npm run fuzz` runs
|
|
104
|
+
100,000.
|
|
105
|
+
9. **`test/size.js`** asserts `shortcut/transform.js` still fits into the
|
|
106
|
+
Actions app. See [MAPPING.md](MAPPING.md#bytes-budget).
|
|
107
|
+
10. **`test/emacs.js`** tests using Emacs. `npm run test:emacs`.
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
Specifications are in [`specs/`](specs/README.md).
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
## Building
|
|
114
|
+
|
|
115
|
+
`src/` is several modules; `build.js` concatenates them into the browser and
|
|
116
|
+
Shortcut copies. Run it after editing anything in `src/`:
|
|
117
|
+
|
|
118
|
+
```sh
|
|
119
|
+
node build.js
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
The CommonMark parser in `src/vendor/` is generated and committed. You only need
|
|
123
|
+
`node tools/build-vendor.js` if you want to regenerate it.
|
|
124
|
+
|
|
125
|
+
### Size limit on the iOS Shortcut
|
|
126
|
+
|
|
127
|
+
The iOS Shortcut is extremely tightly constrained by a size limit for the md2org core code `shortcut/transform.js`: when pasting its contents into the Actions app's JavaScript code field, the Shortcuts editor crashes. There is a limit on lines and on bytes. For that reason the Shortcut copy is minified and new features are costed in bytes in [MAPPING.md](MAPPING.md).
|
|
128
|
+
|
|
129
|
+
I've tested on iOS 26.3.1(a) and 26.6.1 (iPhone 14). If you hit this issue on your device, please open an issue with your iOS version. Might not be fixable 🙁.
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
## License
|
|
133
|
+
|
|
134
|
+
MIT
|
package/bin/md2org
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/*
|
|
3
|
+
* md2org CLI: a Unix filter that reads Markdown and writes Org.
|
|
4
|
+
*
|
|
5
|
+
* cat notes.md | md2org > notes.org
|
|
6
|
+
* md2org notes.md > notes.org
|
|
7
|
+
* md2org < notes.md
|
|
8
|
+
*
|
|
9
|
+
* Uses the same core as the web page and the iOS Shortcut.
|
|
10
|
+
*/
|
|
11
|
+
"use strict";
|
|
12
|
+
|
|
13
|
+
const fs = require("fs");
|
|
14
|
+
const path = require("path");
|
|
15
|
+
const md2org = require(path.join(__dirname, "..", "src", "md2org.js"));
|
|
16
|
+
|
|
17
|
+
const args = process.argv.slice(2);
|
|
18
|
+
|
|
19
|
+
const USAGE =
|
|
20
|
+
"md2org — convert Markdown to Org mode\n\n" +
|
|
21
|
+
"Usage:\n" +
|
|
22
|
+
" md2org [file] convert file (or stdin) to stdout\n" +
|
|
23
|
+
" cat f.md | md2org read from a pipe\n\n" +
|
|
24
|
+
"Options:\n" +
|
|
25
|
+
" -e, --entities decode HTML entities and numeric character references\n" +
|
|
26
|
+
" to the characters they name (— and — to —)\n" +
|
|
27
|
+
" -h, --help show this help\n" +
|
|
28
|
+
" -v, --version show the version\n";
|
|
29
|
+
|
|
30
|
+
if (args.includes("-h") || args.includes("--help")) {
|
|
31
|
+
process.stdout.write(USAGE);
|
|
32
|
+
process.exit(0);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (args.includes("-v") || args.includes("--version")) {
|
|
36
|
+
process.stdout.write(require("../package.json").version + "\n");
|
|
37
|
+
process.exit(0);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Character-reference decoding, off by default (DESIGN.md, Character references
|
|
41
|
+
// and entities).
|
|
42
|
+
const entities = args.includes("-e") || args.includes("--entities");
|
|
43
|
+
|
|
44
|
+
// Reject unknown options rather than falling through to wait on stdin.
|
|
45
|
+
const badOpt = args.find((a) => a.startsWith("-") && a !== "-" &&
|
|
46
|
+
a !== "-e" && a !== "--entities");
|
|
47
|
+
if (badOpt) {
|
|
48
|
+
process.stderr.write("md2org: unknown option " + badOpt + "\n\n" + USAGE);
|
|
49
|
+
process.exit(2);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const fileArgs = args.filter((a) => !a.startsWith("-"));
|
|
53
|
+
if (fileArgs.length > 1) {
|
|
54
|
+
process.stderr.write("md2org: expected at most one file, got " +
|
|
55
|
+
fileArgs.length + "\n\n" + USAGE);
|
|
56
|
+
process.exit(2);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/*
|
|
60
|
+
* Terminates the output with a newline, as a POSIX text file requires. The core
|
|
61
|
+
* omits it because the browser and Shortcut place the result in a text box or on
|
|
62
|
+
* the clipboard.
|
|
63
|
+
*/
|
|
64
|
+
function convert(input) {
|
|
65
|
+
const out = entities ? md2org.withEntities(input) : md2org(input);
|
|
66
|
+
process.stdout.write(out === "" ? out : out.replace(/\n?$/, "\n"));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const fileArg = fileArgs[0];
|
|
70
|
+
|
|
71
|
+
if (fileArg) {
|
|
72
|
+
let input;
|
|
73
|
+
try {
|
|
74
|
+
input = fs.readFileSync(fileArg, "utf8");
|
|
75
|
+
} catch (e) {
|
|
76
|
+
process.stderr.write("md2org: cannot read " + fileArg + ": " + e.message + "\n");
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
convert(input);
|
|
80
|
+
} else {
|
|
81
|
+
// Reading stdin. A terminal means no pipe was given, so report it rather than
|
|
82
|
+
// block.
|
|
83
|
+
if (process.stdin.isTTY) {
|
|
84
|
+
process.stderr.write("md2org: no input — give a file or pipe stdin\n\n" + USAGE);
|
|
85
|
+
process.exit(2);
|
|
86
|
+
}
|
|
87
|
+
const chunks = [];
|
|
88
|
+
process.stdin.on("data", (c) => chunks.push(c));
|
|
89
|
+
process.stdin.on("end", () => convert(Buffer.concat(chunks).toString("utf8")));
|
|
90
|
+
process.stdin.resume();
|
|
91
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "md2org",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "One-way Markdown to Org mode converter. CLI, browser and iOS Shortcut.",
|
|
5
|
+
"bin": {
|
|
6
|
+
"md2org": "bin/md2org"
|
|
7
|
+
},
|
|
8
|
+
"main": "src/md2org.js",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "node test/spec.js && node test/cli.js && node test/conformance.js && node test/gfm.js && node test/document.js && node test/invariant.js && node test/warnings.js && node test/fuzz.js 5000 && node test/size.js && node test/emacs.js",
|
|
11
|
+
"build": "node build.js",
|
|
12
|
+
"vendor": "node tools/build-vendor.js",
|
|
13
|
+
"fuzz": "node test/fuzz.js 100000",
|
|
14
|
+
"prepublishOnly": "npm test",
|
|
15
|
+
"test:emacs": "node test/emacs.js",
|
|
16
|
+
"compare": "node tools/compare.js"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"src",
|
|
20
|
+
"bin",
|
|
21
|
+
"README.md",
|
|
22
|
+
"CHANGELOG.md",
|
|
23
|
+
"LICENSE"
|
|
24
|
+
],
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"keywords": [
|
|
27
|
+
"markdown",
|
|
28
|
+
"org",
|
|
29
|
+
"org-mode",
|
|
30
|
+
"emacs",
|
|
31
|
+
"converter",
|
|
32
|
+
"commonmark",
|
|
33
|
+
"gfm",
|
|
34
|
+
"cli",
|
|
35
|
+
"ios",
|
|
36
|
+
"shortcuts"
|
|
37
|
+
],
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"esbuild": "^0.28.2"
|
|
40
|
+
},
|
|
41
|
+
"homepage": "https://nicholascarroll.github.io/md2org.js/",
|
|
42
|
+
"repository": {
|
|
43
|
+
"type": "git",
|
|
44
|
+
"url": "git+https://github.com/nicholascarroll/md2org.js.git"
|
|
45
|
+
},
|
|
46
|
+
"bugs": {
|
|
47
|
+
"url": "https://github.com/nicholascarroll/md2org.js/issues"
|
|
48
|
+
},
|
|
49
|
+
"author": "Nicholas Carroll",
|
|
50
|
+
"engines": {
|
|
51
|
+
"node": ">=18"
|
|
52
|
+
}
|
|
53
|
+
}
|
package/src/md2org.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* md2org: one-way Markdown to Org mode converter.
|
|
3
|
+
*
|
|
4
|
+
* A pure function, string in and string out, with no I/O or platform APIs. The
|
|
5
|
+
* CLI, the web page and the iOS Shortcut run this same code, copied into place by
|
|
6
|
+
* build.js.
|
|
7
|
+
*
|
|
8
|
+
* Structure follows CommonMark's two-phase parsing strategy (spec Appendix):
|
|
9
|
+
*
|
|
10
|
+
* src/vendor/commonmark.js parse (forked commonmark.js, BSD-2-Clause)
|
|
11
|
+
* src/org-render.js AST to Org
|
|
12
|
+
* src/org-escape.js escaping
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
/*
|
|
17
|
+
* Node/CommonJS bootstrap. Outside the core markers, so build.js drops it from
|
|
18
|
+
* the generated bundles, which concatenate the modules into one scope.
|
|
19
|
+
*/
|
|
20
|
+
if (typeof __cmark === "undefined") { var __cmark = require("./vendor/commonmark.js"); }
|
|
21
|
+
if (typeof renderOrg === "undefined") { var renderOrg = require("./org-render.js"); }
|
|
22
|
+
if (typeof codeSpan === "undefined") {
|
|
23
|
+
var __esc = require("./org-escape.js");
|
|
24
|
+
var codeSpan = __esc.codeSpan,
|
|
25
|
+
escapeLinkPath = __esc.escapeLinkPath,
|
|
26
|
+
protectBlockBody = __esc.protectBlockBody,
|
|
27
|
+
escapeCell = __esc.escapeCell;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/* --8<-- core start */
|
|
31
|
+
|
|
32
|
+
function md2org(src) {
|
|
33
|
+
if (typeof src !== "string") src = String(src == null ? "" : src);
|
|
34
|
+
if (src === "") return "";
|
|
35
|
+
renderOrg.warn = {};
|
|
36
|
+
|
|
37
|
+
var parser = new __cmark.Parser({ sourcepos: true });
|
|
38
|
+
|
|
39
|
+
var escapes = {
|
|
40
|
+
codeSpan: codeSpan,
|
|
41
|
+
escapeLinkPath: escapeLinkPath,
|
|
42
|
+
protectBlockBody: protectBlockBody,
|
|
43
|
+
escapeCell: escapeCell
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
var out = renderOrg(parser.parse(src), escapes);
|
|
47
|
+
var k = Object.keys(renderOrg.warn);
|
|
48
|
+
return k.length ? out + "\n\n# md2org warnings:\n" +
|
|
49
|
+
k.map(function (x) { return "# " + x + ": line " + renderOrg.warn[x].join(", "); }).join("\n") : out;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/* --8<-- core end */
|
|
53
|
+
|
|
54
|
+
/*
|
|
55
|
+
* Character-reference decoding, off by default (DESIGN.md, Character references
|
|
56
|
+
* and entities). Used by the CLI's -e option.
|
|
57
|
+
*
|
|
58
|
+
* Outside the core markers, so it costs the Shortcut nothing. It swaps in the
|
|
59
|
+
* parser built with upstream's entity table, which is loaded on first use. This
|
|
60
|
+
* works because the core reads __cmark at call time.
|
|
61
|
+
*/
|
|
62
|
+
var __cmarkEntities = null;
|
|
63
|
+
|
|
64
|
+
md2org.withEntities = function (src) {
|
|
65
|
+
if (!__cmarkEntities) __cmarkEntities = require("./vendor/commonmark-entities.js");
|
|
66
|
+
var saved = __cmark;
|
|
67
|
+
__cmark = __cmarkEntities;
|
|
68
|
+
try {
|
|
69
|
+
return md2org(src);
|
|
70
|
+
} finally {
|
|
71
|
+
__cmark = saved;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// Universal export: CommonJS (Node/CLI), bundlers, browser global.
|
|
76
|
+
if (typeof module !== "undefined" && module.exports) {
|
|
77
|
+
module.exports = md2org;
|
|
78
|
+
module.exports.md2org = md2org;
|
|
79
|
+
}
|
|
80
|
+
if (typeof window !== "undefined") {
|
|
81
|
+
window.md2org = md2org;
|
|
82
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* All of md2org's escaping (DESIGN.md, Escaping).
|
|
3
|
+
*
|
|
4
|
+
* Literal text is not escaped. Each function below either applies Org's own
|
|
5
|
+
* quoting mechanism or protects markup that md2org generates.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/* --8<-- core start */
|
|
9
|
+
|
|
10
|
+
/*
|
|
11
|
+
* Block bodies (§Lesser Elements). A line beginning with "*" or "#+" inside a
|
|
12
|
+
* block is comma-quoted, or Org reads it as a headline or a block delimiter. Org
|
|
13
|
+
* removes the comma on read.
|
|
14
|
+
*
|
|
15
|
+
* Mirrors org-escape-code-in-string: quote only when the line, after any existing
|
|
16
|
+
* commas, begins with "*" or "#+". Org strips a comma only from such lines, so
|
|
17
|
+
* quoting ",foo" would not be reversed.
|
|
18
|
+
*/
|
|
19
|
+
function protectBlockBody(text) {
|
|
20
|
+
return text.split("\n").map(function (line) {
|
|
21
|
+
return line.replace(/^([ \t]*)(,*)(\*|#\+)/, "$1$2,$3");
|
|
22
|
+
}).join("\n");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/*
|
|
26
|
+
* Code spans (§Text Markup). CONTENTS may not contain the MARKER, so a span
|
|
27
|
+
* holding "=" uses "~" and vice versa. A span holding both is returned as plain
|
|
28
|
+
* text, since entities are not expanded inside verbatim or code.
|
|
29
|
+
*/
|
|
30
|
+
function codeSpan(text) {
|
|
31
|
+
if (text.indexOf("=") === -1) return "=" + text + "=";
|
|
32
|
+
if (text.indexOf("~") === -1) return "~" + text + "~";
|
|
33
|
+
return text;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/*
|
|
37
|
+
* Table cells (§Table). A bare "|" ends a cell and Org has no escape for it.
|
|
38
|
+
* The parser consumes the backslash of GFM's "\|"; this restores it, so the
|
|
39
|
+
* output shows what the author typed. The renderer warns.
|
|
40
|
+
*/
|
|
41
|
+
function escapeCell(text) {
|
|
42
|
+
return String(text).replace(/\|/g, "\\|");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/*
|
|
46
|
+
* Link paths (§Regular Link). "[", "]" and "\" in PATHREG are backslash-escaped,
|
|
47
|
+
* the only escape Org provides there.
|
|
48
|
+
*
|
|
49
|
+
* A bare path such as "url" or "a/b.md" would be a FUZZY link (a headline
|
|
50
|
+
* search), and "(foo)" a CODEREF, so any path without an explicit type gets
|
|
51
|
+
* "file:", Org's type for a relative path. Unchanged: a path with its own
|
|
52
|
+
* LINKTYPE ("http:", "mailto:", "id:"), a FILENAME starting with "/", "~", "./"
|
|
53
|
+
* or "../", and a "#" CUSTOM-ID.
|
|
54
|
+
*/
|
|
55
|
+
function linkType(path) {
|
|
56
|
+
return /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(path) // LINKTYPE:...
|
|
57
|
+
|| /^[/~#]/.test(path) // FILENAME or #CUSTOM-ID
|
|
58
|
+
|| /^\.\.?\//.test(path); // ./ or ../ FILENAME
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function escapeLinkPath(path) {
|
|
62
|
+
var p = String(path);
|
|
63
|
+
if (p && !linkType(p)) p = "file:" + p;
|
|
64
|
+
return p.replace(/([\[\]\\])/g, "\\$1");
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* --8<-- core end */
|
|
68
|
+
|
|
69
|
+
module.exports = { protectBlockBody: protectBlockBody, codeSpan: codeSpan,
|
|
70
|
+
escapeLinkPath: escapeLinkPath, escapeCell: escapeCell };
|