Files
pnpm/yaml/document-sync/README.md

170 lines
3.3 KiB
Markdown

# @pnpm/yaml.document-sync
> Update a YAML document to match the contents of an in-memory object.
<!--@shields('npm')-->
[![npm version](https://img.shields.io/npm/v/@pnpm/yaml.document-sync.svg)](https://www.npmjs.com/package/@pnpm/yaml.document-sync)
<!--/@-->
## Installation
```sh
pnpm add @pnpm/yaml.document-sync
```
## Usage
Given a "_source_" document such as:
```yaml
foo:
bar:
# 25 is better than 24
baz: 25
qux:
# He was number 1
- 1
```
And a "_target_" object in-memory:
```ts
import fs from 'node:fs'
import { patchDocument } from '@pnpm/yaml.document-sync'
import yaml from 'yaml'
const source = await fs.promises.readFile('source.yaml', 'utf8')
const document = yaml.parseDocument(source)
const target = {
foo: { bar: { baz: 25 } },
qux: [1, 2, 3]
}
patchDocument(document, target)
```
The `patchDocument` function will mutate `document` to match the `target`'s contents, retaining comments along the way. In the example above, the final rendered document will be:
```yaml
foo:
bar:
# 25 is better than 24
baz: 25
qux:
# He was number 1
- 1
- 2
- 3
```
## Purpose
This package is useful when your codebase:
1. Uses the [yaml](https://www.npmjs.com/package/yaml) library.
2. Calls `.toJSON()` on the parse result and performs changes to it.
3. Needs to "sync" those changes back to the source document.
Instead of this package, consider performing mutations directly on the `yaml.Document` returned from `yaml.parseDocument()` instead. Directly modifying will be faster and more accurate. This package exists as a workaround for codebases that make changes to a JSON object instead and need to reconcile changes back into the source `yaml.Document`.
## Caveats
There are several cases where comment preservation is inherently ambiguous. If the caveats outlined below are problematic, consider modifying the source `yaml.Document` before running the patch function in this package.
### Key Renames
For example, renames of a key are ambiguous. Given:
```yaml
- foo:
# Test
bar: 1
```
And a target object to match:
```json
{
"baz": {
"bar": 1
}
}
```
The comment on `bar` won't be retained.
### List Reconciliation
For simple lists (e.g. lists with only primitives), items will be uniquely matched using their contents. However, updates to complex lists are inherently ambiguous.
For example, given a source list with objects as elements:
```yaml
- foo: 1
# Comment
- bar: 2
```
And a target:
```json
[
{ "foo": 1 },
{ "baz": 3 },
{ "bar": 2 }
]
```
The result will erase the comment:
```yaml
- foo: 1
- baz: 3
- bar: 2
```
It's not trivial to detect that the object with `bar` as a field moved down. Detecting this case would require a diffing algorithm, which would be best effort anyway.
Virtual DOM libraries such as React have the same problem. In React, list elements need to specify a `key` prop to uniquely identify each item. This library may take a similar approach in the future if needed. This is not a problem for primitive lists since their values can be compared using simple equality checks.
### Aliases
Given:
```yaml
foo: &config
- 1
- 2
bar: *config
```
And a target object:
```json
{
"foo": [1, 2],
"bar": [1, 2, 3]
}
```
For correctness, the YAML alias needs to be removed.
```yaml
foo: &config
- 1
- 2
bar:
- 1
- 2
- 3
```
## License
MIT