markdown-patch 0.1.1 → 0.1.2
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.js +108 -0
- package/{src/constants.ts → dist/constants.js} +7 -8
- package/dist/debug.js +50 -0
- package/dist/index.js +1 -0
- package/dist/map.js +144 -0
- package/dist/patch.js +191 -0
- package/dist/tests/map.test.js +202 -0
- package/dist/tests/patch.test.js +222 -0
- package/dist/types.js +1 -0
- package/package.json +7 -2
- 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/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
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
aliases:
|
|
3
|
-
- Structured Markdown Patch
|
|
4
|
-
project-type: Technical
|
|
5
|
-
repository: https://github.com/coddingtonbear/markdown-patch
|
|
6
|
-
---
|
|
7
|
-
Beep Boop
|
|
8
|
-
|
|
9
|
-
# Overview
|
|
10
|
-
I came up with this project for supporting [[Obsidian Local Rest API]] (and via that [[Obsidian Web]]) because I often need to shove data into Markdown documents for posterity, but want to be able to do that programmatically with more care than just shoving content to the end of a file.
|
|
11
|
-
# Problems
|
|
12
|
-
- ~~It would be nice for the mechanism to be able to handle something like `upsert` for frontmatter fields. See [[#^e6068e]] in addition to what we already support [[#^259a73]].~~
|
|
13
|
-
- This was solved by making every content block directly-addressable. All interactions treat the document as a key-value mapping.
|
|
14
|
-
- ~~You can't use our earlier header delimiter `::` in an HTTP header; how did I not notice that? I've had a [conversation with ChatGPT](https://chatgpt.com/share/117b262a-f534-40e6-bc05-287758706f34) to land on a choice of `@#@` instead, but there aren't obvious good options. See [[#^1d6271]]~~
|
|
15
|
-
- I changed my mind in the end and went with the more-likely-to-collide-but-at-least-not-bizarre `///`.
|
|
16
|
-
- Should we allow partial matches? The pros are that it would make the usual, garden path of just wanting to push content into a section very easy. The cons are that it makes it kind of unclear what's going to happen when you do an `upsert` or `insert` for a particular value. (See [[#^bfec1f]] for more).
|
|
17
|
-
|
|
18
|
-
# Actions
|
|
19
|
-
| Name | Description | Heading? | Frontmatter? | Block |
|
|
20
|
-
| --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ------------ | ------------------- |
|
|
21
|
-
| `update` | Find the referenced `target` and replace the content at that region. | ✅ | ✅ | ✅ |
|
|
22
|
-
| `append` | Find the referenced `target` and add content to the end of its region. | ✅ | ✅ | ✅ |
|
|
23
|
-
| `prepend` | Find the referenced `target` and add content to the beginning of its region. | ✅ | ✅ | ✅ |
|
|
24
|
-
| `insert` | Find the path leading to the referenced `target` and add the specified content under a the specified name. This will create a new header or frontmatter field if necessary. | ✅ | ✅ | ❌[^block-ambiguity] |
|
|
25
|
-
| `upsert` | Find the path leading to the referenced `target` and either replace the content under the specified name, or add new content with a new header or frontmatter field if necessary. | ✅ | ✅ | ❌[^block-ambiguity] |
|
|
26
|
-
|
|
27
|
-
^2c67a6
|
|
28
|
-
|
|
29
|
-
# Headers
|
|
30
|
-
| Header | Explanation |
|
|
31
|
-
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
32
|
-
| `Target-Type` | `heading`, `block`, `frontmatter` |
|
|
33
|
-
| `Target` | Name of the target:<br>- `heading`: The `///`-delimited path to the heading to replace the content of. E.g. `Page Targets///Block///Use Cases`. This value should be URL-encoded. |
|
|
34
|
-
| `Target-Delimiter` | By default, we use `///` to delimit a `Target`, but it's remotely possible that this value might be present in a header. If it is, you can specify a different delimiter to use for `Target`. |
|
|
35
|
-
|
|
36
|
-
^1d6271
|
|
37
|
-
|
|
38
|
-
# Page Targets
|
|
39
|
-
|
|
40
|
-
## Heading
|
|
41
|
-
|
|
42
|
-
| Heading | Value |
|
|
43
|
-
| ------------- | ----------------------------------------------------- |
|
|
44
|
-
| `Target-Type` | `heading` |
|
|
45
|
-
| `Target` | The path to the heading you would like to append to. |
|
|
46
|
-
|
|
47
|
-
^bfec1f
|
|
48
|
-
|
|
49
|
-
| Position | Where |
|
|
50
|
-
| -------- | -------------------------------------------------------------- |
|
|
51
|
-
| Start | Beginning of line immediately following line named by heading |
|
|
52
|
-
| End | Last newline before heading of same or higher priority or EOF. |
|
|
53
|
-
| | |
|
|
54
|
-
Unlike with [[#Heading]], this targets the *content* of the heading and does not include the heading iself.
|
|
55
|
-
- ✅: ...replacing the content specified by a particular heading.
|
|
56
|
-
- ✅: ...appending content to a block specified by a particular heading.
|
|
57
|
-
## Block
|
|
58
|
-
| Position | Where |
|
|
59
|
-
| -------- | ------------------------------------------------------ |
|
|
60
|
-
| Start | Beginning of line for specified block. |
|
|
61
|
-
| End | Last character (including newline) of specified block. |
|
|
62
|
-
A "Block" in Obsidian can be any *block*-type element. This might mean a paragraph, but it could also mean a table, but how block references are marked differs in Obsidian depending upon what kind of block is being marked.
|
|
63
|
-
### Use Cases
|
|
64
|
-
- ✅: ...replacing the content specified by a particular block ID.
|
|
65
|
-
- I want to be able to replace a whole table or whole paragraph with new content.
|
|
66
|
-
- ✅: ...appending content to a block specified by a particular block ID.
|
|
67
|
-
- I want to be able to add new rows to an existing table.
|
|
68
|
-
- I want to be able to add new content to the end of a line.
|
|
69
|
-
|
|
70
|
-
## Frontmatter Field
|
|
71
|
-
|
|
72
|
-
| Position | Where |
|
|
73
|
-
| -------- | ------------------------------------------------------------------------ |
|
|
74
|
-
| Start | First character of content referenced by a particular frontmatter field. |
|
|
75
|
-
| End | Last character of content referenced by a particular frontmatter field. |
|
|
76
|
-
### Use Cases
|
|
77
|
-
- ✅: ...appending content to an existing frontmatter field.
|
|
78
|
-
- ✅: ...replacing the content of an existing frontmatter field. ^259a73
|
|
79
|
-
- ✅: ...adding a new frontmatter field. ^e6068e
|
|
80
|
-
## Document Properties (Exploratory)
|
|
81
|
-
|
|
82
|
-
[^block-ambiguity]: There is currently no obvious place to plop a block were we to create a new one. So, I might implement this such that these actions *work*, but will just add the block to the end of a file. This isn't great, but it's at least consistent?
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
aliases:
|
|
3
|
-
- Structured Markdown Patch
|
|
4
|
-
project-type: Technical
|
|
5
|
-
repository: https://github.com/coddingtonbear/markdown-patch
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# Overview
|
|
9
|
-
Beep Boop
|
|
10
|
-
I came up with this project for supporting [[Obsidian Local Rest API]] (and via that [[Obsidian Web]]) because I often need to shove data into Markdown documents for posterity, but want to be able to do that programmatically with more care than just shoving content to the end of a file.
|
|
11
|
-
# Problems
|
|
12
|
-
- ~~It would be nice for the mechanism to be able to handle something like `upsert` for frontmatter fields. See [[#^e6068e]] in addition to what we already support [[#^259a73]].~~
|
|
13
|
-
- This was solved by making every content block directly-addressable. All interactions treat the document as a key-value mapping.
|
|
14
|
-
- ~~You can't use our earlier header delimiter `::` in an HTTP header; how did I not notice that? I've had a [conversation with ChatGPT](https://chatgpt.com/share/117b262a-f534-40e6-bc05-287758706f34) to land on a choice of `@#@` instead, but there aren't obvious good options. See [[#^1d6271]]~~
|
|
15
|
-
- I changed my mind in the end and went with the more-likely-to-collide-but-at-least-not-bizarre `///`.
|
|
16
|
-
- Should we allow partial matches? The pros are that it would make the usual, garden path of just wanting to push content into a section very easy. The cons are that it makes it kind of unclear what's going to happen when you do an `upsert` or `insert` for a particular value. (See [[#^bfec1f]] for more).
|
|
17
|
-
|
|
18
|
-
# Actions
|
|
19
|
-
| Name | Description | Heading? | Frontmatter? | Block |
|
|
20
|
-
| --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ------------ | ------------------- |
|
|
21
|
-
| `update` | Find the referenced `target` and replace the content at that region. | ✅ | ✅ | ✅ |
|
|
22
|
-
| `append` | Find the referenced `target` and add content to the end of its region. | ✅ | ✅ | ✅ |
|
|
23
|
-
| `prepend` | Find the referenced `target` and add content to the beginning of its region. | ✅ | ✅ | ✅ |
|
|
24
|
-
| `insert` | Find the path leading to the referenced `target` and add the specified content under a the specified name. This will create a new header or frontmatter field if necessary. | ✅ | ✅ | ❌[^block-ambiguity] |
|
|
25
|
-
| `upsert` | Find the path leading to the referenced `target` and either replace the content under the specified name, or add new content with a new header or frontmatter field if necessary. | ✅ | ✅ | ❌[^block-ambiguity] |
|
|
26
|
-
|
|
27
|
-
^2c67a6
|
|
28
|
-
|
|
29
|
-
# Headers
|
|
30
|
-
| Header | Explanation |
|
|
31
|
-
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
32
|
-
| `Target-Type` | `heading`, `block`, `frontmatter` |
|
|
33
|
-
| `Target` | Name of the target:<br>- `heading`: The `///`-delimited path to the heading to replace the content of. E.g. `Page Targets///Block///Use Cases`. This value should be URL-encoded. |
|
|
34
|
-
| `Target-Delimiter` | By default, we use `///` to delimit a `Target`, but it's remotely possible that this value might be present in a header. If it is, you can specify a different delimiter to use for `Target`. |
|
|
35
|
-
|
|
36
|
-
^1d6271
|
|
37
|
-
|
|
38
|
-
# Page Targets
|
|
39
|
-
|
|
40
|
-
## Heading
|
|
41
|
-
|
|
42
|
-
| Heading | Value |
|
|
43
|
-
| ------------- | ----------------------------------------------------- |
|
|
44
|
-
| `Target-Type` | `heading` |
|
|
45
|
-
| `Target` | The path to the heading you would like to append to. |
|
|
46
|
-
|
|
47
|
-
^bfec1f
|
|
48
|
-
|
|
49
|
-
| Position | Where |
|
|
50
|
-
| -------- | -------------------------------------------------------------- |
|
|
51
|
-
| Start | Beginning of line immediately following line named by heading |
|
|
52
|
-
| End | Last newline before heading of same or higher priority or EOF. |
|
|
53
|
-
| | |
|
|
54
|
-
Unlike with [[#Heading]], this targets the *content* of the heading and does not include the heading iself.
|
|
55
|
-
- ✅: ...replacing the content specified by a particular heading.
|
|
56
|
-
- ✅: ...appending content to a block specified by a particular heading.
|
|
57
|
-
## Block
|
|
58
|
-
| Position | Where |
|
|
59
|
-
| -------- | ------------------------------------------------------ |
|
|
60
|
-
| Start | Beginning of line for specified block. |
|
|
61
|
-
| End | Last character (including newline) of specified block. |
|
|
62
|
-
A "Block" in Obsidian can be any *block*-type element. This might mean a paragraph, but it could also mean a table, but how block references are marked differs in Obsidian depending upon what kind of block is being marked.
|
|
63
|
-
### Use Cases
|
|
64
|
-
- ✅: ...replacing the content specified by a particular block ID.
|
|
65
|
-
- I want to be able to replace a whole table or whole paragraph with new content.
|
|
66
|
-
- ✅: ...appending content to a block specified by a particular block ID.
|
|
67
|
-
- I want to be able to add new rows to an existing table.
|
|
68
|
-
- I want to be able to add new content to the end of a line.
|
|
69
|
-
|
|
70
|
-
## Frontmatter Field
|
|
71
|
-
|
|
72
|
-
| Position | Where |
|
|
73
|
-
| -------- | ------------------------------------------------------------------------ |
|
|
74
|
-
| Start | First character of content referenced by a particular frontmatter field. |
|
|
75
|
-
| End | Last character of content referenced by a particular frontmatter field. |
|
|
76
|
-
### Use Cases
|
|
77
|
-
- ✅: ...appending content to an existing frontmatter field.
|
|
78
|
-
- ✅: ...replacing the content of an existing frontmatter field. ^259a73
|
|
79
|
-
- ✅: ...adding a new frontmatter field. ^e6068e
|
|
80
|
-
## Document Properties (Exploratory)
|
|
81
|
-
|
|
82
|
-
[^block-ambiguity]: There is currently no obvious place to plop a block were we to create a new one. So, I might implement this such that these actions *work*, but will just add the block to the end of a file. This isn't great, but it's at least consistent?
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
aliases:
|
|
3
|
-
- Structured Markdown Patch
|
|
4
|
-
project-type: Technical
|
|
5
|
-
repository: https://github.com/coddingtonbear/markdown-patch
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# Overview
|
|
9
|
-
Beep Boop
|
|
10
|
-
# Problems
|
|
11
|
-
- ~~It would be nice for the mechanism to be able to handle something like `upsert` for frontmatter fields. See [[#^e6068e]] in addition to what we already support [[#^259a73]].~~
|
|
12
|
-
- This was solved by making every content block directly-addressable. All interactions treat the document as a key-value mapping.
|
|
13
|
-
- ~~You can't use our earlier header delimiter `::` in an HTTP header; how did I not notice that? I've had a [conversation with ChatGPT](https://chatgpt.com/share/117b262a-f534-40e6-bc05-287758706f34) to land on a choice of `@#@` instead, but there aren't obvious good options. See [[#^1d6271]]~~
|
|
14
|
-
- I changed my mind in the end and went with the more-likely-to-collide-but-at-least-not-bizarre `///`.
|
|
15
|
-
- Should we allow partial matches? The pros are that it would make the usual, garden path of just wanting to push content into a section very easy. The cons are that it makes it kind of unclear what's going to happen when you do an `upsert` or `insert` for a particular value. (See [[#^bfec1f]] for more).
|
|
16
|
-
|
|
17
|
-
# Actions
|
|
18
|
-
| Name | Description | Heading? | Frontmatter? | Block |
|
|
19
|
-
| --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ------------ | ------------------- |
|
|
20
|
-
| `update` | Find the referenced `target` and replace the content at that region. | ✅ | ✅ | ✅ |
|
|
21
|
-
| `append` | Find the referenced `target` and add content to the end of its region. | ✅ | ✅ | ✅ |
|
|
22
|
-
| `prepend` | Find the referenced `target` and add content to the beginning of its region. | ✅ | ✅ | ✅ |
|
|
23
|
-
| `insert` | Find the path leading to the referenced `target` and add the specified content under a the specified name. This will create a new header or frontmatter field if necessary. | ✅ | ✅ | ❌[^block-ambiguity] |
|
|
24
|
-
| `upsert` | Find the path leading to the referenced `target` and either replace the content under the specified name, or add new content with a new header or frontmatter field if necessary. | ✅ | ✅ | ❌[^block-ambiguity] |
|
|
25
|
-
|
|
26
|
-
^2c67a6
|
|
27
|
-
|
|
28
|
-
# Headers
|
|
29
|
-
| Header | Explanation |
|
|
30
|
-
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
31
|
-
| `Target-Type` | `heading`, `block`, `frontmatter` |
|
|
32
|
-
| `Target` | Name of the target:<br>- `heading`: The `///`-delimited path to the heading to replace the content of. E.g. `Page Targets///Block///Use Cases`. This value should be URL-encoded. |
|
|
33
|
-
| `Target-Delimiter` | By default, we use `///` to delimit a `Target`, but it's remotely possible that this value might be present in a header. If it is, you can specify a different delimiter to use for `Target`. |
|
|
34
|
-
|
|
35
|
-
^1d6271
|
|
36
|
-
|
|
37
|
-
# Page Targets
|
|
38
|
-
|
|
39
|
-
## Heading
|
|
40
|
-
|
|
41
|
-
| Heading | Value |
|
|
42
|
-
| ------------- | ----------------------------------------------------- |
|
|
43
|
-
| `Target-Type` | `heading` |
|
|
44
|
-
| `Target` | The path to the heading you would like to append to. |
|
|
45
|
-
|
|
46
|
-
^bfec1f
|
|
47
|
-
|
|
48
|
-
| Position | Where |
|
|
49
|
-
| -------- | -------------------------------------------------------------- |
|
|
50
|
-
| Start | Beginning of line immediately following line named by heading |
|
|
51
|
-
| End | Last newline before heading of same or higher priority or EOF. |
|
|
52
|
-
| | |
|
|
53
|
-
Unlike with [[#Heading]], this targets the *content* of the heading and does not include the heading iself.
|
|
54
|
-
- ✅: ...replacing the content specified by a particular heading.
|
|
55
|
-
- ✅: ...appending content to a block specified by a particular heading.
|
|
56
|
-
## Block
|
|
57
|
-
| Position | Where |
|
|
58
|
-
| -------- | ------------------------------------------------------ |
|
|
59
|
-
| Start | Beginning of line for specified block. |
|
|
60
|
-
| End | Last character (including newline) of specified block. |
|
|
61
|
-
A "Block" in Obsidian can be any *block*-type element. This might mean a paragraph, but it could also mean a table, but how block references are marked differs in Obsidian depending upon what kind of block is being marked.
|
|
62
|
-
### Use Cases
|
|
63
|
-
- ✅: ...replacing the content specified by a particular block ID.
|
|
64
|
-
- I want to be able to replace a whole table or whole paragraph with new content.
|
|
65
|
-
- ✅: ...appending content to a block specified by a particular block ID.
|
|
66
|
-
- I want to be able to add new rows to an existing table.
|
|
67
|
-
- I want to be able to add new content to the end of a line.
|
|
68
|
-
|
|
69
|
-
## Frontmatter Field
|
|
70
|
-
|
|
71
|
-
| Position | Where |
|
|
72
|
-
| -------- | ------------------------------------------------------------------------ |
|
|
73
|
-
| Start | First character of content referenced by a particular frontmatter field. |
|
|
74
|
-
| End | Last character of content referenced by a particular frontmatter field. |
|
|
75
|
-
### Use Cases
|
|
76
|
-
- ✅: ...appending content to an existing frontmatter field.
|
|
77
|
-
- ✅: ...replacing the content of an existing frontmatter field. ^259a73
|
|
78
|
-
- ✅: ...adding a new frontmatter field. ^e6068e
|
|
79
|
-
## Document Properties (Exploratory)
|
|
80
|
-
|
|
81
|
-
[^block-ambiguity]: There is currently no obvious place to plop a block were we to create a new one. So, I might implement this such that these actions *work*, but will just add the block to the end of a file. This isn't great, but it's at least consistent?
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
aliases:
|
|
3
|
-
- Structured Markdown Patch
|
|
4
|
-
project-type: Technical
|
|
5
|
-
repository: https://github.com/coddingtonbear/markdown-patch
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# Overview
|
|
9
|
-
I came up with this project for supporting [[Obsidian Local Rest API]] (and via that [[Obsidian Web]]) because I often need to shove data into Markdown documents for posterity, but want to be able to do that programmatically with more care than just shoving content to the end of a file.
|
|
10
|
-
# Problems
|
|
11
|
-
- ~~It would be nice for the mechanism to be able to handle something like `upsert` for frontmatter fields. See [[#^e6068e]] in addition to what we already support [[#^259a73]].~~
|
|
12
|
-
- This was solved by making every content block directly-addressable. All interactions treat the document as a key-value mapping.
|
|
13
|
-
- ~~You can't use our earlier header delimiter `::` in an HTTP header; how did I not notice that? I've had a [conversation with ChatGPT](https://chatgpt.com/share/117b262a-f534-40e6-bc05-287758706f34) to land on a choice of `@#@` instead, but there aren't obvious good options. See [[#^1d6271]]~~
|
|
14
|
-
- I changed my mind in the end and went with the more-likely-to-collide-but-at-least-not-bizarre `///`.
|
|
15
|
-
- Should we allow partial matches? The pros are that it would make the usual, garden path of just wanting to push content into a section very easy. The cons are that it makes it kind of unclear what's going to happen when you do an `upsert` or `insert` for a particular value. (See [[#^bfec1f]] for more).Beep Boop
|
|
16
|
-
# Actions
|
|
17
|
-
| Name | Description | Heading? | Frontmatter? | Block |
|
|
18
|
-
| --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ------------ | ------------------- |
|
|
19
|
-
| `update` | Find the referenced `target` and replace the content at that region. | ✅ | ✅ | ✅ |
|
|
20
|
-
| `append` | Find the referenced `target` and add content to the end of its region. | ✅ | ✅ | ✅ |
|
|
21
|
-
| `prepend` | Find the referenced `target` and add content to the beginning of its region. | ✅ | ✅ | ✅ |
|
|
22
|
-
| `insert` | Find the path leading to the referenced `target` and add the specified content under a the specified name. This will create a new header or frontmatter field if necessary. | ✅ | ✅ | ❌[^block-ambiguity] |
|
|
23
|
-
| `upsert` | Find the path leading to the referenced `target` and either replace the content under the specified name, or add new content with a new header or frontmatter field if necessary. | ✅ | ✅ | ❌[^block-ambiguity] |
|
|
24
|
-
|
|
25
|
-
^2c67a6
|
|
26
|
-
|
|
27
|
-
# Headers
|
|
28
|
-
| Header | Explanation |
|
|
29
|
-
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
30
|
-
| `Target-Type` | `heading`, `block`, `frontmatter` |
|
|
31
|
-
| `Target` | Name of the target:<br>- `heading`: The `///`-delimited path to the heading to replace the content of. E.g. `Page Targets///Block///Use Cases`. This value should be URL-encoded. |
|
|
32
|
-
| `Target-Delimiter` | By default, we use `///` to delimit a `Target`, but it's remotely possible that this value might be present in a header. If it is, you can specify a different delimiter to use for `Target`. |
|
|
33
|
-
|
|
34
|
-
^1d6271
|
|
35
|
-
|
|
36
|
-
# Page Targets
|
|
37
|
-
|
|
38
|
-
## Heading
|
|
39
|
-
|
|
40
|
-
| Heading | Value |
|
|
41
|
-
| ------------- | ----------------------------------------------------- |
|
|
42
|
-
| `Target-Type` | `heading` |
|
|
43
|
-
| `Target` | The path to the heading you would like to append to. |
|
|
44
|
-
|
|
45
|
-
^bfec1f
|
|
46
|
-
|
|
47
|
-
| Position | Where |
|
|
48
|
-
| -------- | -------------------------------------------------------------- |
|
|
49
|
-
| Start | Beginning of line immediately following line named by heading |
|
|
50
|
-
| End | Last newline before heading of same or higher priority or EOF. |
|
|
51
|
-
| | |
|
|
52
|
-
Unlike with [[#Heading]], this targets the *content* of the heading and does not include the heading iself.
|
|
53
|
-
- ✅: ...replacing the content specified by a particular heading.
|
|
54
|
-
- ✅: ...appending content to a block specified by a particular heading.
|
|
55
|
-
## Block
|
|
56
|
-
| Position | Where |
|
|
57
|
-
| -------- | ------------------------------------------------------ |
|
|
58
|
-
| Start | Beginning of line for specified block. |
|
|
59
|
-
| End | Last character (including newline) of specified block. |
|
|
60
|
-
A "Block" in Obsidian can be any *block*-type element. This might mean a paragraph, but it could also mean a table, but how block references are marked differs in Obsidian depending upon what kind of block is being marked.
|
|
61
|
-
### Use Cases
|
|
62
|
-
- ✅: ...replacing the content specified by a particular block ID.
|
|
63
|
-
- I want to be able to replace a whole table or whole paragraph with new content.
|
|
64
|
-
- ✅: ...appending content to a block specified by a particular block ID.
|
|
65
|
-
- I want to be able to add new rows to an existing table.
|
|
66
|
-
- I want to be able to add new content to the end of a line.
|
|
67
|
-
|
|
68
|
-
## Frontmatter Field
|
|
69
|
-
|
|
70
|
-
| Position | Where |
|
|
71
|
-
| -------- | ------------------------------------------------------------------------ |
|
|
72
|
-
| Start | First character of content referenced by a particular frontmatter field. |
|
|
73
|
-
| End | Last character of content referenced by a particular frontmatter field. |
|
|
74
|
-
### Use Cases
|
|
75
|
-
- ✅: ...appending content to an existing frontmatter field.
|
|
76
|
-
- ✅: ...replacing the content of an existing frontmatter field. ^259a73
|
|
77
|
-
- ✅: ...adding a new frontmatter field. ^e6068e
|
|
78
|
-
## Document Properties (Exploratory)
|
|
79
|
-
|
|
80
|
-
[^block-ambiguity]: There is currently no obvious place to plop a block were we to create a new one. So, I might implement this such that these actions *work*, but will just add the block to the end of a file. This isn't great, but it's at least consistent?
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
aliases:
|
|
3
|
-
- Structured Markdown Patch
|
|
4
|
-
project-type: Technical
|
|
5
|
-
repository: https://github.com/coddingtonbear/markdown-patch
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# Overview
|
|
9
|
-
I came up with this project for supporting [[Obsidian Local Rest API]] (and via that [[Obsidian Web]]) because I often need to shove data into Markdown documents for posterity, but want to be able to do that programmatically with more care than just shoving content to the end of a file.
|
|
10
|
-
# Problems
|
|
11
|
-
- ~~It would be nice for the mechanism to be able to handle something like `upsert` for frontmatter fields. See [[#^e6068e]] in addition to what we already support [[#^259a73]].~~
|
|
12
|
-
- This was solved by making every content block directly-addressable. All interactions treat the document as a key-value mapping.
|
|
13
|
-
- ~~You can't use our earlier header delimiter `::` in an HTTP header; how did I not notice that? I've had a [conversation with ChatGPT](https://chatgpt.com/share/117b262a-f534-40e6-bc05-287758706f34) to land on a choice of `@#@` instead, but there aren't obvious good options. See [[#^1d6271]]~~
|
|
14
|
-
- I changed my mind in the end and went with the more-likely-to-collide-but-at-least-not-bizarre `///`.
|
|
15
|
-
- Should we allow partial matches? The pros are that it would make the usual, garden path of just wanting to push content into a section very easy. The cons are that it makes it kind of unclear what's going to happen when you do an `upsert` or `insert` for a particular value. (See [[#^bfec1f]] for more).
|
|
16
|
-
|
|
17
|
-
# Actions
|
|
18
|
-
| Name | Description | Heading? | Frontmatter? | Block |
|
|
19
|
-
| --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ------------ | ------------------- |
|
|
20
|
-
| `update` | Find the referenced `target` and replace the content at that region. | ✅ | ✅ | ✅ |
|
|
21
|
-
| `append` | Find the referenced `target` and add content to the end of its region. | ✅ | ✅ | ✅ |
|
|
22
|
-
| `prepend` | Find the referenced `target` and add content to the beginning of its region. | ✅ | ✅ | ✅ |
|
|
23
|
-
| `insert` | Find the path leading to the referenced `target` and add the specified content under a the specified name. This will create a new header or frontmatter field if necessary. | ✅ | ✅ | ❌[^block-ambiguity] |
|
|
24
|
-
| `upsert` | Find the path leading to the referenced `target` and either replace the content under the specified name, or add new content with a new header or frontmatter field if necessary. | ✅ | ✅ | ❌[^block-ambiguity] |
|
|
25
|
-
|
|
26
|
-
^2c67a6
|
|
27
|
-
|
|
28
|
-
# Headers
|
|
29
|
-
| Header | Explanation |
|
|
30
|
-
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
31
|
-
| `Target-Type` | `heading`, `block`, `frontmatter` |
|
|
32
|
-
| `Target` | Name of the target:<br>- `heading`: The `///`-delimited path to the heading to replace the content of. E.g. `Page Targets///Block///Use Cases`. This value should be URL-encoded. |
|
|
33
|
-
| `Target-Delimiter` | By default, we use `///` to delimit a `Target`, but it's remotely possible that this value might be present in a header. If it is, you can specify a different delimiter to use for `Target`. |
|
|
34
|
-
|
|
35
|
-
^1d6271
|
|
36
|
-
|
|
37
|
-
# Page Targets
|
|
38
|
-
|
|
39
|
-
## Heading
|
|
40
|
-
|
|
41
|
-
| Heading | Value |
|
|
42
|
-
| ------------- | ----------------------------------------------------- |
|
|
43
|
-
| `Target-Type` | `heading` |
|
|
44
|
-
| `Target` | The path to the heading you would like to append to. |
|
|
45
|
-
|
|
46
|
-
^bfec1f
|
|
47
|
-
|
|
48
|
-
| Position | Where |
|
|
49
|
-
| -------- | -------------------------------------------------------------- |
|
|
50
|
-
| Start | Beginning of line immediately following line named by heading |
|
|
51
|
-
| End | Last newline before heading of same or higher priority or EOF. |
|
|
52
|
-
| | |
|
|
53
|
-
Unlike with [[#Heading]], this targets the *content* of the heading and does not include the heading iself.
|
|
54
|
-
- ✅: ...replacing the content specified by a particular heading.
|
|
55
|
-
- ✅: ...appending content to a block specified by a particular heading.
|
|
56
|
-
## Block
|
|
57
|
-
| Position | Where |
|
|
58
|
-
| -------- | ------------------------------------------------------ |
|
|
59
|
-
| Start | Beginning of line for specified block. |
|
|
60
|
-
| End | Last character (including newline) of specified block. |
|
|
61
|
-
A "Block" in Obsidian can be any *block*-type element. This might mean a paragraph, but it could also mean a table, but how block references are marked differs in Obsidian depending upon what kind of block is being marked.
|
|
62
|
-
### Use Cases
|
|
63
|
-
- ✅: ...replacing the content specified by a particular block ID.
|
|
64
|
-
- I want to be able to replace a whole table or whole paragraph with new content.
|
|
65
|
-
- ✅: ...appending content to a block specified by a particular block ID.
|
|
66
|
-
- I want to be able to add new rows to an existing table.
|
|
67
|
-
- I want to be able to add new content to the end of a line.
|
|
68
|
-
|
|
69
|
-
## Frontmatter Field
|
|
70
|
-
|
|
71
|
-
| Position | Where |
|
|
72
|
-
| -------- | ------------------------------------------------------------------------ |
|
|
73
|
-
| Start | First character of content referenced by a particular frontmatter field. |
|
|
74
|
-
| End | Last character of content referenced by a particular frontmatter field. |
|
|
75
|
-
### Use Cases
|
|
76
|
-
- ✅: ...appending content to an existing frontmatter field.
|
|
77
|
-
- ✅: ...replacing the content of an existing frontmatter field. ^259a73
|
|
78
|
-
- ✅: ...adding a new frontmatter field. ^e6068e
|
|
79
|
-
## Document Properties (Exploratory)
|
|
80
|
-
Beep Boop[^block-ambiguity]: There is currently no obvious place to plop a block were we to create a new one. So, I might implement this such that these actions *work*, but will just add the block to the end of a file. This isn't great, but it's at least consistent?
|
package/src/types.ts
DELETED
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
export interface DocumentRange {
|
|
2
|
-
start: number;
|
|
3
|
-
end: number;
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
export interface DocumentMapMarkerContentPair {
|
|
7
|
-
marker: DocumentRange;
|
|
8
|
-
content: DocumentRange;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export interface HeadingMarkerContentPair extends DocumentMapMarkerContentPair {
|
|
12
|
-
level: number;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export interface DocumentMap {
|
|
16
|
-
heading: Record<string, HeadingMarkerContentPair>;
|
|
17
|
-
block: Record<string, DocumentMapMarkerContentPair>;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export type PatchTargetType = "heading" | "block";
|
|
21
|
-
|
|
22
|
-
export type PatchOperation = "replace" | "prepend" | "append";
|
|
23
|
-
|
|
24
|
-
export interface BasePatchInstructionTarget {
|
|
25
|
-
targetType: PatchTargetType;
|
|
26
|
-
target: any;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface BasePatchInstructionOperation {
|
|
30
|
-
operation: string;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export interface BaseHeadingPatchInstruction
|
|
34
|
-
extends BasePatchInstructionTarget {
|
|
35
|
-
targetType: "heading";
|
|
36
|
-
target: string[] | null;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface BaseBlockPatchInstruction extends BasePatchInstructionTarget {
|
|
40
|
-
targetType: "block";
|
|
41
|
-
target: string;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export interface NonExtendingPatchInstruction
|
|
45
|
-
extends BasePatchInstructionOperation {}
|
|
46
|
-
|
|
47
|
-
export interface ExtendingPatchInstruction
|
|
48
|
-
extends BasePatchInstructionOperation {
|
|
49
|
-
/** Trim whitepsace from target before joining with content
|
|
50
|
-
*
|
|
51
|
-
* - For `prepend`: Trims content from the beginning of
|
|
52
|
-
* the target content.
|
|
53
|
-
* - For `append`: Trims content from the end of the target
|
|
54
|
-
* content. Your content should probably end in a newline
|
|
55
|
-
* in this case, or the trailing heading will no longer
|
|
56
|
-
* be the start of its own line
|
|
57
|
-
*/
|
|
58
|
-
trimTargetWhitespace?: boolean;
|
|
59
|
-
/** Apply patch even if content already exists at target
|
|
60
|
-
*
|
|
61
|
-
* By default, we will fail to apply a patch if the supplied
|
|
62
|
-
* content is found anywhere in your target content. If you
|
|
63
|
-
* would instead like the patch to occur, regardless of whether
|
|
64
|
-
* it appears the content is already there, you can set
|
|
65
|
-
* `applyIfContentPreexists` to `true`.
|
|
66
|
-
*/
|
|
67
|
-
applyIfContentPreexists?: boolean;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export interface StringContent {
|
|
71
|
-
content: string;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export interface TableRowsContent {
|
|
75
|
-
targetBlockTypeBehavior: "table";
|
|
76
|
-
content: string[][];
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export interface PrependHeadingPatchInstruction
|
|
80
|
-
extends ExtendingPatchInstruction,
|
|
81
|
-
BaseHeadingPatchInstruction,
|
|
82
|
-
StringContent {
|
|
83
|
-
operation: "prepend";
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export interface AppendHeadingPatchInstruction
|
|
87
|
-
extends ExtendingPatchInstruction,
|
|
88
|
-
BaseHeadingPatchInstruction,
|
|
89
|
-
StringContent {
|
|
90
|
-
operation: "append";
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
export interface ReplaceHeadingPatchInstruction
|
|
94
|
-
extends NonExtendingPatchInstruction,
|
|
95
|
-
BaseHeadingPatchInstruction,
|
|
96
|
-
StringContent {
|
|
97
|
-
operation: "replace";
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
export interface PrependBlockPatchInstruction
|
|
101
|
-
extends ExtendingPatchInstruction,
|
|
102
|
-
BaseBlockPatchInstruction,
|
|
103
|
-
StringContent {
|
|
104
|
-
operation: "prepend";
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
export interface AppendBlockPatchInstruction
|
|
108
|
-
extends ExtendingPatchInstruction,
|
|
109
|
-
BaseBlockPatchInstruction,
|
|
110
|
-
StringContent {
|
|
111
|
-
operation: "append";
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
export interface ReplaceBlockPatchInstruction
|
|
115
|
-
extends NonExtendingPatchInstruction,
|
|
116
|
-
BaseBlockPatchInstruction,
|
|
117
|
-
StringContent {
|
|
118
|
-
operation: "replace";
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
export interface PrependTableRowsBlockPatchInstruction
|
|
122
|
-
extends ExtendingPatchInstruction,
|
|
123
|
-
BaseBlockPatchInstruction,
|
|
124
|
-
TableRowsContent {
|
|
125
|
-
operation: "prepend";
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
export interface AppendTableRowsBlockPatchInstruction
|
|
129
|
-
extends ExtendingPatchInstruction,
|
|
130
|
-
BaseBlockPatchInstruction,
|
|
131
|
-
TableRowsContent {
|
|
132
|
-
operation: "append";
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
export interface ReplaceTableRowsBlockPatchInstruction
|
|
136
|
-
extends NonExtendingPatchInstruction,
|
|
137
|
-
BaseBlockPatchInstruction,
|
|
138
|
-
TableRowsContent {
|
|
139
|
-
operation: "replace";
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
export type HeadingPatchInstruction =
|
|
143
|
-
| PrependHeadingPatchInstruction
|
|
144
|
-
| AppendHeadingPatchInstruction
|
|
145
|
-
| ReplaceHeadingPatchInstruction;
|
|
146
|
-
|
|
147
|
-
export type BlockPatchInstruction =
|
|
148
|
-
| PrependBlockPatchInstruction
|
|
149
|
-
| AppendBlockPatchInstruction
|
|
150
|
-
| ReplaceBlockPatchInstruction
|
|
151
|
-
| PrependTableRowsBlockPatchInstruction
|
|
152
|
-
| AppendTableRowsBlockPatchInstruction
|
|
153
|
-
| ReplaceTableRowsBlockPatchInstruction;
|
|
154
|
-
|
|
155
|
-
export type PatchInstruction = HeadingPatchInstruction | BlockPatchInstruction;
|
package/tsconfig.json
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "@tsconfig/node16/tsconfig.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"module": "ESNext", /* Specify what module code is generated. */
|
|
5
|
-
"target": "ES2020", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
|
|
6
|
-
"moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
|
|
7
|
-
"outDir": "./dist", /* Specify an output folder for all emitted files. */
|
|
8
|
-
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
|
|
9
|
-
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
|
|
10
|
-
"strict": true, /* Enable all strict type-checking options. */
|
|
11
|
-
"skipLibCheck": true, /* Skip type checking all .d.ts files. */
|
|
12
|
-
"resolveJsonModule": true
|
|
13
|
-
},
|
|
14
|
-
"include": ["src/**/*"],
|
|
15
|
-
"ts-node": {
|
|
16
|
-
"transpileOnly": true
|
|
17
|
-
}
|
|
18
|
-
}
|