char/avril

(wip) why'd you have to go and make things so complicaaaaated?

git clone https://git.t4t.associates/char/avril

Charlotte Somdeslop: human-rewrite fjson module docsf1d07d8

main
2.6 KiB57 linesraw

avril:fjson

fJSON ("friendly JSON") is a JSON superset object notation used for the avril configuration and lockfile formats. unlike JSON, it's well-suited to being a human-authored configuration file format

it is essentially JSON, except:

  • the root of a document may be an "implicit map" (i.e. imaginary { <source> })
  • simple ASCII identifier keys may be unquoted in maps
  • trailing commas are permitted in maps and arrays
  • commas can be replaced by newlines in maps and arrays
  • strings may be single-quoted
  • documents may contain // comments

an fJSON document can look like this:

name: "@me/my-library"
version: "0.1.0"
description: 'Implements the "myLibrary" protocol.'
deps: [
  // we use the REST client for interacting with myLibrary servers
  "./lib/rest-client/"
  "./lib/schema-generation/"
]

the avril:fjson built-in library implements the following operations:

  • parse(source: string): unknown: parse an fJSON document into a JavaScript value
    • an empty or comment-only document will become {}
  • stringify(value: unknown): string: convert a JavaScript value into a pretty-printed fJSON document
    • two-space indentation
    • will wrap; targets an 80-column width
    • no final newline
  • apply(source: string, ...overrides): string: update an fJSON map document's properties in-place, preserving comments and ordering. see below
    • replaced strings retain their choice of quote mark
    • invalid overrides throw TypeError
    • non-own+enumerable props are ignored in overrides
    • best-effort: property deletion may leave residual whitespace/blank lines/floating comments

fJSON.apply(…)

apply allows you to programmatically modify an fJSON document without unintentionally losing any information that can't be represented in a parse -> stringify round-trip: for instance, comments don't make it into the data returned by parse, since they're only lexically- and not semantically-present in the document. apply takes a source document and any number of object updates, which will be applied left-to-right.

object updates are plain objects (à la Object.assign), but can contain undefined to remove properties from the target. additionally, object updates will also drill down into nested objects that are present instead of replacing them. it follows that you can replace a nested map by first setting it to undefined and then setting it to the new map:

import * as fJSON from "avril:fjson";

fJSON.apply("x: 0, y: { a: 1, b: 2 }", { y: { c: 3 } })
// => "x: 0, y: { a: 1, b: 2, c: 3 }"

fJSON.apply(
  "x: 0, y: { a: 1, b: 2 }",
  { y: undefined },
  { y: { c: 3 } },
)
// => "x: 0, y: { c: 3 }"
1# `avril:fjson`
2
3fJSON ("friendly JSON") is a JSON superset object notation used for the avril configuration and lockfile formats. unlike JSON, it's well-suited to being a human-authored configuration file format
4
5it is essentially JSON, except:
6- the root of a document may be an "implicit map" (i.e. imaginary `{ <source> }`)
7- simple ASCII identifier keys may be unquoted in maps
8- trailing commas are permitted in maps and arrays
9- commas can be replaced by newlines in maps and arrays
10- strings may be single-quoted
11- documents may contain `// comment`s
12
13an fJSON document can look like this:
14
15```
16name: "@me/my-library"
17version: "0.1.0"
18description: 'Implements the "myLibrary" protocol.'
19deps: [
20  // we use the REST client for interacting with myLibrary servers
21  "./lib/rest-client/"
22  "./lib/schema-generation/"
23]
24```
25
26the `avril:fjson` built-in library implements the following operations:
27- `parse(source: string): unknown`: parse an fJSON document into a JavaScript value
28  - an empty or comment-only document will become `{}`
29- `stringify(value: unknown): string`: convert a JavaScript value into a pretty-printed fJSON document
30  - two-space indentation
31  - will wrap; targets an 80-column width
32  - no final newline
33- `apply(source: string, ...overrides): string`: update an fJSON map document's properties in-place, preserving comments and ordering. see below
34  - replaced strings retain their choice of quote mark
35  - invalid overrides throw `TypeError`
36  - non-own+enumerable props are ignored in overrides
37  - best-effort: property deletion may leave residual whitespace/blank lines/floating comments
38
39## fJSON.apply(…)
40
41`apply` allows you to programmatically modify an fJSON document without unintentionally losing any information that can't be represented in a `parse` -> `stringify` round-trip: for instance, comments don't make it into the data returned by `parse`, since they're only lexically- and not semantically-present in the document. `apply` takes a source document and any number of object updates, which will be applied left-to-right.
42
43object updates are plain objects (à la Object.assign), but can contain `undefined` to remove properties from the target. additionally, object updates will also drill down into nested objects that are present instead of replacing them. it follows that you can replace a nested map by first setting it to `undefined` and then setting it to the new map:
44
45```typescript
46import * as fJSON from "avril:fjson";
47
48fJSON.apply("x: 0, y: { a: 1, b: 2 }", { y: { c: 3 } })
49// => "x: 0, y: { a: 1, b: 2, c: 3 }"
50
51fJSON.apply(
52  "x: 0, y: { a: 1, b: 2 }",
53  { y: undefined },
54  { y: { c: 3 } },
55)
56// => "x: 0, y: { c: 3 }"
57```