markdown-patch 0.1.1 → 0.1.3
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 +111 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +108 -0
- package/dist/constants.d.ts +7 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +15 -0
- package/dist/debug.d.ts +3 -0
- package/dist/debug.d.ts.map +1 -0
- package/dist/debug.js +50 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/map.d.ts +3 -0
- package/dist/map.d.ts.map +1 -0
- package/dist/map.js +144 -0
- package/dist/patch.d.ts +19 -0
- package/dist/patch.d.ts.map +1 -0
- package/dist/patch.js +189 -0
- package/dist/tests/map.test.d.ts +2 -0
- package/dist/tests/map.test.d.ts.map +1 -0
- package/dist/tests/map.test.js +202 -0
- package/dist/tests/patch.test.d.ts +2 -0
- package/dist/tests/patch.test.d.ts.map +1 -0
- package/dist/tests/patch.test.js +223 -0
- package/dist/types.d.ts +95 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/package.json +23 -3
- package/.tool-versions +0 -1
- package/.vscode/launch.json +0 -21
- package/document.md +0 -11
- package/document.mdpatch.json +0 -8
- package/jest.config.ts +0 -9
- package/src/cli.ts +0 -88
- package/src/constants.ts +0 -11
- package/src/debug.ts +0 -75
- package/src/index.ts +0 -9
- package/src/map.ts +0 -200
- package/src/patch.ts +0 -326
- package/src/tests/map.test.ts +0 -212
- package/src/tests/patch.test.ts +0 -297
- package/src/tests/sample.md +0 -81
- package/src/tests/sample.patch.block.append.md +0 -82
- package/src/tests/sample.patch.block.prepend.md +0 -82
- package/src/tests/sample.patch.block.replace.md +0 -81
- package/src/tests/sample.patch.block.targetBlockTypeBehavior.table.append.md +0 -82
- package/src/tests/sample.patch.block.targetBlockTypeBehavior.table.prepend.md +0 -82
- package/src/tests/sample.patch.block.targetBlockTypeBehavior.table.replace.md +0 -77
- package/src/tests/sample.patch.heading.append.md +0 -82
- package/src/tests/sample.patch.heading.document.append.md +0 -82
- package/src/tests/sample.patch.heading.document.prepend.md +0 -82
- package/src/tests/sample.patch.heading.prepend.md +0 -82
- package/src/tests/sample.patch.heading.replace.md +0 -81
- package/src/tests/sample.patch.heading.trimTargetWhitespace.append.md +0 -80
- package/src/tests/sample.patch.heading.trimTargetWhitespace.prepend.md +0 -80
- package/src/types.ts +0 -155
- package/tsconfig.json +0 -18
package/README.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Markdown Patch
|
|
2
|
+
|
|
3
|
+
Have you ever needed to set up a script for modifying a markdown document and found yourself using arcane tools like `sed` before giving up entirely?
|
|
4
|
+
|
|
5
|
+
Markdown Patch (`mdpatch`) is aware of the structure of your markdown document and makes it possible for you to easily inserting content relative to parts of that structure like headings and block references.
|
|
6
|
+
|
|
7
|
+
## Quickstart
|
|
8
|
+
|
|
9
|
+
You can install the package via `npm`:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install markdown-patch
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
And if you were to create a document named `document.md` with the following content:
|
|
16
|
+
|
|
17
|
+
```markdown
|
|
18
|
+
# Noise Floor
|
|
19
|
+
|
|
20
|
+
- Some content
|
|
21
|
+
|
|
22
|
+
# Discoveries
|
|
23
|
+
|
|
24
|
+
# Events
|
|
25
|
+
|
|
26
|
+
- Checked out of my hotel
|
|
27
|
+
- Caught the flight home
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Then you can use the `patch` or `apply` subcommands to alter the document. For example, the following will add a new heading below the heading "Discoveries":
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
mdpatch patch append heading Discoveries ./document.md
|
|
35
|
+
|
|
36
|
+
## My discovery
|
|
37
|
+
I discovered a thing
|
|
38
|
+
|
|
39
|
+
<Ctrl+D>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Your final document will then look like:
|
|
43
|
+
|
|
44
|
+
```markdown
|
|
45
|
+
# Noise Floor
|
|
46
|
+
|
|
47
|
+
- Some content
|
|
48
|
+
|
|
49
|
+
# Discoveries
|
|
50
|
+
|
|
51
|
+
## My discovery
|
|
52
|
+
I discovered a thing
|
|
53
|
+
|
|
54
|
+
# Events
|
|
55
|
+
|
|
56
|
+
- Checked out of my hotel
|
|
57
|
+
- Caught the flight home
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
See `--help` for more insight into what commands are available.
|
|
62
|
+
|
|
63
|
+
## Use as a library
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
import {PatchInstruction, applyPatch} from "markdown-patch"
|
|
67
|
+
|
|
68
|
+
const myDocument = `
|
|
69
|
+
# Noise Floor
|
|
70
|
+
|
|
71
|
+
- Some content
|
|
72
|
+
|
|
73
|
+
# Discoveries
|
|
74
|
+
|
|
75
|
+
# Events
|
|
76
|
+
|
|
77
|
+
- Checked out of my hotel
|
|
78
|
+
- Caught the flight home
|
|
79
|
+
|
|
80
|
+
`
|
|
81
|
+
|
|
82
|
+
const instruction: PatchInstruction {
|
|
83
|
+
operation: "append",
|
|
84
|
+
targetType: "heading",
|
|
85
|
+
target: "Discoveries",
|
|
86
|
+
content: "\n## My discovery\nI discovered a thing\n",
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
console.log(
|
|
90
|
+
applyPatch(myDocument, instruction)
|
|
91
|
+
)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
and you'll see the output:
|
|
95
|
+
|
|
96
|
+
```markdown
|
|
97
|
+
# Noise Floor
|
|
98
|
+
|
|
99
|
+
- Some content
|
|
100
|
+
|
|
101
|
+
# Discoveries
|
|
102
|
+
|
|
103
|
+
## My discovery
|
|
104
|
+
I discovered a thing
|
|
105
|
+
|
|
106
|
+
# Events
|
|
107
|
+
|
|
108
|
+
- Checked out of my hotel
|
|
109
|
+
- Caught the flight home
|
|
110
|
+
|
|
111
|
+
```
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import fs from "fs/promises";
|
|
4
|
+
import { getDocumentMap } from "./map.js";
|
|
5
|
+
import { printMap } from "./debug.js";
|
|
6
|
+
import { applyPatch } from "./patch.js";
|
|
7
|
+
import packageJson from "../package.json" assert { type: "json" };
|
|
8
|
+
async function readStdin() {
|
|
9
|
+
return new Promise((resolve, reject) => {
|
|
10
|
+
let data = "";
|
|
11
|
+
process.stdin.on("data", (chunk) => (data += chunk));
|
|
12
|
+
process.stdin.on("end", () => resolve(data));
|
|
13
|
+
process.stdin.on("error", reject);
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
const program = new Command();
|
|
17
|
+
// Configure the CLI
|
|
18
|
+
program
|
|
19
|
+
.name(Object.keys(packageJson.bin)[0])
|
|
20
|
+
.description(packageJson.description)
|
|
21
|
+
.version(packageJson.version);
|
|
22
|
+
program
|
|
23
|
+
.command("print-map")
|
|
24
|
+
.argument("<path>", "filepath to show identified patchable paths for")
|
|
25
|
+
.argument("[regex]", "limit displayed matches to those matching the supplied regular expression")
|
|
26
|
+
.action(async (path, regex) => {
|
|
27
|
+
const document = await fs.readFile(path, "utf-8");
|
|
28
|
+
const documentMap = getDocumentMap(document);
|
|
29
|
+
printMap(document, documentMap, regex ? new RegExp(regex) : undefined);
|
|
30
|
+
});
|
|
31
|
+
program
|
|
32
|
+
.command("patch")
|
|
33
|
+
.option("-i, --input <input>", "Path to content to insert; by default reads from stdin.")
|
|
34
|
+
.option("-o, --output <output>", "Path to write output to; use '-' for stdout. Defaults to patching in-place.")
|
|
35
|
+
.option("-d, --delimiter <delimiter>", "Heading delimiter to use in place of '::'.", "::")
|
|
36
|
+
.argument("<operation>", "Operation to perform ('replace', 'append', etc.)")
|
|
37
|
+
.argument("<targetType>", "Target type ('heading', 'block', etc.)")
|
|
38
|
+
.argument("<target>", "Target ('::'-delimited by default for Headings); see `mdpatch print-map <path to document>` for options)")
|
|
39
|
+
.argument("<documentPath>", "Path to document to apply patch to.")
|
|
40
|
+
.action(async (operation, targetType, target, documentPath, options) => {
|
|
41
|
+
let content;
|
|
42
|
+
if (options.input) {
|
|
43
|
+
content = await fs.readFile(options.input, "utf-8");
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
content = await readStdin();
|
|
47
|
+
}
|
|
48
|
+
const document = await fs.readFile(documentPath, "utf-8");
|
|
49
|
+
const instruction = {
|
|
50
|
+
operation,
|
|
51
|
+
targetType,
|
|
52
|
+
content,
|
|
53
|
+
target: targetType !== "heading" ? target : target.split(options.delimiter),
|
|
54
|
+
};
|
|
55
|
+
const patchedDocument = applyPatch(document, instruction);
|
|
56
|
+
if (options.output === "-") {
|
|
57
|
+
process.stdout.write(patchedDocument);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
await fs.writeFile(options.output ? options.output : documentPath, patchedDocument);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
program
|
|
64
|
+
.command("apply")
|
|
65
|
+
.argument("<path>", "file to patch")
|
|
66
|
+
.argument("<patch>", "patch file to apply")
|
|
67
|
+
.option("-o, --output <output>", "write output to the specified path instead of applying in-place; use '-' for stdout")
|
|
68
|
+
.action(async (path, patch, options) => {
|
|
69
|
+
let patchParsed;
|
|
70
|
+
let patchData;
|
|
71
|
+
try {
|
|
72
|
+
if (patch === "-") {
|
|
73
|
+
patchData = await readStdin();
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
patchData = await fs.readFile(patch, "utf-8");
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch (e) {
|
|
80
|
+
console.error("Failed to read patch: ", e);
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
const parsedData = JSON.parse(patchData);
|
|
85
|
+
if (!Array.isArray(parsedData)) {
|
|
86
|
+
patchParsed = [parsedData];
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
patchParsed = parsedData;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
catch (e) {
|
|
93
|
+
console.error("Could not parse patch file as JSON");
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
let document = await fs.readFile(path, "utf-8");
|
|
97
|
+
console.log("Document", document);
|
|
98
|
+
for (const instruction of patchParsed) {
|
|
99
|
+
document = applyPatch(document, instruction);
|
|
100
|
+
}
|
|
101
|
+
if (options.output === "-") {
|
|
102
|
+
process.stdout.write(document);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
await fs.writeFile(options.output ? options.output : path, document);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
program.parse(process.argv);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const TARGETABLE_BY_ISOLATED_BLOCK_REFERENCE: string[];
|
|
2
|
+
export declare const CAN_INCLUDE_BLOCK_REFERENCE: string[];
|
|
3
|
+
export declare enum ContentType {
|
|
4
|
+
text = "text/plain",
|
|
5
|
+
tableRows = "application/vnd.markdown-patch.table-rows+json"
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,sCAAsC,UAQlD,CAAC;AAEF,eAAO,MAAM,2BAA2B,UAA6B,CAAC;AAEtE,oBAAY,WAAW;IACrB,IAAI,eAAe;IACnB,SAAS,mDAAmD;CAC7D"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export const TARGETABLE_BY_ISOLATED_BLOCK_REFERENCE = [
|
|
2
|
+
"code",
|
|
3
|
+
"heading",
|
|
4
|
+
"table",
|
|
5
|
+
"blockquote",
|
|
6
|
+
"list",
|
|
7
|
+
"paragraph",
|
|
8
|
+
"image",
|
|
9
|
+
];
|
|
10
|
+
export const CAN_INCLUDE_BLOCK_REFERENCE = ["paragraph", "list_item"];
|
|
11
|
+
export var ContentType;
|
|
12
|
+
(function (ContentType) {
|
|
13
|
+
ContentType["text"] = "text/plain";
|
|
14
|
+
ContentType["tableRows"] = "application/vnd.markdown-patch.table-rows+json";
|
|
15
|
+
})(ContentType || (ContentType = {}));
|
package/dist/debug.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"debug.d.ts","sourceRoot":"","sources":["../src/debug.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,eAAO,MAAM,QAAQ,YACV,MAAM,eACF,WAAW,SACjB,MAAM,GAAG,SAAS,KACxB,IAmEF,CAAC"}
|
package/dist/debug.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
export const printMap = (content, documentMap, regex) => {
|
|
3
|
+
for (const type in documentMap) {
|
|
4
|
+
for (const positionName in documentMap[type]) {
|
|
5
|
+
const position = documentMap[type][positionName];
|
|
6
|
+
const blockName = `[${chalk.magenta(type)}] ${positionName
|
|
7
|
+
.split("\u001f")
|
|
8
|
+
.map((pos) => chalk.blueBright(pos))
|
|
9
|
+
.join(",")}`;
|
|
10
|
+
if (regex && !blockName.match(regex)) {
|
|
11
|
+
continue;
|
|
12
|
+
}
|
|
13
|
+
console.log("\n" + blockName + "\n");
|
|
14
|
+
if (position.content.start < position.marker.start) {
|
|
15
|
+
console.log(content
|
|
16
|
+
.slice(position.content.start - 100, position.content.start)
|
|
17
|
+
.replaceAll("\n", "\\n\n") +
|
|
18
|
+
chalk.black.bgGreen(content
|
|
19
|
+
.slice(position.content.start, position.content.end)
|
|
20
|
+
.replaceAll("\n", "\\n\n")) +
|
|
21
|
+
content
|
|
22
|
+
.slice(position.content.end, Math.min(position.content.end + 100, position.marker.start))
|
|
23
|
+
.replaceAll("\n", "\\n\n") +
|
|
24
|
+
chalk.black.bgRed(content
|
|
25
|
+
.slice(position.marker.start, position.marker.end)
|
|
26
|
+
.replaceAll("\n", "\\n\n")) +
|
|
27
|
+
content
|
|
28
|
+
.slice(position.marker.end, position.marker.end + 100)
|
|
29
|
+
.replaceAll("\n", "\\n\n"));
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
console.log(content
|
|
33
|
+
.slice(position.marker.start - 100, position.marker.start)
|
|
34
|
+
.replaceAll("\n", "\\n\n") +
|
|
35
|
+
chalk.black.bgRed(content
|
|
36
|
+
.slice(position.marker.start, position.marker.end)
|
|
37
|
+
.replaceAll("\n", "\\n\n")) +
|
|
38
|
+
content
|
|
39
|
+
.slice(position.marker.end, Math.min(position.marker.end + 100, position.content.start))
|
|
40
|
+
.replaceAll("\n", "\\n\n") +
|
|
41
|
+
chalk.black.bgGreen(content
|
|
42
|
+
.slice(position.content.start, position.content.end)
|
|
43
|
+
.replaceAll("\n", "\\n\n")) +
|
|
44
|
+
content
|
|
45
|
+
.slice(position.content.end, position.content.end + 100)
|
|
46
|
+
.replaceAll("\n", "\\n\n"));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,WAAW,EACX,UAAU,EACV,kBAAkB,EAClB,UAAU,GACX,MAAM,YAAY,CAAC;AAEpB,cAAc,YAAY,CAAC"}
|
package/dist/index.js
ADDED
package/dist/map.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"map.d.ts","sourceRoot":"","sources":["../src/map.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,WAAW,EAGZ,MAAM,YAAY,CAAC;AAyLpB,eAAO,MAAM,cAAc,aAAc,MAAM,KAAG,WAQjD,CAAC"}
|
package/dist/map.js
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import * as marked from "marked";
|
|
2
|
+
import { CAN_INCLUDE_BLOCK_REFERENCE, TARGETABLE_BY_ISOLATED_BLOCK_REFERENCE, } from "./constants.js";
|
|
3
|
+
function getHeadingPositions(document, tokens) {
|
|
4
|
+
// If the document starts with frontmatter, figure out where
|
|
5
|
+
// the frontmatter ends so we can know where the text of the
|
|
6
|
+
// document begins
|
|
7
|
+
let documentStart = 0;
|
|
8
|
+
if (tokens[0].type === "hr") {
|
|
9
|
+
documentStart = tokens[0].raw.length + 1;
|
|
10
|
+
for (const token of tokens.slice(1)) {
|
|
11
|
+
documentStart += token.raw.length;
|
|
12
|
+
if (token.type === "hr") {
|
|
13
|
+
break;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
const positions = {
|
|
18
|
+
"": {
|
|
19
|
+
content: {
|
|
20
|
+
start: documentStart,
|
|
21
|
+
end: document.length,
|
|
22
|
+
},
|
|
23
|
+
marker: {
|
|
24
|
+
start: 0,
|
|
25
|
+
end: 0,
|
|
26
|
+
},
|
|
27
|
+
level: 0,
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
const stack = [];
|
|
31
|
+
let currentPosition = 0;
|
|
32
|
+
tokens.forEach((token, index) => {
|
|
33
|
+
if (token.type === "heading") {
|
|
34
|
+
const headingToken = token;
|
|
35
|
+
const startHeading = document.indexOf(headingToken.raw.trim(), currentPosition);
|
|
36
|
+
const endHeading = startHeading + headingToken.raw.trim().length + 1;
|
|
37
|
+
const headingLevel = headingToken.depth;
|
|
38
|
+
// Determine the start of the content after this heading
|
|
39
|
+
const startContent = endHeading;
|
|
40
|
+
// Determine the end of the content before the next heading of the same or higher level, or end of document
|
|
41
|
+
let endContent = undefined;
|
|
42
|
+
for (let i = index + 1; i < tokens.length; i++) {
|
|
43
|
+
if (tokens[i].type === "heading" &&
|
|
44
|
+
tokens[i].depth <= headingLevel) {
|
|
45
|
+
endContent = document.indexOf(tokens[i].raw.trim(), startContent);
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (endContent === undefined) {
|
|
50
|
+
endContent = document.length;
|
|
51
|
+
}
|
|
52
|
+
const currentHeading = {
|
|
53
|
+
content: {
|
|
54
|
+
start: startContent,
|
|
55
|
+
end: endContent,
|
|
56
|
+
},
|
|
57
|
+
marker: {
|
|
58
|
+
start: startHeading,
|
|
59
|
+
end: endHeading,
|
|
60
|
+
},
|
|
61
|
+
level: headingLevel,
|
|
62
|
+
};
|
|
63
|
+
// Build the full heading path with parent headings separated by '\t'
|
|
64
|
+
let fullHeadingPath = headingToken.text.trim();
|
|
65
|
+
while (stack.length &&
|
|
66
|
+
stack[stack.length - 1].position.level >= headingLevel) {
|
|
67
|
+
stack.pop();
|
|
68
|
+
}
|
|
69
|
+
if (stack.length) {
|
|
70
|
+
const parent = stack[stack.length - 1];
|
|
71
|
+
parent.position.content.end = endContent;
|
|
72
|
+
fullHeadingPath = `${parent.heading}\u001f${fullHeadingPath}`;
|
|
73
|
+
}
|
|
74
|
+
positions[fullHeadingPath] = currentHeading;
|
|
75
|
+
stack.push({ heading: fullHeadingPath, position: currentHeading });
|
|
76
|
+
currentPosition = endHeading;
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
return positions;
|
|
80
|
+
}
|
|
81
|
+
function getBlockPositions(document, tokens) {
|
|
82
|
+
const positions = {};
|
|
83
|
+
let lastBlockDetails = undefined;
|
|
84
|
+
let startContent = 0;
|
|
85
|
+
let endContent = 0;
|
|
86
|
+
let endMarker = 0;
|
|
87
|
+
marked.walkTokens(tokens, (token) => {
|
|
88
|
+
const blockReferenceRegex = /(?:\s+|^)\^([a-zA-Z0-9_-]+)\s*$/;
|
|
89
|
+
startContent = document.indexOf(token.raw, startContent);
|
|
90
|
+
const match = blockReferenceRegex.exec(token.raw);
|
|
91
|
+
endContent = startContent + (match ? match.index : token.raw.length);
|
|
92
|
+
const startMarker = match ? startContent + match.index : -1;
|
|
93
|
+
endMarker = startContent + token.raw.length;
|
|
94
|
+
// The end of a list item token sometimes doesn't include the trailing
|
|
95
|
+
// newline -- i'm honestly not sure why, but treating it as
|
|
96
|
+
// included here would simplify my implementation
|
|
97
|
+
if (document.slice(endMarker - 1, endMarker) !== "\n" &&
|
|
98
|
+
document.slice(endMarker, endMarker + 1) === "\n") {
|
|
99
|
+
endMarker += 1;
|
|
100
|
+
}
|
|
101
|
+
else if (document.slice(endMarker - 2, endMarker) !== "\r\n" &&
|
|
102
|
+
document.slice(endMarker, endMarker + 2) === "\r\n") {
|
|
103
|
+
endMarker += 2;
|
|
104
|
+
}
|
|
105
|
+
if (CAN_INCLUDE_BLOCK_REFERENCE.includes(token.type) && match) {
|
|
106
|
+
const name = match[1];
|
|
107
|
+
if (!name || match.index === undefined) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const finalStartContent = {
|
|
111
|
+
start: startContent,
|
|
112
|
+
end: endContent,
|
|
113
|
+
};
|
|
114
|
+
if (finalStartContent.start === finalStartContent.end &&
|
|
115
|
+
lastBlockDetails) {
|
|
116
|
+
finalStartContent.start = lastBlockDetails.start;
|
|
117
|
+
finalStartContent.end = lastBlockDetails.end;
|
|
118
|
+
}
|
|
119
|
+
positions[name] = {
|
|
120
|
+
content: finalStartContent,
|
|
121
|
+
marker: {
|
|
122
|
+
start: startMarker,
|
|
123
|
+
end: endMarker,
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
if (TARGETABLE_BY_ISOLATED_BLOCK_REFERENCE.includes(token.type)) {
|
|
128
|
+
lastBlockDetails = {
|
|
129
|
+
token: token,
|
|
130
|
+
start: startContent,
|
|
131
|
+
end: endContent - 1,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
return positions;
|
|
136
|
+
}
|
|
137
|
+
export const getDocumentMap = (document) => {
|
|
138
|
+
const lexer = new marked.Lexer();
|
|
139
|
+
const tokens = lexer.lex(document);
|
|
140
|
+
return {
|
|
141
|
+
heading: getHeadingPositions(document, tokens),
|
|
142
|
+
block: getBlockPositions(document, tokens),
|
|
143
|
+
};
|
|
144
|
+
};
|
package/dist/patch.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { DocumentMapMarkerContentPair, PatchInstruction } from "./types.js";
|
|
2
|
+
export declare enum PatchFailureReason {
|
|
3
|
+
InvalidTarget = "invalid-target",
|
|
4
|
+
ContentAlreadyPreexistsInTarget = "content-already-preexists-in-target",
|
|
5
|
+
TableContentIncorrectColumnCount = "table-content-incorrect-column-count",
|
|
6
|
+
RequestedBlockTypeBehaviorUnavailable = "requested-block-type-behavior-unavailable"
|
|
7
|
+
}
|
|
8
|
+
export declare class PatchFailed extends Error {
|
|
9
|
+
reason: PatchFailureReason;
|
|
10
|
+
instruction: PatchInstruction;
|
|
11
|
+
targetMap: DocumentMapMarkerContentPair | null;
|
|
12
|
+
constructor(reason: PatchFailureReason, instruction: PatchInstruction, targetMap: DocumentMapMarkerContentPair | null);
|
|
13
|
+
}
|
|
14
|
+
export declare class PatchError extends Error {
|
|
15
|
+
}
|
|
16
|
+
export declare class TablePartsNotFound extends Error {
|
|
17
|
+
}
|
|
18
|
+
export declare const applyPatch: (document: string, instruction: PatchInstruction) => string;
|
|
19
|
+
//# sourceMappingURL=patch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"patch.d.ts","sourceRoot":"","sources":["../src/patch.ts"],"names":[],"mappings":"AAEA,OAAO,EAIL,4BAA4B,EAE5B,gBAAgB,EAEjB,MAAM,YAAY,CAAC;AAGpB,oBAAY,kBAAkB;IAC5B,aAAa,mBAAmB;IAChC,+BAA+B,wCAAwC;IACvE,gCAAgC,yCAAyC;IACzE,qCAAqC,8CAA8C;CACpF;AAED,qBAAa,WAAY,SAAQ,KAAK;IAC7B,MAAM,EAAE,kBAAkB,CAAC;IAC3B,WAAW,EAAE,gBAAgB,CAAC;IAC9B,SAAS,EAAE,4BAA4B,GAAG,IAAI,CAAC;gBAGpD,MAAM,EAAE,kBAAkB,EAC1B,WAAW,EAAE,gBAAgB,EAC7B,SAAS,EAAE,4BAA4B,GAAG,IAAI;CAUjD;AAED,qBAAa,UAAW,SAAQ,KAAK;CAAG;AA0CxC,qBAAa,kBAAmB,SAAQ,KAAK;CAAG;AAmNhD,eAAO,MAAM,UAAU,aACX,MAAM,eACH,gBAAgB,KAC5B,MA8BF,CAAC"}
|