Files
Zoltan KochanandClaude Opus 5.5 6f95957063 docs: lint prose with Vale (#17238)
* docs: lint prose with Vale

Add Vale with an in-repo style, .vale/styles/pnpm, that turns the
writing rules in AGENTS.md and the Google developer documentation style
guide into lint rules. The stock Vale packages (Google, Microsoft,
RedHat, write-good) were too noisy to block on, for example 1,300
passive-voice hits in the v12 docs alone, so the rules are written for
this repo, following the GitLab model. Each one links the guideline it
enforces.

Levels:
- error, fails CI: dashes, plain-word substitutions, condescending
  words, "please", "note that", hedges, puffery, optional plurals,
  exclamation marks, ellipses, curly quotes, generic link text, product
  spellings, sentence-case headings, repeated words, double spaces,
  implementation history in comments; in changesets, bold or italic
  emphasis, internal names and release-line names.
- warning: future tense, sentences over 30 words, "instead of" and
  "so that" tails in changesets.
- suggestion: passive voice.

.vale.ini scopes the rules. Docs get every rule. Contributor Markdown
and code comments keep dashes and headings as warnings until their
roughly 8,100 em dashes are rewritten. Released changesets stay in
.changeset/ and are not rewritten, so lint-prose.mjs lints only the
changesets added since PROSE_BASE (the PR base in CI).

The prose workflow installs a pinned, checksum-verified Vale release and
runs `pnpm lint:prose`. The rest of this change rewrites the existing
docs, READMEs, contributor Markdown, and code comments to pass the
error-level rules. Only prose and comment text changed. Renamed
headings keep their anchors.

Spelling stays with cspell, which checks identifiers that Vale does not
see.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: keep prose lint from flattening meaning

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 21:48:09 +02:00
..
2026-10-22 21:48:09 +02:00
2026-09-20 23:11:14 +02:00

@pnpm/yaml.document-sync

Update a YAML document to match the contents of an in-memory object.

npm version

Installation

pnpm add @pnpm/yaml.document-sync

Usage

Given a "source" document such as:

foo:
  bar:
    # 25 is better than 24
    baz: 25

qux:
  # He was number 1
  - 1

And a "target" object in-memory:

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:

foo:
  bar:
    # 25 is better than 24
    baz: 25

qux:
  # He was number 1
  - 1
  - 2
  - 3

For package manifests, retain null values and empty maps and keep existing keys in their original order:

patchDocument(document, target, {
  preserveKeyOrder: true,
  preserveScalarAliases: true,
  pruneEmptyValues: false,
})

New keys are appended to their mapping. By default, keys follow the target object's order, and null values and empty maps are pruned from existing nodes.

Consumers that use a different conversion for scalar mapping keys can pass stringifyKey. For example, stringifyKey: String matches js-yaml, which exposes a null mapping key as the property name "null". The default follows yaml and uses an empty string.

Purpose

This package is useful when your codebase:

  1. Uses the 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:

- foo:
    # Test
    bar: 1

And a target object to match:

{
  "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:

- foo: 1
# Comment
- bar: 2

And a target:

[
  { "foo": 1 },
  { "baz": 3 },
  { "bar": 2 }
]

The result will erase the comment:

- 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:

foo: &config
  - 1
  - 2

bar: *config

And a target object:

{
  "foo": [1, 2],
  "bar": [1, 2, 3]
}

For correctness, the YAML alias needs to be removed.

foo: &config
  - 1
  - 2

bar:
  - 1
  - 2
  - 3

Set preserveScalarAliases: true to retain scalar aliases whose final values agree. If an anchor's defining entry is removed, the first surviving entry defines it. An entry whose value differs from the defining entry is written as a scalar. This option does not coordinate dependency updates; it preserves YAML representation while applying the supplied target values.

patchDocument(document, target, { preserveScalarAliases: true })

License

MIT